Skip to content

fix: correct MTHINC normals on non-uniform grids#1401

Draft
sbryngelson wants to merge 40 commits intoMFlowCode:masterfrom
sbryngelson:MTHINC
Draft

fix: correct MTHINC normals on non-uniform grids#1401
sbryngelson wants to merge 40 commits intoMFlowCode:masterfrom
sbryngelson:MTHINC

Conversation

@sbryngelson
Copy link
Copy Markdown
Member

@sbryngelson sbryngelson commented May 5, 2026

Summary

Builds on #1303 (MTHINC implementation). Fixes two bugs found during review:

Fix 1: MTHINC normals on non-uniform grids (#1395)

The reference-space normal in s_compute_mthinc_normals used a uniform-grid assumption (* 0.5) that gives the wrong direction when cell widths vary. The correct weighting is:

n_ξ ∝ (∂α/∂x) · Δx_j  =  [α(j+1) - α(j-1)] · (x_cb(j) - x_cb(j-1)) / (x_cc(j+1) - x_cc(j-1))

On uniform grids this ratio equals 0.5 exactly, so all existing tests pass unchanged.

Fix 2: int_comp with viscous / surface-tension flows (#1396)

int_comp > 0 requires full (non-split) reconstruction, but the viscous and surface-tension RHS paths were calling split reconstruction, silently disabling interface compression. Fixed by routing those paths through the full reconstruction branch and adding a runtime @:PROHIBIT guard.

New regression test (7A1719C6)

A new golden-file test exercises Fix 1 on a non-uniform grid:

  • 2D domain with cosh-based x-stretching, creating non-uniform cell widths
  • Circular fluid-2 bubble creates a curved interface with diagonal normals (both nr_x ≠ 0 and nr_y ≠ 0) — only diagonal cells are sensitive to the Δx_j weighting correction; axis-aligned interfaces always normalize to ±1 regardless of scaling
  • Uniform vel(1)=0.5 advects the bubble so MTHINC reconstruction produces non-trivial fluxes; 129+ distinct values at t=50 confirm genuine interface evolution

Test plan

  • All 4 existing uniform-grid MTHINC tests pass unchanged (5126B21F, 4E4FECA9, 4F3722DB, 4C4F339C)
  • New non-uniform grid MTHINC test passes (7A1719C6)
  • ./mfc.sh precheck -j 8 passes
  • ./mfc.sh build -t simulation -j 8 compiles cleanly

sbryngelson and others added 30 commits March 26, 2026 18:50
- case.md: add muscl_eps row/description alongside int_comp (MTHINC version)
- m_global_parameters.fpp: keep int_comp as integer (0/1/2), add muscl_eps; keep GPU_DECLARE; both GPU_UPDATEs
- m_mpi_proxy.fpp: broadcast both int_comp and collision_model as integers
- case_validator.py: keep recon_type validation from master; drop stale int_comp=boolean check (superseded by check_interface_compression)
…rface_tension

f_thinc_integral_1d used log_cosh(a+h) - log_cosh(a-h) which suffers
catastrophic cancellation in single precision when h (= beta*n_transverse/2)
is small (interface nearly axis-aligned). Replace with the identity
2*atanh(tanh(a)*tanh(h)), which is algebraically equivalent and numerically
stable at all precisions. Fixes the single-precision CI failure on the
2D and 3D MTHINC WENO tests (5126B21F, 4F3722DB).

Also add validator rules prohibiting int_comp > 0 with viscous=T or
surface_tension=T: the split reconstruction paths in m_rhs prevent
s_thinc_compression from ever running in those cases, so permitting the
combination would silently produce unsharpened results.
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 5, 2026

Claude Code Review

Head SHA: ae74a6a

Files changed:

  • 16
  • src/simulation/m_thinc.fpp (new)
  • src/simulation/m_global_parameters.fpp
  • src/simulation/m_muscl.fpp
  • src/simulation/m_weno.fpp
  • src/simulation/m_rhs.fpp
  • src/simulation/m_start_up.fpp
  • src/simulation/m_mpi_proxy.fpp
  • src/common/m_constants.fpp
  • toolchain/mfc/case_validator.py
  • toolchain/mfc/params/definitions.py

Findings

1. GPU correctness bug — Gauss-quadrature constants uninitialized on device (MTHINC only)

File: src/simulation/m_thinc.fpp

real(wp) :: gq3_pts(3) = [-5e-1_wp*0.7745966692414834_wp, 0._wp, 5e-1_wp*0.7745966692414834_wp]
real(wp) :: gq3_wts(3) = [5._wp/18._wp, 8._wp/18._wp, 5._wp/18._wp]
real(wp) :: ln2 = 0.6931471805599453_wp
$:GPU_DECLARE(create='[gq3_pts, gq3_wts, ln2]')

GPU_DECLARE(create=...) allocates device memory but does not copy the host values. The host initializers (set at declaration time) are never transferred to the device — s_initialize_thinc_module has no $:GPU_UPDATE(device='[gq3_pts, gq3_wts, ln2]') call.

gq3_pts and gq3_wts are accessed inside GPU_PARALLEL_LOOP kernels via the GPU_ROUTINE(parallelism='[seq]') functions f_mthinc_volume_integral, f_mthinc_volume_integral_dd, and f_mthinc_face_average. On any GPU build with int_comp=2, these routines read uninitialized device memory, producing garbage Gauss-quadrature results and silently incorrect MTHINC reconstruction.

Fix: add $:GPU_UPDATE(device='[gq3_pts, gq3_wts, ln2]') in s_initialize_thinc_module, or change the declare to GPU_DECLARE(copyin='[gq3_pts, gq3_wts, ln2]'). This does not affect int_comp=1 (THINC), which does not use these arrays.


2. Dead device function — f_log_cosh compiled for GPU but never called

File: src/simulation/m_thinc.fpp

function f_log_cosh(x) result(res)
    $:GPU_ROUTINE(parallelism='[seq]')
    ...
end function f_log_cosh

f_log_cosh is not called anywhere in the module. f_thinc_integral_1d calls f_log_cosh_diff directly (via atanh(tanh(a)*tanh(h))), bypassing f_log_cosh entirely. The function and its sole dependency ln2 are dead code that will nonetheless be compiled into device code on GPU builds. Both should be removed, along with ln2 from the GPU_DECLARE list.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants