Anchored Dual-pass HLLD for Hypoelasticity (+ HLLC and interface-consistent HLL)#1414
Anchored Dual-pass HLLD for Hypoelasticity (+ HLLC and interface-consistent HLL)#1414ChrisZYJ wants to merge 90 commits into
Conversation
|
@sbryngelson Hi Spencer, I’ve completed the merge and the PR is ready for review. Would you mind reopening it when you have a chance? Thanks! |
Claude Code ReviewHead SHA: d55a3f3 Files changed:
Findings1. CORRECTNESS — Index transposition in HLLC axisymmetric geometric source (y-sweep)File: flux_gsrc_rsx_vf(${SF('')}$, eqn_idx%cont%end + dir_idx(1)) = flux_rsx_vf(j, k, l, &
& eqn_idx%cont%end + dir_idx(1)) - p_face + tau_qq_faceThe LHS correctly uses For comparison, the analogous assignment in the HLLD geometric source path (diff line ~4621) correctly reads 2. CORRECTNESS —
|
|
The merge took a while because I first merged #1546, then did a performance refactor of the HLLD path, and had to re-merge onto #1550–#1556. The refactored code is a lot cleaner now though. Thanks for the improvements! The relocation map was helpful, and I followed it where I could. One thing I wanted to discuss is keeping m_riemann_solver_hypo_hlld.fpp separate from m_riemann_solver_hlld.fpp:
On a side note, I think the two developer's notes in misc/ are pretty useful. Feel free to take a look, and let me know if you'd like them moved somewhere else. |
|
For Claude Code Review: 1 is false positive. 2 I've added a PROHIBIT to be extra safe |
sbryngelson
left a comment
There was a problem hiding this comment.
High-level review of structure and organization, not a line-by-line bug pass. The change is smaller than the 110-file count suggests: most of those files are golden tests and examples. The real surface is about 6 files in src/simulation plus the global params and the dev notes.
What works, keep it:
- The new HLLD solver gets its own module, which matches the existing one-module-per-solver layout. Your reasons for not fusing it with MHD HLLD are correct. Leave them separate.
- The dev_notes derivations are good. This math is hard to recover later. Keep writing these.
- The adv_src_* and hypo_nc_dual_pass flags are derived once in m_global_parameters, not raw user inputs. That is the right place for them.
Main point: hypoelasticity now enters the Riemann solvers in three different shapes.
- HLLC: inline "if (hypoelasticity)" branches inside the existing ~1970-line s_hllc_riemann_solver.
- HLL: similar inline branches.
- HLLD: a separate module plus a separate hypo_nc_dual_pass selection path.
Each choice on its own is fine. Together, anyone asking "how is hypoelasticity handled at interfaces" has to read three different shapes of code. I would not refactor HLLC in this PR (you scoped that out, correctly), but add a short comment at each site, and in dev_notes, that names all three and says why HLLD needs its own path.
Two cleanups that are cheap and in scope:
- Collapse the _hatR duplication in the dual pass. The second pass duplicates whole field sets (nc_iface_vel_n and nc_iface_vel_hatR_n) and finalize routines (the regular ones and their _hatR twins). The pass identity, left-anchored vs right-anchored, should be an argument, not a copied routine. This roughly halves the dual-pass code and stops the two copies from drifting apart.
- Replace the three adv_src_* booleans with one enum. They are mutually exclusive by construction, and the code already asks maintainers to keep them that way by hand. An enum makes the "two true at once" state impossible and drops the manual rule.
Two notes for later, mostly out of scope:
3. m_rhs.fpp is already ~2200 lines. The dual pass deepens it: allocate hatR fields, solve, finalize hat_L, assemble RHS, finalize hat_R, assemble RHS again. That orchestration would be better in its own routine. Not required here, just flagging it so it does not harden.
4. The 900 to 2000 line solver subroutines are a pre-existing pattern, not something you introduced. Your new solver at 934 lines is actually better than HLLC because you split out the finalize helpers. Splitting the left/right state setup and the per-wave blocks into helpers would set a good example, but it is not a blocker.
Net: the hard parts, the module boundary and the documentation, are right. Before this leaves draft I would do (1) parameterize the dual pass instead of copying hatR, and (2) make adv_src* an enum. The rest is comments or future debt. Inline notes below.
| integer, intent(in) :: norm_dir | ||
| type(int_bounds_info), intent(in) :: ix, iy, iz | ||
|
|
||
| if (hypo_nc_dual_pass) then |
There was a problem hiding this comment.
This is a second solver-selection path. The other solvers are picked by riemann_solver == NUM in the loop below; the dual-pass HLLD is picked by this separate boolean and returns early. Since hypo_nc_dual_pass is derived from riemann_solver and the elasticity flags, the two paths stay consistent, so this is fine to ship. But it is the point where hypoelasticity splits into three different shapes across the Riemann solvers: inline branches in HLLC and HLL, and this separate module for HLLD. Worth a short comment here, and in dev_notes, that names all three and says why HLLD needs its own path.
There was a problem hiding this comment.
Added comments and explained clearly. Also added a corresponding note in misc/dev_notes and back references at HLL/HLLC sites
| ! adv_src_alpha_iface, adv_src_vel_iface, and adv_src_none true whenever this logic is modified. | ||
| ! | ||
| ! HLL Method 1 (alpha-interface): flux_src(adv_idx%beg:adv_idx%end) carries interface alpha_k per fluid. | ||
| adv_src_alpha_iface = (riemann_solver == 1 .and. .not. hll_u_interface) |
There was a problem hiding this comment.
These three booleans are mutually exclusive by construction, and the comment just above asks maintainers to keep them that way by hand. A single enum (for example adv_src_mode with values alpha_iface, vel_iface, none) would make the two-true-at-once state impossible and drop the manual rule. Same readability win at every if (adv_src_*) site downstream.
There was a problem hiding this comment.
Added adv_src_mode enum as you suggested and dropped the manual rule.
|
|
||
| !> hat_R-pass interface velocities for fused dual-pass HLLD: the axisymmetric geometric source consumes both passes' face | ||
| !! velocities after the direction loop, so the hat_R values need their own field set (hat_L uses nc_iface_vel_n). | ||
| type(vector_field), allocatable, dimension(:) :: nc_iface_vel_hatR_n |
There was a problem hiding this comment.
This _hatR field set is a near copy of nc_iface_vel_n, and the dual pass also adds _hatR copies of the finalize routines in the hypo HLLD module. The left-anchored vs right-anchored distinction reads like it should be a parameter, not a duplicated field set plus routine. Passing the pass identity in would remove the copy and stop the two from drifting. Separate, larger point: the dual-pass orchestration (allocate hatR, solve, finalize hat_L, assemble, finalize hat_R, assemble again) sits inside an already ~2200-line m_rhs. Good candidate to move into its own routine later.
There was a problem hiding this comment.
Used a template to factor out the _hatR routines and removed the duplicated parts. I kept nc_iface_vel_hatR_n as it's not a copy but a separate storage per pass. The axisymmetric geometric source needs to run after the direction loop and needs both passes' velocities, so we can't overwrite the hat_L values before the source uses it.
| contains | ||
|
|
||
| !> HLLD Riemann solver resolves all 5 waves for the hypoelastic equations: 1 entropy wave, 2 shear stress waves, 2 fast waves. | ||
| subroutine s_hypo_hlld_riemann_solver(qL_prim_rsx_vf, dqL_prim_dx_vf, dqL_prim_dy_vf, dqL_prim_dz_vf, qL_prim_vf, & |
There was a problem hiding this comment.
This subroutine is about 934 lines. That is actually better than the existing HLLC solver at ~1970, and you already split out the finalize helpers below, which is the right instinct. Not a blocker, and the giant-subroutine pattern is pre-existing here. If you want to set a better example, the left/right state setup and the per-wave-region blocks are natural helper boundaries.
There was a problem hiding this comment.
Agreed that it's not a blocker here
| !! output arrays. Mirrors s_finalize_nc_iface_vel for the nc_iface_vel_hatR_rs* buffers. | ||
| !! @param nc_iface_vel_vf Output: physical hat_R velocity components at interfaces | ||
| !! @param norm_dir Sweep direction (1=x, 2=y, 3=z) | ||
| subroutine s_finalize_nc_iface_vel_hatR(nc_iface_vel_vf, norm_dir) |
There was a problem hiding this comment.
This is a copy of s_finalize_nc_iface_vel above with a _hatR suffix, and s_finalize_riemann_solver_hatR mirrors the regular finalize the same way. Two copies that have to stay in sync by hand. Consider one routine that takes the pass (left vs right) or the target field set as an argument, instead of the _hatR twin. Same point as the field duplication noted in m_rhs.
There was a problem hiding this comment.
Used a template to factor out s_finalize_nc_iface_vel and _hatR. Kept s_finalize_riemann_solver_hatR as it's a subset of the shared finalize, without flux_src, gg3 gsrc.
|
Made all the requested changes other than a few minor caveats. I've explained all the changes in the comment replies |
|
@ChrisZYJ - pushed two correctness fixes to this branch (bb0090e), both flagged by the Claude review:
Both compile. |
|
@sbryngelson Many thanks for the changes! Speaking of point 2, there's an important, subtle bug outside of hypoelasticity: "num_fluids > 2 .and. alt_soundspeed" is not prohibited in the checkers, which silently computes the wrong "K-term". We should add a checker to enforce that alt_soundspeed is only used for 2 components (alt_soundspeed is only for the 5-equation model). I can open a separate PR for that. |
There was a problem hiding this comment.
Pull request overview
Note
Copilot couldn't run its full agentic review because no GitHub Actions runner was available. Make sure your repository has a runner available to run Copilot's review, or add a copilot-setup-steps.yml file specifying one with the runs-on attribute. See the docs for more details.
Adds hypoelasticity support for additional Riemann solvers, including an anchored dual-pass HLLD path, and wires up new non-conservative (NC) RHS dataflow/validation/documentation to support these solvers.
Changes:
- Adds hypoelasticity HLLD dual-pass dispatch plus supporting state buffers (hat-R flux set) and NC interface-velocity export.
- Introduces new runtime/toolchain parameters + validation for hypoelasticity solver options (ADC, interface-consistent RHS, energy guard, HLL u-interface mode).
- Updates docs, examples, and golden test artifacts for the new solver capabilities.
Reviewed changes
Copilot reviewed 70 out of 110 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| toolchain/mfc/test/case.py | Loosens tolerance for hypoelasticity test comparisons. |
| toolchain/mfc/params/descriptions.py | Documents newly introduced hypoelasticity/HLL configuration parameters. |
| toolchain/mfc/params/definitions.py | Registers new parameters and adds constraints/visibility metadata. |
| toolchain/mfc/case_validator.py | Extends toolchain-side validation for hypoelasticity + new flags. |
| tests/F31EAABF/golden-metadata.txt | Adds new golden metadata file for regression test artifacts. |
| tests/DA44D68D/golden-metadata.txt | Adds new golden metadata file for regression test artifacts. |
| tests/D41FFB94/golden-metadata.txt | Adds new golden metadata file for regression test artifacts. |
| tests/CF11AA56/golden-metadata.txt | Adds new golden metadata file for regression test artifacts. |
| tests/C7B686C0/golden-metadata.txt | Adds new golden metadata file for regression test artifacts. |
| tests/B76C4F04/golden-metadata.txt | Adds new golden metadata file for regression test artifacts. |
| tests/AE3ECF01/golden-metadata.txt | Adds new golden metadata file for regression test artifacts. |
| tests/AB56B056/golden-metadata.txt | Adds new golden metadata file for regression test artifacts. |
| tests/A26B7E00/golden-metadata.txt | Adds new golden metadata file for regression test artifacts. |
| tests/9AEC024A/golden-metadata.txt | Adds new golden metadata file for regression test artifacts. |
| tests/891C8626/golden-metadata.txt | Adds new golden metadata file for regression test artifacts. |
| tests/879C490D/golden-metadata.txt | Adds new golden metadata file for regression test artifacts. |
| tests/6AF90F3C/golden-metadata.txt | Adds new golden metadata file for regression test artifacts. |
| tests/6736AFD0/golden-metadata.txt | Adds new golden metadata file for regression test artifacts. |
| tests/6103FA4F/golden-metadata.txt | Adds new golden metadata file for regression test artifacts. |
| tests/5D405BF9/golden-metadata.txt | Adds new golden metadata file for regression test artifacts. |
| tests/4FBA4023/golden-metadata.txt | Adds new golden metadata file for regression test artifacts. |
| tests/4912EFF1/golden-metadata.txt | Adds new golden metadata file for regression test artifacts. |
| tests/481CA4B4/golden-metadata.txt | Adds new golden metadata file for regression test artifacts. |
| tests/43CADBB8/golden-metadata.txt | Adds new golden metadata file for regression test artifacts. |
| tests/34F3999B/golden-metadata.txt | Adds new golden metadata file for regression test artifacts. |
| tests/345BC486/golden-metadata.txt | Adds new golden metadata file for regression test artifacts. |
| tests/34336A1F/golden-metadata.txt | Adds new golden metadata file for regression test artifacts. |
| tests/17E2C6D1/golden-metadata.txt | Adds new golden metadata file for regression test artifacts. |
| tests/29C5D458/golden-metadata.txt | Adds new golden metadata file for regression test artifacts. |
| tests/2097140E/golden-metadata.txt | Adds new golden metadata file for regression test artifacts. |
| tests/19981E38/golden-metadata.txt | Adds new golden metadata file for regression test artifacts. |
| tests/0DDE8A87/golden-metadata.txt | Adds new golden metadata file for regression test artifacts. |
| tests/0648E422/golden-metadata.txt | Adds new golden metadata file for regression test artifacts. |
| tests/03EB2617/golden-metadata.txt | Adds new golden metadata file for regression test artifacts. |
| tests/026AA23F/golden-metadata.txt | Adds new golden metadata file for regression test artifacts. |
| src/simulation/m_riemann_state.fpp | Adds new state buffers (flux hat-R + nc_iface_vel) and stress tensor permutation support for elasticity. |
| src/simulation/m_riemann_solvers.fpp | Adds hypoelastic HLLD solver dispatch, alloc/dealloc for new buffers, and dual-pass early-return path. |
| src/simulation/m_riemann_solver_lf.fpp | Updates hypoelastic elastic-energy contribution computation with optional guard and safe denominators. |
| src/simulation/m_global_parameters.fpp | Adds derived NC modes, adv_src_mode selection, and use_nc_iface_vel flag; wires new parameters. |
| src/simulation/m_data_output.fpp | Fixes pressure computation call site to pass energy by correct index. |
| src/simulation/m_checker.fpp | Adds runtime input validation for new HLL/Hypo NC branches and solver compatibility rules. |
| src/pre_process/m_global_parameters.fpp | Establishes default for hypo_energy_guard in pre_process. |
| src/post_process/m_global_parameters.fpp | Establishes default for hypo_energy_guard in post_process. |
| src/common/m_variables_conversion.fpp | Adds guarded elastic-energy (add/subtract) treatment to avoid division-by-near-zero G. |
| src/common/m_global_parameters_common.fpp | Exposes new parameters to device via GPU_DECLARE. |
| src/common/include/shared_parallel_macros.fpp | Improves folding of long GPU directives by also splitting oversized clauses at commas. |
| examples/3D_hypo_hlld/case.py | Adds a 3D hypoelastic HLLD example configuration. |
| examples/2D_hypo_hlld/case.py | Adds a 2D hypoelastic HLLD example configuration. |
| examples/2D_axisym_hypo_hlld/case.py | Adds a 2D axisymmetric hypoelastic HLLD example configuration. |
| docs/module_categories.json | Registers the new hypoelastic HLLD solver module in documentation categories. |
| docs/documentation/equations.md | Documents hypoelastic HLLD and NC-term developer notes references. |
| docs/documentation/case.md | Documents new user-facing parameters and updated solver availability for hypoelasticity. |
Comments suppressed due to low confidence (1)
tests/F31EAABF/golden-metadata.txt:1
- These golden metadata files embed ephemeral environment details (timestamp, local branch name, and a '(dirty)' marker). If any CI/test logic compares these files byte-for-byte, this will be brittle across regeneration and developer environments. Consider either excluding such metadata from golden comparisons (e.g., ignore sections/lines) or stripping volatile fields when generating the golden-metadata.txt files.
| ! Elastic energy (guard skips when G near zero) | ||
| if (.not. hypo_energy_guard .or. ((G_L > verysmall) .and. (G_R > verysmall))) then | ||
| E_L = E_L + (tau_e_L(i)*tau_e_L(i))/max(4._wp*G_L, verysmall) | ||
| E_R = E_R + (tau_e_R(i)*tau_e_R(i))/max(4._wp*G_R, verysmall) | ||
| ! Double for shear stresses | ||
| if (any(eqn_idx%stress%beg - 1 + i == shear_indices)) then | ||
| E_L = E_L + (tau_e_L(i)*tau_e_L(i))/(4._wp*G_L) | ||
| E_R = E_R + (tau_e_R(i)*tau_e_R(i))/(4._wp*G_R) | ||
| E_L = E_L + (tau_e_L(i)*tau_e_L(i))/max(4._wp*G_L, verysmall) | ||
| E_R = E_R + (tau_e_R(i)*tau_e_R(i))/max(4._wp*G_R, verysmall) | ||
| end if | ||
| end if |
| @:PROHIBIT(hypoelasticity .and. riemann_solver == 4 .and. num_fluids /= 2, & | ||
| & "HLLD hypoelasticity currently requires exactly 2 fluid components") | ||
| @:PROHIBIT(hypoelasticity .and. (riemann_solver == 1 .or. riemann_solver == 2) .and. num_fluids /= 2, & | ||
| & "HLL/HLLC hypoelasticity currently requires exactly 2 fluid components (blkmod uses fluid_pp(1:2) only)") |
| $:GPU_UPDATE(device='[int_comp, ic_eps, ic_beta]') | ||
| $:GPU_UPDATE(device='[muscl_eps]') | ||
| $:GPU_UPDATE(device='[dir_idx, dir_flg, dir_idx_tau]') | ||
| $:GPU_UPDATE(device='[dir_idx, dir_flg, dir_idx_tau, stress_perm]') |
…2 (1-fluid is valid)
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #1414 +/- ##
==========================================
+ Coverage 60.48% 60.59% +0.10%
==========================================
Files 83 84 +1
Lines 19888 20811 +923
Branches 2951 3095 +144
==========================================
+ Hits 12030 12610 +580
- Misses 5864 6089 +225
- Partials 1994 2112 +118 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Description
Adds:
Key Design Choices
Separate HLLD Riemann Solvers
At a glance it might be tempting to combine HLLD MHD with dual-pass hypoelasticity HLLD, but keeping them separate makes the code cleaner and much easier to maintain because:
Riemann Source Terms
For the non-conservative terms, unlike the usual governing equations that only need div U i.e. du/dx, dv/dy, dw/dz (alpha div U, K div U, etc.), Hypoelasticity has cross terms like du/dy, so we must also pass those Riemann-consistent traces from Riemann solver to the rhs. (The old Hypoelasticity code with the HLL Riemann solver uses finite difference for non-conservative rhs, which provides enough stability given that HLL smears the interface immediately, so there wasn't a need to pass the du/dy traces before this PR. But that does not work for HLLC/HLLD for Hypoelasticity.)
Also grouped/named the condition branches (with lots of comments within the code):
adv_src_alpha_ifaceflux_src_n(dir)%vf(j_adv)= per-fluidnc_iface_vel_n(dir)%vf(1)adv_src_vel_ifaceflux_src_n(dir)%vf(adv\%beg)= sharedflux_src_nslot (alreadyadv_src_noneThe derivations, meanings, and usage of the Riemann source variables are not straightforward. I've added some hopefully very helpful notes in
misc/dev_notesfor future developers (or AI agents; directing them to my notes should help them make fewer mistakes with the source terms) in terms of the understanding and derivations for the HLL/HLLC non-conservative fluxes, and their variable mapping for Riemann solvers and RHS.Backwards Compatibiilty
Type of change
Testing
All tests passed locally on CPU and Nvidia GPU, and on Frontier.
Smooth Eigenmode Convergence
Checklist
AI code reviews
Reviews are not triggered automatically. To request a review, comment on the PR:
@coderabbitai review— incremental review (new changes only)@coderabbitai full review— full review from scratch/review— Qodo review/improve— Qodo code suggestions@claude full review— Claude full review (also triggers on PR open/reopen/ready)claude-full-review— Claude full review via label