Skip to content

HLLC: opt-in smoothing of the tangential interface velocity in the viscous work (ib_smooth_contact) - #2211

Merged
sbryngelson merged 3 commits into
MFlowCode:masterfrom
sbryngelson:feat/ib-smooth-contact
Nov 14, 2026
Merged

sbryngelson merged 3 commits into
MFlowCode:masterfrom
sbryngelson:feat/ib-smooth-contact

Conversation

@sbryngelson

Copy link
Copy Markdown
Member

Branch feat/ib-smooth-contact, one commit on origin/master 9649d50. Default F: no existing result moves. Adds four tests (viscous IB circle and sphere, one and two fluids).

Summary

The viscous work in the HLLC solver uses the tangential interface velocity xi_M u_L + xi_P u_R, with xi_M = 0.5 + sign(0.5, s_S). At a face between a fluid cell and an
immersed-boundary ghost cell the contact speed s_S can be round-off (equal pressures on both sides, a transverse velocity that is zero up to round-off), while u_L and u_R differ by O(U)
across the ghost mirror. The sign of s_S then decides whether the viscous work sees the fluid or the ghost velocity, and it is decided by round-off, so the answer depends on the compiler,
the device and the rank decomposition.

ib_smooth_contact = T replaces the step by a linear ramp of half-width contact_ramp * c (contact_ramp = 0.01): the weight on the left state is
min(max(1/2 + s_S / (2 * 0.01 c), 0), 1). Faces with |s_S| > 0.01 c get exactly the old value, so the change is confined to faces next to a ghost cell whose contact speed is
negligible. It applies to the model_eqns = 2 HLLC path; case_validator.py requires ib, viscous and
riemann_solver = 2. It does not require weno_Re_flux: the viscous work reads the same interface velocity either way.

Code: f_contact_weight in m_riemann_state.fpp, the blend after the existing vel_src loop in m_riemann_solver_hllc.fpp, the constant in m_constants.fpp, the parameter
plumbing (definitions.py, descriptions.py, fortran_gen.py for the device copy, the device update next to the other IB flags in m_ibm.fpp), docs and four tests.

Evidence: cross-platform and cross-decomposition agreement

3D viscous sphere in a uniform stream: Mach 0.3, Re = U D / nu = 100, 20 cells/D, box [-2.5, 5] x [-2.5, 2.5]^2 (150 x 100 x 100 cells), WENO5 + HLLC + RK3, weno_Re_flux = T,
ghost-extrapolation boundaries, fixed dt = 0.25 dx / (c + U), two steps, double precision. The sphere is centred on a cell corner, so the transverse velocity is round-off exactly where
the problem sits. Saved conservative variables compared; "cells" counts values that differ by more than 1e-10 of the variable's maximum (1.5e6 values each).

A100 (nvfortran 25.11, OpenACC) against MI300A (amdflang AFAR, OpenMP offload), 2 ranks each, master and the branch:

variable master: max rel. difference cells branch, ib_smooth_contact = T: max rel. difference cells
density 9.3e-5 10096 8.5e-16 0
x momentum 2.3e-4 7703 1.9e-15 0
y momentum 8.0e-3 11783 3.5e-14 0
z momentum 8.0e-3 11787 2.8e-14 0
energy 3.8e-4 10450 1.3e-15 0

One rank against two and four (A100, a rank boundary cuts the sphere, ib_wall_order = 2), sorted values compared because the cell order differs:

variable master: 1 vs 2 ranks master: 1 vs 4 ranks branch, ib_smooth_contact = T: 1 vs 2 1 vs 4
density 5.0e-5 7.9e-5 7.2e-16 1.2e-15
x momentum 5.9e-5 1.5e-4 6.0e-15 6.9e-15
y momentum 2.1e-3 3.2e-3 1.6e-14 1.8e-14
z momentum 3.3e-3 3.7e-3 1.7e-14 1.8e-14
energy 1.4e-4 1.5e-4 7.8e-16 1.6e-15

(The 1e-14 level is summation order.) So without the option the two-step answer differs by up to 8e-3 of the maximum between two machines and up to 3.7e-3 between rank counts on one
machine; with it the answers agree to round-off. On the same machine the option changes the master answer by up to 1.2e-2 of the maximum (z momentum, 1.2e4 cells) after two steps:
the old answer at those faces was a coin toss, not a better or worse approximation.

Does it change accuracy? Not on master's default path: with ib_smooth_contact = T the annulus (L2, torques), the channel (L2 and flow rate, 8 to 64 cells across) and the cylinder (Cd, wake length) change by
at most 0.5 % of the error (cylinder: identical to the digits printed). It does reduce the largest density excursion in the flat channel, from 3.0e-2 to 1.2e-2 of the mean at 32 cells across (wall on a face).
Together with the companion ib_visc_compact PR the annulus and cylinder are again unchanged, but the channel at 32 and 64 cells across improves by 13 % and 19 % in L2 (5.0e-2 to 4.3e-2 and 2.2e-2 to 1.8e-2),
because it removes part of the near-wall density excursion described in that PR.

Tests

Default F leaves every existing golden unchanged: on a local tree with this commit plus the two companion commits the full IBM label, the full Viscous label and the eleven viscous-IB examples
pass against master's goldens on both machines. New tests (viscous IB circle 2D one and two fluids, sphere 3D one and two fluids, ib_smooth_contact = T, goldens generated on the A100, checked on the MI300A):

Machine, compiler Tree IBM label Viscous label New tests alone
A100, nvfortran 25.11, --gpu acc, 2 GPUs this branch n/a n/a 4 passed
MI300A, amdflang (AFAR), --gpu mp combined tree 97 passed 83 passed included
A100, nvfortran 25.11, --gpu acc combined tree 85 passed, 12 new without golden, then 12 passed 71 passed, 12 new, then 12 passed included

(The label runs are on the tree with all three changes and all twelve new tests; the new tests of this PR are four of the twelve. Two --only labels intersect, so each label was run on its own.)
Examples with a viscous IB and weno_Re_flux: 2D ibm, mibm_cylinder_in_cross_flow, mibm_particle_cloud, mibm_shock_cylinder, tumbling_rectangle, 3D ibm_flapping_plate, rotating_sphere, and the
chemistry ones ibm_burning_grain, ibm_flameholder, ibm_lumped_wall, ibm_reacting_surface: all 11 pass on the MI300A and on the A100 (7 and 4 in two runs). ./mfc.sh format and ./mfc.sh precheck pass on the commit.

Limitations

  • A modelling choice, not a derivation: the width 0.01 c is the value I used and tested (not tuned); I did not scan it, and the ramp averages the tangential velocity of the two sides, which is
    what the step already did on average over the ill-defined faces.
  • Applies to the HLLC model_eqns = 2 path only. The 6-equation path (model_eqns = 3) and the hypoelastic star-state branch were not exercised with the option on (the validator allows them).
  • The mechanism (round-off sign of s_S at fluid/ghost faces) is inferred, not measured: I did not log s_S. What I measured is that the option removes the cross-machine and cross-decomposition
    differences, which a sign flip of the viscous-work weight explains.
  • The cross-platform and decomposition tests above are two steps of one case; I did not show that the option helps a long run, and the accuracy runs show no change either way.
  • Only two machines and compilers: nvfortran 25.11 (OpenACC) and amdflang AFAR (OpenMP offload). Not run: gfortran, Intel, Cray CCE (in CI), a CPU build, Frontier, multi-node.
  • No timing measured (one extra weight evaluation per face when the option is on).

This PR was made with Claude Code (AI).

…scous work (ib_smooth_contact)

The viscous work of the HLLC solver uses the tangential interface velocity xi_M u_L + xi_P u_R, with xi_M = 0.5 + sign(0.5, s_S). At a face between a fluid cell and an immersed-boundary ghost cell the contact speed s_S is round-off while u_L and u_R differ by O(U), so the sign, and with it the viscous work, depended on the build, the device and the rank decomposition.

ib_smooth_contact = T blends the two states linearly over |s_S| < 0.01 c instead. Faces away from s_S = 0 are unchanged, and the default F changes no result.
@github-actions

github-actions Bot commented Nov 12, 2026 •

Copy link
Copy Markdown

Claude Code Review

Head SHA: 05987e2

Files changed:

  • 11
  • docs/documentation/case.md
  • src/common/m_constants.fpp
  • src/simulation/m_global_parameters.fpp
  • src/simulation/m_ibm.fpp
  • src/simulation/m_riemann_solver_hllc.fpp
  • src/simulation/m_riemann_state.fpp
  • toolchain/mfc/case_validator.py
  • toolchain/mfc/params/definitions.py
  • toolchain/mfc/params/descriptions.py
  • toolchain/mfc/params/generators/fortran_gen.py

Findings:
No issues found. Checked in step 4: m_constants.fpp, m_global_parameters.fpp, fortran_gen.py, descriptions.py. I could not open the m_ibm.fpp, m_riemann_solver_hllc.fpp, m_riemann_state.fpp, case_validator.py, definitions.py or cases.py hunks beyond the diff, because they have no context files. Those hunks were checked against the diff only.

@sbryngelson
sbryngelson marked this pull request as ready for review November 12, 2026 20:32
@sbryngelson
sbryngelson requested a review from a team as a code owner November 12, 2026 20:32
@sbryngelson
sbryngelson requested review from danieljvickers and removed request for a team November 12, 2026 20:32
@codecov

codecov Bot commented Nov 12, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 50.00000% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 65.15%. Comparing base (9649d50) to head (1fb6566).
⚠️ Report is 8 commits behind head on master.

Files with missing lines Patch % Lines
src/simulation/m_riemann_solver_hllc.fpp 0.00% 3 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #2211      +/-   ##
==========================================
+ Coverage   63.71%   65.15%   +1.44%     
==========================================
  Files          90      110      +20     
  Lines       24569    29565    +4996     
  Branches     3584     4477     +893     
==========================================
+ Hits        15653    19262    +3609     
- Misses       6135     6746     +611     
- Partials     2781     3557     +776     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@github-actions

Copy link
Copy Markdown

Lines of Code

File Lines Diff
src/simulation/m_riemann_solver_hllc.fpp 1382 +9
src/simulation/m_riemann_state.fpp 1192 +6
src/common/m_constants.fpp 104 +1
src/simulation/m_global_parameters.fpp 947 +1
Directory Lines Diff
common 11047 +1
simulation 39371 +16
total 59337 +17

@sbryngelson
sbryngelson merged commit 6c9b1e2 into MFlowCode:master Nov 14, 2026
51 of 54 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant