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 82 commits into
Conversation
… single-side us/uss + zone)
|
Heads-up on what changed in master since this branch was cut, since the conflicts here are structural rather than mechanical:
We attempted a maintainer-side conflict resolution and got everything mechanical done, but the HLLD flux block (and a couple of smaller physics choices, e.g. the elastic flux adjustment and the |
Resolves 12 conflicted files. Hypo code migrated to the reshape-removed physical-layout convention (MFlowCode#1433): single rsx buffers + SF()-pattern face indexing in s_hll/s_hllc hypo blocks, s_hypo_hlld kernel, and hatR/nc_iface finalizers. Params re-registered via NAMELIST_VARS (MFlowCode#1456). HLLC xi-factor fix (MFlowCode#1403) preserved in non-hypo branches; hypo branches kept numerically verbatim. Validation: bitcheck + hypo suite + full suite follow as separate verification.
…beta (MFlowCode#1408) Upstream's WENO5 sum-of-squares smoothness indicators (cancellation-free on uniform grids, MFlowCode#1408) change WENO5 answers; upstream regenerated 18 of its own goldens in that PR. These two PR-added tests (2D axisym HLL u-interface, weno_order=5, tol 1e-11) are the only ones in the suite sensitive enough to exceed tolerance (rel up to 7.4e-3 after 50 steps). Attribution verified by control: the same config at weno_order=1 differs between pre-merge and merged trees by at most 1 ulp (6e-16 rel), so the u-interface/axisym code path itself is exact; the shift is entirely the WENO5 algorithm change. All 47 hypoelasticity tests and the remaining 614 suite tests pass against existing goldens.
The elastic branches of s_write_probe_files passed q_cons_vf(1) (partial density) instead of q_cons_vf(eqn_idx%E) (total energy) to s_compute_pressure in all three dimensional branches, yielding a constant nonphysical probe pressure for hypoelastic runs. Diagnostic-only: solver and golden outputs unaffected. Regression introduced by the eqn_idx refactor.
|
Thanks for the update. I'm working on the merge together with a few other changes - will commit soon |
|
Following up on my earlier note about #1546 (x-only flux arrays) — the Riemann refactor I mentioned has now landed (#1550–#1556), and since you said you're mid-merge, here is a map of where your touched regions now live on
New parameters must go through the registry now. Your hand-edited declarations and Other mappings: your Two cautions that still stand: (1) the x-only flux architecture concern from my earlier comment — everything writes Happy to help with the mechanical relocation once you push your merge — ping me here. |
|
No movement/response from author. |
|
@sbryngelson almost done with the merge |
…iemann split)
|
@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.
| ! 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.
|
|
||
| !> 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.
| 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.
| !! 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.
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