Skip to content

MHD Hyperbolic Divergence Cleaning#1086

Merged
sbryngelson merged 33 commits intoMFlowCode:masterfrom
ChrisZYJ:mhd_hypercleaning
Feb 3, 2026
Merged

MHD Hyperbolic Divergence Cleaning#1086
sbryngelson merged 33 commits intoMFlowCode:masterfrom
ChrisZYJ:mhd_hypercleaning

Conversation

@ChrisZYJ
Copy link
Contributor

@ChrisZYJ ChrisZYJ commented Dec 13, 2025

User description

User description

Description

Implements the correct hyperbolic divergence cleaning for 2D/3D MHD in place of the current Powell's method. It is fully working and validated (with results below).

It is based on an outdated MFC branch, so it needs to be rebased, cleaned up, and made GPU-ready. @danieljvickers might be interested in collaborating on this; otherwise, I will get this ready as time permits.

Formulation

image image

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Something else

Scope

  • This PR comprises a set of related changes with a common goal

How Has This Been Tested?

All the test cases are temporarily placed in the case_new folder.

  • Gaussian divergence pulse
image image image

  • Slanted Shock Tube
image image image

  • Orszag-Tang Vortex

orszag_tang_all

Orszag-Tang result at final time; comparison across WENO variants. Top row is p. Bottom row is ∇·B. The last column shows the last saved time step before crashing, for a simulation without hyperbolic cleaning. 3Z is a typo; it should be 5Z.


  • 2D Rotor Test
image

Checklist

  • I have added comments for the new code
  • I added Doxygen docstrings to the new code
  • I have made corresponding changes to the documentation (docs/)
  • I have added regression tests to the test suite so that people can verify in the future that the feature is behaving as expected
  • I have added example cases in examples/ that demonstrate my new feature performing as expected.
    They run to completion and demonstrate "interesting physics"
  • I ran ./mfc.sh format before committing my code
  • New and existing tests pass locally with my changes, including with GPU capability enabled (both NVIDIA hardware with NVHPC compilers and AMD hardware with CRAY compilers) and disabled
  • This PR does not introduce any repeated code (it follows the DRY principle)
  • I cannot think of a way to condense this code and reduce any introduced additional line count

If your code changes any code source files (anything in src/simulation)

To make sure the code is performing as expected on GPU devices, I have:

  • Checked that the code compiles using NVHPC compilers
  • Checked that the code compiles using CRAY compilers
  • Ran the code on either V100, A100, or H100 GPUs and ensured the new feature performed as expected (the GPU results match the CPU results)
  • Ran the code on MI200+ GPUs and ensure the new features performed as expected (the GPU results match the CPU results)
  • Enclosed the new feature via nvtx ranges so that they can be identified in profiles
  • Ran a Nsight Systems profile using ./mfc.sh run XXXX --gpu -t simulation --nsys, and have attached the output file (.nsys-rep) and plain text results to this PR
  • Ran a Rocprof Systems profile using ./mfc.sh run XXXX --gpu -t simulation --rsys --hip-trace, and have attached the output file and plain text results to this PR.
  • Ran my code using various numbers of different GPUs (1, 2, and 8, for example) in parallel and made sure that the results scale similarly to what happens if you run without the new code/feature

PR Type

Enhancement, Bug fix


Description

  • Replace Powell's method with hyperbolic (GLM) divergence cleaning for MHD

  • Add new state variable psi_idx for tracking magnetic divergence

  • Implement hyperbolic cleaning in Riemann solver flux calculations

  • Add three new MHD test cases: rotor, Gaussian pulse, tilted shock tube


Diagram Walkthrough

flowchart LR
  A["Powell's Method"] -->|Replace| B["Hyperbolic Cleaning"]
  B --> C["psi State Variable"]
  C --> D["Riemann Solver Fluxes"]
  D --> E["MHD Test Cases"]
  B --> F["GPU Support"]
Loading

File Walkthrough

Relevant files
Enhancement
19 files
2dHardcodedIC.fpp
Add three new MHD hardcoded initial conditions                     
+99/-2   
m_global_parameters.fpp
Add hyperbolic cleaning parameters and psi index                 
+22/-5   
m_riemann_solvers.fpp
Implement hyperbolic cleaning in flux calculations             
+18/-4   
m_mpi_proxy.fpp
Add hyperbolic cleaning to MPI broadcast variables             
+5/-3     
m_global_parameters.fpp
Add hyperbolic cleaning support to post-processor               
+10/-0   
m_start_up.fpp
Initialize psi state variable for hyperbolic cleaning       
+14/-2   
m_global_parameters.fpp
Add hyperbolic cleaning parameters to pre-processor           
+8/-0     
m_rhs.fpp
Replace Powell RHS with hyperbolic cleaning damping term 
+19/-5   
m_time_steppers.fpp
Allocate and deallocate psi state variable arrays               
+12/-0   
m_start_up.fpp
Add hyperbolic cleaning to post-processor initialization 
+9/-1     
m_variables_conversion.fpp
Add psi conversion between primitive and conservative       
+3/-0     
m_mpi_proxy.fpp
Broadcast hyperbolic cleaning flag in pre-processor           
+1/-1     
m_mpi_proxy.fpp
Broadcast hyperbolic cleaning flag in post-processor         
+1/-1     
m_initial_condition.fpp
Initialize psi to zero in initial conditions                         
+7/-0     
m_data_output.fpp
Add psi variable to database output count                               
+3/-0     
case.py
Update Orszag-Tang case to use hyperbolic cleaning             
+7/-5     
case_validator.py
Update validation to check hyperbolic cleaning constraints
+5/-8     
case_dicts.py
Add hyperbolic cleaning parameters to case dictionary       
+3/-1     
case.py
Update magnetic vortex case to use new case ID                     
+1/-1     
Bug fix
1 files
m_start_up.fpp
Remove Powell's method, add hyperbolic cleaning imports   
+2/-6     
Tests
2 files
case.py
New 2D MHD rotor test case with hyperbolic cleaning           
+84/-0   
cases.py
Replace Powell test with hyperbolic cleaning test               
+1/-1     
Documentation
2 files
case.md
Document hyperbolic cleaning parameters and remove Powell
+13/-10 
references.md
Replace Powell reference with Dedner et al. reference       
+1/-1     
Additional files
7 files
m_mhd.fpp +0/-149 
golden-metadata.txt +185/-0 
golden.txt +20/-0   
golden-metadata.txt +193/-0 
golden.txt +20/-0   
golden-metadata.txt +185/-0 
golden.txt +20/-0   

Summary by CodeRabbit

  • New Features

    • Added hyperbolic divergence cleaning (hyper_cleaning) for MHD with configurable speed and damping; exposes psi as an optional state and output variable.
  • New Examples / ICs

    • Added a 2D MHD rotor case and expanded initial-condition suite (rotor, Gaussian divergence pulse, blob, tilted shock-tube); updated Orszag–Tang hyper_cleaning case.
  • Removed

    • Powell divergence-correction method removed.
  • Post-processing / I/O

    • Binary/formatted outputs now include psi when hyper_cleaning is enabled.
  • Documentation

    • Docs and references updated to describe hyper_cleaning and compatibility notes.
  • Tests

    • Added golden metadata artifacts for CI/test validation.

CodeAnt-AI Description

Add GLM hyperbolic divergence cleaning for MHD and replace Powell's method

What Changed

  • New optional hyperbolic (GLM) divergence-cleaning mode for MHD controlled by hyper_cleaning with user parameters hyper_cleaning_speed and hyper_cleaning_tau; introduces a conserved/primitive psi field that propagates and damps div B errors.
  • Riemann fluxes, RHS, I/O and global parameters updated so when hyper_cleaning is enabled the solver propagates and damps magnetic divergence; when disabled the normal MHD behavior remains (Powell option removed).
  • New example and test cases added (2D MHD rotor, Gaussian divergence pulse, tilted shock, Orszag–Tang variant) and documentation updated to describe hyper_cleaning, its parameters, and reference.
  • psi is written to formatted output when enabled and initialized consistently; new golden test metadata included for validation.

Impact

✅ Fewer div B artifacts in 2D/3D MHD runs when enabled
✅ New 2D MHD rotor example for quick validation
✅ Clearer MHD configuration and documented cleaning parameters

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 13, 2025

📝 Walkthrough

Walkthrough

Replaces Powell divergence correction with Dedner-style hyperbolic divergence cleaning for MHD: adds hyper_cleaning flag, psi state index and parameters (speed, tau), removes Powell module, updates initialization, MPI broadcasts, Riemann fluxes, RHS, IO, examples, and docs to support the new psi-based cleaning path.

Changes

Cohort / File(s) Summary
Global parameters & GPU reflection
src/simulation/m_global_parameters.fpp, src/pre_process/m_global_parameters.fpp, src/post_process/m_global_parameters.fpp
Added hyper_cleaning (logical), psi_idx (int), hyper_cleaning_speed, hyper_cleaning_tau (reals); removed public powell; adjusted sys_size allocation and GPU reflection to include new params.
Module removal
src/simulation/m_mhd.fpp
Deleted Powell-based MHD module entirely (initialization, RHS, finalization procedures and related data/GPU code).
Riemann solver & RHS
src/simulation/m_riemann_solvers.fpp, src/simulation/m_rhs.fpp
Constrain c_fast by ±hyper_cleaning_speed; add psi flux propagation in flux updates; add psi RHS relaxation term (-psi/hyper_cleaning_tau) and data transfers gated by hyper_cleaning.
State, allocation & conversion
src/pre_process/m_initial_condition.fpp, src/simulation/m_time_steppers.fpp, src/common/m_variables_conversion.fpp
Allocate/zero q_cons_vf(psi_idx) and q_prim_vf(psi_idx) when enabled; add psi mapping in conservative↔primitive conversions.
Startup, inputs & MPI
src/simulation/m_start_up.fpp, src/pre_process/m_start_up.fpp, src/post_process/m_start_up.fpp, src/pre_process/m_mpi_proxy.fpp, src/post_process/m_mpi_proxy.fpp, src/simulation/m_mpi_proxy.fpp
Remove powell from namelists and init/finalize calls; add hyper_cleaning, hyper_cleaning_speed, hyper_cleaning_tau to input parsing and MPI broadcasts; minor namelist tidy (removed bc_y).
I/O & post-process
src/post_process/m_data_output.fpp, src/post_process/m_start_up.fpp
Increment binary output variable count when hyper_cleaning enabled; add conditional formatted output of psi variable.
Routines & helpers
src/simulation/m_global_parameters.fpp, src/simulation/m_time_steppers.fpp, src/simulation/m_start_up.fpp, src/simulation/m_mpi_proxy.fpp
Propagate new public params into runtime/device updates, allocate/deallocate psi primitive fields, remove powell references in startup/finalize flows.
Hardcoded ICs
src/common/include/2dHardcodedIC.fpp
Repurposed case 252 as 2D MHD rotor; re-labeled 253 as magnetic vortex; added cases 260–262 (Gaussian divergence pulse, blob, tilted shock-tube) and supporting internal vars.
Examples & tests
examples/2D_mhd_rotor/case.py, examples/2D_orszag_tang_hyper_cleaning/case.py, examples/2D_mhd_magnetic_vortex/case.py, tests/*/golden-metadata.txt
Added rotor example with hyper_cleaning; updated Orszag-Tang and vortex example parameters; added three golden-metadata files (test metadata).
Toolchain & validation
toolchain/mfc/run/case_dicts.py, toolchain/mfc/case_validator.py, toolchain/mfc/test/cases.py
Add hyper_cleaning, hyper_cleaning_speed, hyper_cleaning_tau parameters; remove powell; update validation rules (require mhd, disallow 1D with hyper_cleaning); rename test case reference.
Documentation
docs/documentation/case.md, docs/documentation/references.md
Replace Powell docs with Dedner/hyper_cleaning section; update parameter descriptions and reference (Powell94 → Dedner02).

Sequence Diagram

sequenceDiagram
    participant TS as TimeStepper
    participant RS as RiemannSolver
    participant RHS as RHSModule
    participant State as StateVector
    rect rgba(135,206,235,0.5)
    Note over TS,RS: Per-time-step flux & psi propagation
    TS->>RS: Request fluxes (limit c_fast to ±hyper_cleaning_speed)
    RS->>RS: Compute fluxes, add psi flux terms (propagate ∇·B)
    RS->>RHS: Return fluxes
    end
    rect rgba(144,238,144,0.5)
    Note over RHS,State: Source & relaxation update
    RHS->>State: Apply flux divergences
    RHS->>State: Update rhs(psi_idx) += -q_prim(psi_idx)/hyper_cleaning_tau
    State->>TS: Advance q_cons including psi
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

size:XXL

Suggested reviewers

  • wilfonba

Poem

🐰
I spun the rotor, tuned the psi,
Let Dedner chase the stray B-dye,
Speeds constrained and taus set true,
A cleaner field hops through and through! 🥕✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The PR description covers the core changes and includes test validation results, but several required checklist items remain unchecked (comments, docstrings, tests, formatting, GPU verification). Clarify the status of unchecked items: confirm whether code comments and Doxygen docstrings were added, whether regression tests and example cases have been properly included, and provide update on GPU compilation/testing status (NVHPC, CRAY, and device runs).
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'MHD Hyperbolic Divergence Cleaning' directly and clearly summarizes the main change: replacing Powell's method with hyperbolic divergence cleaning for MHD.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@sbryngelson
Copy link
Member

beautiful! @ChrisZYJ
minus the old branching!

Maybe something we can look at...

@danieljvickers
Copy link
Member

Yeah, this the the hyperbolic solver I told you I was going to add. The one I put in this week is an outdated elliptic solver.

I was hoping to update this one as I have time and redo our 2D convergence test that I added previously.

@danieljvickers
Copy link
Member

danieljvickers commented Jan 23, 2026

Hey, @ChrisZYJ, I think I almost have this merged with master. I wanted to ask about this variable rkck_adap_dt that you add to the code. From what I can see, it was never explicitly declared as a global nor is it used anywhere in the code. Is this something that you intend to keep around? If so, can you point me to where it is suppose to be getting used?

This appear to work fine if I just remove it, but I wanted to see if there is a purpose to it sticking around

@ChrisZYJ
Copy link
Contributor Author

Hi @danieljvickers , thanks for the help with the PR!!

rkck_adap_dt is unrelated to this commit and has been present since at least v5.0.0. I can clean it up for the merge if you’d like.

@danieljvickers
Copy link
Member

I don't mind finishing it up. It looks like it's just an issue casting double to complex. You've really done most the heavy lifting already.

1 similar comment
@danieljvickers
Copy link
Member

I don't mind finishing it up. It looks like it's just an issue casting double to complex. You've really done most the heavy lifting already.

@ChrisZYJ
Copy link
Contributor Author

Thanks! Please let me know if you need me to work on anything.

@danieljvickers
Copy link
Member

MacOS failures are due to issues with updated gcc in the latest brew update. I implemented a fix on #1111, which currently is passing all tests. Once that gets merged, we can pull that in and resolve the failure.

@codecov
Copy link

codecov bot commented Jan 24, 2026

Codecov Report

❌ Patch coverage is 48.97959% with 25 lines in your changes missing coverage. Please review.
✅ Project coverage is 44.03%. Comparing base (bac27a5) to head (ba7b4af).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
src/simulation/m_rhs.fpp 44.44% 4 Missing and 1 partial ⚠️
src/simulation/m_riemann_solvers.fpp 37.50% 4 Missing and 1 partial ⚠️
src/pre_process/m_initial_condition.fpp 0.00% 2 Missing and 1 partial ⚠️
src/pre_process/m_start_up.fpp 40.00% 2 Missing and 1 partial ⚠️
src/simulation/m_time_steppers.fpp 0.00% 2 Missing and 1 partial ⚠️
src/post_process/m_start_up.fpp 60.00% 1 Missing and 1 partial ⚠️
src/common/m_variables_conversion.fpp 0.00% 1 Missing ⚠️
src/post_process/m_data_output.fpp 0.00% 1 Missing ⚠️
src/post_process/m_global_parameters.fpp 75.00% 0 Missing and 1 partial ⚠️
src/pre_process/m_global_parameters.fpp 75.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1086      +/-   ##
==========================================
- Coverage   44.05%   44.03%   -0.02%     
==========================================
  Files          71       70       -1     
  Lines       20649    20649              
  Branches     2042     2053      +11     
==========================================
- Hits         9097     9093       -4     
+ Misses      10376    10368       -8     
- Partials     1176     1188      +12     

☔ View full report in Codecov by Sentry.
📢 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.

@ChrisZYJ
Copy link
Contributor Author

@danieljvickers No problem, glad we caught it! By the way, thanks for moving the damping equations to the RHS so they align better with the literature.

I see the Frontier compilation is still failing, but don't want to cause merge conflicts with overlapping changes. Let me know if you want me to take a look!

@danieljvickers
Copy link
Member

Yeah, o saw the failure yesterday and restarted it to see if that would fix it. I saw it pass before I added the GPU change, and I didn't touch anything in common or post process. So my bet is that this is spurious failures on frontier. But I'll take a look.

@danieljvickers
Copy link
Member

danieljvickers commented Jan 28, 2026

@sbryngelson This PR is now ready for merge, as long as there isn't any other input from @ChrisZYJ

@sbryngelson
Copy link
Member

very good, waiting on @anandrdbz for the AMDFlang compiler stuff first.

@sbryngelson sbryngelson marked this pull request as ready for review January 28, 2026 17:07
Copilot AI review requested due to automatic review settings January 28, 2026 17:07
@qodo-code-review
Copy link
Contributor

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 4 🔵🔵🔵🔵⚪
🧪 PR contains tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Possible Issue

The new MHD magnetic-field flux computation no longer masks out the normal component during the per-component loop, and then later conditionally overwrites/adds to the normal component depending on hyper_cleaning. This can lead to the normal component being computed twice (baseline term plus GLM adjustment) when hyper_cleaning is enabled, and to different behavior than intended compared to the previous dir_flg gating. Please verify the intended GLM/HLL(HLLD) flux form for B_normal and ensure only the correct contribution is present.

! B_z d/d${XYZ}$ flux = (1 - delta(z,${XYZ}$)) * (v_${XYZ}$ * B_z - v_z * B_${XYZ}$)
$:GPU_LOOP(parallelism='[seq]')
do i = 0, 2
    flux_rs${XYZ}$_vf(j, k, l, B_idx%beg + i) = (s_M*(vel_R(dir_idx(1))*B%R(i + 1) - vel_R(i + 1)*B%R(norm_dir)) - &
                                                 s_P*(vel_L(dir_idx(1))*B%L(i + 1) - vel_L(i + 1)*B%L(norm_dir)) + &
                                                 s_M*s_P*(B%L(i + 1) - B%R(i + 1)))/(s_M - s_P)
end do

if (hyper_cleaning) then
    ! propagate magnetic field divergence as a wave
    flux_rs${XYZ}$_vf(j, k, l, B_idx%beg + norm_dir - 1) = flux_rs${XYZ}$_vf(j, k, l, B_idx%beg + norm_dir - 1) + &
                                                           (s_M*qR_prim_rs${XYZ}$_vf(j + 1, k, l, psi_idx) - s_P*qL_prim_rs${XYZ}$_vf(j, k, l, psi_idx))/(s_M - s_P)

    flux_rs${XYZ}$_vf(j, k, l, psi_idx) = (hyper_cleaning_speed**2*(s_M*B%R(norm_dir) - s_P*B%L(norm_dir)) + s_M*s_P*(qL_prim_rs${XYZ}$_vf(j, k, l, psi_idx) - qR_prim_rs${XYZ}$_vf(j + 1, k, l, psi_idx)))/(s_M - s_P)
else
    flux_rs${XYZ}$_vf(j, k, l, B_idx%beg + norm_dir - 1) = 0._wp ! Without hyperbolic cleaning, make sure flux of B_normal is identically zero
end if
Possible Issue

psi is initialized to zero in the general IC path, but pre_process startup additionally injects a hard-coded nonzero Gaussian psi field whenever hyper_cleaning is enabled. This is surprising, case-specific behavior in a generic startup routine and may desynchronize pre_process vs simulation assumptions, affect regression determinism, and break users’ expectations that psi defaults to zero unless explicitly specified by an IC.

! hard-coded psi
if (hyper_cleaning) then
    do j = 0, m
        do k = 0, n
            q_cons_vf(psi_idx)%sf(j, k, 0) = 1d-2*exp(-(x_cc(j)**2 + y_cc(k)**2)/(2.0*0.05**2))
            q_prim_vf(psi_idx)%sf(j, k, 0) = q_cons_vf(psi_idx)%sf(j, k, 0)
        end do
    end do
end if
Edge Case

New hardcoded ICs use user-provided parameters (e.g., sigma for the Gaussian divergence pulse) in divisions and in erf((x-0.5)/sigma) without visible validation. If sigma is unset/zero/very small, this can produce NaNs/Infs. Consider guarding against invalid values (or documenting required patch_icpp(... )%a(*) constraints) to prevent hard-to-debug failures.

case (260)  ! Gaussian Divergence Pulse
    !  Bx(x) = 1 + C * erf((x-0.5)/σ)
    !  ⇒  ∂Bx/∂x = C * (2/√π) * exp[-((x-0.5)/σ)**2] * (1/σ)
    !  Choose C = ε * σ * √π / 2  ⇒ ∂Bx/∂x = ε * exp[-((x-0.5)/σ)**2]
    !  ψ is initialized to zero everywhere.

    eps_mhd = patch_icpp(patch_id)%a(2)
    sigma = patch_icpp(patch_id)%a(3)
    C_mhd = eps_mhd*sigma*sqrt(pi)*0.5_wp

    ! B-field
    q_prim_vf(B_idx%beg)%sf(i, j, 0) = 1._wp + C_mhd*erf((x_cc(i) - 0.5_wp)/sigma)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

High-level Suggestion

Refactor the new hardcoded initial conditions in Fortran files like 2dHardcodedIC.fpp into configurable input files for the test cases. This improves modularity and separates test configuration from core logic. [High-level, importance: 6]

Solution Walkthrough:

Before:

! File: src/common/include/2dHardcodedIC.fpp
...
case (251)
    ...
! case 252 is for the 2D MHD Rotor problem
case (252) ! 2D MHD Rotor Problem
    r_sq = (x_cc(i) - 0.5_wp)**2 + (y_cc(j) - 0.5_wp)**2
    if (r_sq <= 0.1**2) then
        q_prim_vf(contxb)%sf(i, j, 0) = 10._wp
        ...
    else if (r_sq <= 0.115**2) then
        ...
    end if
case (253) ! MHD Smooth Magnetic Vortex
    ...
case (260) ! Gaussian Divergence Pulse
    ...

After:

! File: src/common/include/2dHardcodedIC.fpp
...
case (251)
    ...
! case (252) ! MHD Smooth Magnetic Vortex (ID shifted)
!    ...
! Other hardcoded cases removed and logic moved to python case files.
...

! File: examples/2D_mhd_rotor/case.py
...
# Instead of hcid, use python functions to define ICs
# and pass them to the simulation, for example via a file
# that the Fortran code can read.
# (This framework does not seem to support python-defined ICs directly,
# so the best practice would be to generalize the existing IC system
# if possible, rather than adding more hardcoded cases.)

Comment on lines +847 to +855
! hard-coded psi
if (hyper_cleaning) then
do j = 0, m
do k = 0, n
q_cons_vf(psi_idx)%sf(j, k, 0) = 1d-2*exp(-(x_cc(j)**2 + y_cc(k)**2)/(2.0*0.05**2))
q_prim_vf(psi_idx)%sf(j, k, 0) = q_cons_vf(psi_idx)%sf(j, k, 0)
end do
end do
end if
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Relocate the hard-coded psi initialization to prevent it from being overwritten by a subsequent zero-initialization. [possible issue, importance: 9]

Suggested change
! hard-coded psi
if (hyper_cleaning) then
do j = 0, m
do k = 0, n
q_cons_vf(psi_idx)%sf(j, k, 0) = 1d-2*exp(-(x_cc(j)**2 + y_cc(k)**2)/(2.0*0.05**2))
q_prim_vf(psi_idx)%sf(j, k, 0) = q_cons_vf(psi_idx)%sf(j, k, 0)
end do
end do
end if
! This block should be moved to 'src/pre_process/m_initial_condition.fpp'
! after the zero-initialization of psi, or that initialization should be removed/made conditional.
if (hyper_cleaning) then
do j = 0, m
do k = 0, n
q_cons_vf(psi_idx)%sf(j, k, 0) = 1d-2*exp(-(x_cc(j)**2 + y_cc(k)**2)/(2.0*0.05**2))
q_prim_vf(psi_idx)%sf(j, k, 0) = q_cons_vf(psi_idx)%sf(j, k, 0)
end do
end do
end if

Comment on lines +192 to +225
case (252) ! 2D MHD Rotor Problem
! Ambient conditions are set in the JSON file.
! This case imposes the dense, rotating cylinder.
!
! gamma = 1.4
! Ambient medium (r > 0.1):
! rho = 1, p = 1, v = 0, B = (1,0,0)
! Rotor (r <= 0.1):
! rho = 10, p = 1
! v has angular velocity w=20, giving v_tan=2 at r=0.1

! Calculate distance squared from the center
r_sq = (x_cc(i) - 0.5_wp)**2 + (y_cc(j) - 0.5_wp)**2

! inner radius of 0.1
if (r_sq <= 0.1**2) then
! -- Inside the rotor --
! Set density uniformly to 10
q_prim_vf(contxb)%sf(i, j, 0) = 10._wp

! Set vup constant rotation of rate v=2
! v_x = -omega * (y - y_c)
! v_y = omega * (x - x_c)
q_prim_vf(momxb)%sf(i, j, 0) = -20._wp*(y_cc(j) - 0.5_wp)
q_prim_vf(momxb + 1)%sf(i, j, 0) = 20._wp*(x_cc(i) - 0.5_wp)

! taper width of 0.015
else if (r_sq <= 0.115**2) then
! linearly smooth the function between r = 0.1 and 0.115
q_prim_vf(contxb)%sf(i, j, 0) = 1._wp + 9._wp*(0.115_wp - sqrt(r_sq))/(0.015_wp)

q_prim_vf(momxb)%sf(i, j, 0) = -(2._wp/sqrt(r_sq))*(y_cc(j) - 0.5_wp)*(0.115_wp - sqrt(r_sq))/(0.015_wp)
q_prim_vf(momxb + 1)%sf(i, j, 0) = (2._wp/sqrt(r_sq))*(x_cc(i) - 0.5_wp)*(0.115_wp - sqrt(r_sq))/(0.015_wp)
end if
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Add an else branch to the MHD Rotor problem to initialize the ambient region's density and momentum. [possible issue, importance: 8]

Suggested change
case (252) ! 2D MHD Rotor Problem
! Ambient conditions are set in the JSON file.
! This case imposes the dense, rotating cylinder.
!
! gamma = 1.4
! Ambient medium (r > 0.1):
! rho = 1, p = 1, v = 0, B = (1,0,0)
! Rotor (r <= 0.1):
! rho = 10, p = 1
! v has angular velocity w=20, giving v_tan=2 at r=0.1
! Calculate distance squared from the center
r_sq = (x_cc(i) - 0.5_wp)**2 + (y_cc(j) - 0.5_wp)**2
! inner radius of 0.1
if (r_sq <= 0.1**2) then
! -- Inside the rotor --
! Set density uniformly to 10
q_prim_vf(contxb)%sf(i, j, 0) = 10._wp
! Set vup constant rotation of rate v=2
! v_x = -omega * (y - y_c)
! v_y = omega * (x - x_c)
q_prim_vf(momxb)%sf(i, j, 0) = -20._wp*(y_cc(j) - 0.5_wp)
q_prim_vf(momxb + 1)%sf(i, j, 0) = 20._wp*(x_cc(i) - 0.5_wp)
! taper width of 0.015
else if (r_sq <= 0.115**2) then
! linearly smooth the function between r = 0.1 and 0.115
q_prim_vf(contxb)%sf(i, j, 0) = 1._wp + 9._wp*(0.115_wp - sqrt(r_sq))/(0.015_wp)
q_prim_vf(momxb)%sf(i, j, 0) = -(2._wp/sqrt(r_sq))*(y_cc(j) - 0.5_wp)*(0.115_wp - sqrt(r_sq))/(0.015_wp)
q_prim_vf(momxb + 1)%sf(i, j, 0) = (2._wp/sqrt(r_sq))*(x_cc(i) - 0.5_wp)*(0.115_wp - sqrt(r_sq))/(0.015_wp)
end if
case (252) ! 2D MHD Rotor Problem
...
if (r_sq <= 0.1_wp**2) then
...
else if (r_sq <= 0.115_wp**2) then
...
else
! -- Ambient medium --
q_prim_vf(contxb)%sf(i, j, 0) = 1._wp
q_prim_vf(momxb)%sf(i, j, 0) = 0._wp
q_prim_vf(momxb + 1)%sf(i, j, 0) = 0._wp
end if

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Fix all issues with AI agents
In `@src/simulation/m_global_parameters.fpp`:
- Around line 853-855: When hyper_cleaning is true, add validation that
hyper_cleaning_speed and hyper_cleaning_tau are finite and > 0 to avoid
division-by-zero and invalid wave speeds; locate where hyper_cleaning,
hyper_cleaning_speed and hyper_cleaning_tau are initialized in
m_global_parameters.fpp (the routine that sets defaults/reads parameters) and
insert checks that use isfinite() (or the Fortran equivalent) and value > 0, and
if a check fails call the existing error/stop logging path with a clear message
naming hyper_cleaning_speed or hyper_cleaning_tau (so m_rhs.fpp line 993 and
m_riemann_solvers.fpp line 956 won’t receive invalid values), or set safe
fallback values and log a warning if that matches project behavior.
- Around line 294-295: The psi_idx integer (used by GPU kernels in
m_riemann_solvers and m_rhs) is missing from the device mirroring lists; update
the GPU_DECLARE and GPU_UPDATE macros/declarations to include psi_idx so the
variable is copied to/from the device and kept current for kernels. Locate the
GPU_DECLARE and GPU_UPDATE blocks where global parameters are listed (alongside
other indices) and add psi_idx to both the declaration and update lists to
ensure proper device mirroring for GPU kernels that reference psi_idx.
- Around line 1164-1168: The runtime flag hyper_cleaning unconditionally adds
psi_idx to sys_size even when MHD support (mhd) is not compiled in; add a
runtime guard that aborts when hyper_cleaning is true but mhd is false (e.g.,
check hyper_cleaning .and. .not. mhd and call s_mpi_abort with a descriptive
message) before altering psi_idx/sys_size so the extra equation cannot be
enabled without MHD; update the block that sets psi_idx/sys_size to first
validate mhd and call s_mpi_abort if invalid.

In `@src/simulation/m_rhs.fpp`:
- Around line 987-998: The divergence-cleaning damping term is using the stale
passed-in primitive array q_prim_vf instead of the up-to-date primitive computed
during c2p; in the hyper_cleaning block update the subtraction to read from
q_prim_qp%vf(psi_idx)%sf(j,k,l) divided by hyper_cleaning_tau so
rhs_vf(psi_idx)%sf is reduced using the current primitive psi value (keep the
surrounding loop and GPU pragmas and reference psi_idx, rhs_vf, q_prim_vf,
q_prim_qp, and hyper_cleaning_tau when making the change).

In `@src/simulation/m_riemann_solvers.fpp`:
- Around line 946-958: The normal-component magnetic flux
(flux_rs${XYZ}$_vf(..., B_idx%beg + norm_dir - 1)) is being computed before
applying the psi correction which reintroduces HLL dissipation; update the block
inside the hyper_cleaning branch to restore the original gating by applying (1 -
dir_flg(i + 1)) to the B_normal flux (or explicitly set flux_rs${XYZ}$_vf(...,
B_idx%beg + norm_dir - 1)=0._wp when dir_flg(i + 1) is true) before you add the
psi contribution, ensuring the psi update that uses qL_prim_rs${XYZ}$_vf,
qR_prim_rs${XYZ}$_vf and psi_idx only modifies the non-gated value.
🧹 Nitpick comments (1)
src/common/include/2dHardcodedIC.fpp (1)

256-266: Consider removing commented-out code.

The blob implementation is correct. However, lines 263-265 contain commented-out alternative formulations that appear to be debug/exploration artifacts rather than documentation.

🧹 Suggested cleanup
     case (261)  ! Blob
         r0 = 1._wp/sqrt(8._wp)
         r2 = x_cc(i)**2 + y_cc(j)**2
         r = sqrt(r2)
         alpha = r/r0
         if (alpha < 1) then
             q_prim_vf(B_idx%beg)%sf(i, j, 0) = 1._wp/sqrt(4._wp*pi)*(alpha**8 - 2._wp*alpha**4 + 1._wp)
-            ! q_prim_vf(B_idx%beg)%sf(i,j,0) = 1._wp/sqrt(4000._wp*pi) * (4096._wp*r2**4 - 128._wp*r2**2 + 1._wp)
-            ! q_prim_vf(B_idx%beg)%sf(i,j,0) = 1._wp/(4._wp*pi) * (alpha**8 - 2._wp*alpha**4 + 1._wp)
-            ! q_prim_vf(E_idx)%sf(i,j,0) = 6._wp - q_prim_vf(B_idx%beg)%sf(i,j,0)**2/2._wp
         end if

Comment on lines +294 to 295
integer :: psi_idx !< Index of hyperbolic cleaning state variable for MHD
!> @}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Find all usages of psi_idx with context
rg -n "psi_idx" -B 2 -A 2 src/simulation

Repository: MFlowCode/MFC

Length of output: 4223


🏁 Script executed:

# Check the variable declaration in m_global_parameters.fpp
head -n 300 src/simulation/m_global_parameters.fpp | tail -n 20

Repository: MFlowCode/MFC

Length of output: 1770


🏁 Script executed:

# Search for GPU_DECLARE and GPU_UPDATE in the codebase to understand the pattern
rg -n "GPU_DECLARE|GPU_UPDATE" src/simulation | head -20

Repository: MFlowCode/MFC

Length of output: 2026


🏁 Script executed:

# Check if psi_idx appears in any GPU_UPDATE declarations
rg -n "GPU_UPDATE.*psi_idx" src/simulation

Repository: MFlowCode/MFC

Length of output: 39


🏁 Script executed:

# Get the full GPU_DECLARE and GPU_UPDATE sections in m_global_parameters.fpp
sed -n '290,320p' src/simulation/m_global_parameters.fpp

Repository: MFlowCode/MFC

Length of output: 1671


🏁 Script executed:

# Check if there are additional GPU_UPDATE declarations related to global parameters elsewhere
rg -n "GPU_UPDATE.*device=.*momxb" src/simulation -A 3

Repository: MFlowCode/MFC

Length of output: 500


Add psi_idx to GPU_DECLARE and GPU_UPDATE declarations. The variable is used in GPU device code (e.g., m_riemann_solvers.fpp:954–956 to index array elements in flux calculations, and m_rhs.fpp:992–993 in GPU-enabled loops), but is missing from both GPU_DECLARE and GPU_UPDATE lists. Without device mirroring, GPU kernels access stale or uninitialized values.

GPU declaration fix
-    $:GPU_DECLARE(create='[tensor_size,species_idx,c_idx]')
+    $:GPU_DECLARE(create='[tensor_size,species_idx,c_idx,psi_idx]')
GPU update fix
         $:GPU_UPDATE(device='[momxb,momxe,advxb,advxe,contxb,contxe, &
             & bubxb,bubxe,intxb,intxe,sys_size,buff_size,E_idx, &
             & alf_idx,n_idx,adv_n,adap_dt,pi_fac,strxb,strxe, &
-            & chemxb,chemxe,c_idx,adap_dt_tol,adap_dt_max_iters]')
+            & chemxb,chemxe,c_idx,psi_idx,adap_dt_tol,adap_dt_max_iters]')
🤖 Prompt for AI Agents
In `@src/simulation/m_global_parameters.fpp` around lines 294 - 295, The psi_idx
integer (used by GPU kernels in m_riemann_solvers and m_rhs) is missing from the
device mirroring lists; update the GPU_DECLARE and GPU_UPDATE
macros/declarations to include psi_idx so the variable is copied to/from the
device and kept current for kernels. Locate the GPU_DECLARE and GPU_UPDATE
blocks where global parameters are listed (alongside other indices) and add
psi_idx to both the declaration and update lists to ensure proper device
mirroring for GPU kernels that reference psi_idx.

Comment on lines +853 to 855
hyper_cleaning_speed = dflt_real
hyper_cleaning_tau = dflt_real

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

rg -n "hyper_cleaning" -C 3 src --type f90 --type fpp

Repository: MFlowCode/MFC

Length of output: 84


🏁 Script executed:

rg -n "hyper_cleaning" -C 3 src

Repository: MFlowCode/MFC

Length of output: 21189


Add validation for hyper_cleaning_speed and hyper_cleaning_tau when hyper_cleaning = .true. These parameters default to dflt_real and are used in critical calculations without bounds checking. Specifically, hyper_cleaning_tau is used as a divisor in the RHS computation (m_rhs.fpp line 993) and hyper_cleaning_speed is squared in flux calculations (m_riemann_solvers.fpp line 956). Enforce that both are positive and finite to prevent division-by-zero and invalid wave speeds.

🤖 Prompt for AI Agents
In `@src/simulation/m_global_parameters.fpp` around lines 853 - 855, When
hyper_cleaning is true, add validation that hyper_cleaning_speed and
hyper_cleaning_tau are finite and > 0 to avoid division-by-zero and invalid wave
speeds; locate where hyper_cleaning, hyper_cleaning_speed and hyper_cleaning_tau
are initialized in m_global_parameters.fpp (the routine that sets defaults/reads
parameters) and insert checks that use isfinite() (or the Fortran equivalent)
and value > 0, and if a check fails call the existing error/stop logging path
with a clear message naming hyper_cleaning_speed or hyper_cleaning_tau (so
m_rhs.fpp line 993 and m_riemann_solvers.fpp line 956 won’t receive invalid
values), or set safe fallback values and log a warning if that matches project
behavior.

Comment on lines +1164 to +1168
if (hyper_cleaning) then
psi_idx = sys_size + 1
sys_size = psi_idx
end if

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

rg -n "hyper_cleaning" -C3 src/

Repository: MFlowCode/MFC

Length of output: 21189


🏁 Script executed:

rg -n "hyper_cleaning.*=" -B5 -A5 src/pre_process/m_start_up.fpp | head -100

Repository: MFlowCode/MFC

Length of output: 39


🏁 Script executed:

# Search for error checks or validations related to hyper_cleaning
rg -n "if.*hyper_cleaning.*mhd|if.*mhd.*hyper_cleaning" -B3 -A3 src/

Repository: MFlowCode/MFC

Length of output: 745


🏁 Script executed:

# Search for where hyper_cleaning is read from input file or parsed
rg -n "hyper_cleaning" src/pre_process/m_start_up.fpp -B10 -A10 | grep -A10 -B10 "read\|input\|parse"

Repository: MFlowCode/MFC

Length of output: 1420


🏁 Script executed:

# Check if mhd flag exists and how it's validated
rg -n "if.*mhd.*then" src/simulation/m_global_parameters.fpp | head -20

Repository: MFlowCode/MFC

Length of output: 205


🏁 Script executed:

# Search for any validation/error checking in the initialization
rg -n "s_mpi_abort" src/simulation/m_global_parameters.fpp -B5 -A2

Repository: MFlowCode/MFC

Length of output: 39


🏁 Script executed:

# Find where input parameters are read (likely a namelist)
rg -n "namelist\|read.*\(" src/pre_process/m_start_up.fpp | head -30

Repository: MFlowCode/MFC

Length of output: 39


🏁 Script executed:

# Look for the actual input parameter reading section around line 155
sed -n '130,200p' src/pre_process/m_start_up.fpp

Repository: MFlowCode/MFC

Length of output: 3291


🏁 Script executed:

# Check for any validation that checks mhd and hyper_cleaning together
rg -n "mhd\|hyper_cleaning" src/simulation/m_global_parameters.fpp | grep -A2 -B2 "1164\|1165\|1166"

Repository: MFlowCode/MFC

Length of output: 39


🏁 Script executed:

# Look at the section around 1164 in simulation/m_global_parameters.fpp
sed -n '1140,1180p' src/simulation/m_global_parameters.fpp

Repository: MFlowCode/MFC

Length of output: 1451


🏁 Script executed:

# Check if there's any validation checking mhd vs hyper_cleaning in the entire m_global_parameters.fpp
rg -n "mhd\|hyper_cleaning" src/simulation/m_global_parameters.fpp -C2 | grep -A5 -B5 "if\|validation\|error"

Repository: MFlowCode/MFC

Length of output: 39


🏁 Script executed:

# Look for any error handling around the sys_size computation
sed -n '1100,1200p' src/simulation/m_global_parameters.fpp

Repository: MFlowCode/MFC

Length of output: 3997


🏁 Script executed:

# Check what model_eqns == 2 or 3 represents and if it's related to MHD
rg -n "model_eqns" src/simulation/m_global_parameters.fpp | head -40

Repository: MFlowCode/MFC

Length of output: 512


🏁 Script executed:

# Search for model_eqns definitions/documentation
rg -n "model_eqns.*=" src/simulation/m_global_parameters.fpp -B3 -A3 | head -50

Repository: MFlowCode/MFC

Length of output: 1820


🏁 Script executed:

# Look for any input validation that ties hyper_cleaning to mhd
rg -n "hyper_cleaning\|mhd" src/simulation/m_start_up.fpp -B2 -A2 | head -80

Repository: MFlowCode/MFC

Length of output: 39


🏁 Script executed:

# Search for where MHD is enabled (mhd flag setting)
rg -n "mhd.*=" src/simulation/m_global_parameters.fpp -B3 -A3 | head -100

Repository: MFlowCode/MFC

Length of output: 916


🏁 Script executed:

# Check if hyper_cleaning has any input validation
rg -n "hyper_cleaning" src/simulation/m_start_up.fpp -B5 -A5

Repository: MFlowCode/MFC

Length of output: 741


🏁 Script executed:

# Check if there's any runtime validation that checks hyper_cleaning against mhd
rg -n "if.*hyper_cleaning" src/simulation/m_start_up.fpp -B10 -A10 | head -100

Repository: MFlowCode/MFC

Length of output: 39


🏁 Script executed:

# Look for error checking or validation subroutines in simulation startup
rg -n "subroutine.*validation\|subroutine.*check" src/simulation/m_start_up.fpp -A30

Repository: MFlowCode/MFC

Length of output: 39


🏁 Script executed:

# Check if hyper_cleaning is validated/constrained when reading input
sed -n '150,400p' src/simulation/m_start_up.fpp

Repository: MFlowCode/MFC

Length of output: 9639


🏁 Script executed:

# Look at the s_check_inputs subroutines to see if there's validation
rg -n "subroutine s_check_inputs" src/simulation/m_start_up.fpp -A50 | head -100

Repository: MFlowCode/MFC

Length of output: 39


🏁 Script executed:

# Search all files for validation of hyper_cleaning with mhd
rg -n "hyper_cleaning.*mhd|mhd.*hyper_cleaning" src/

Repository: MFlowCode/MFC

Length of output: 156


🏁 Script executed:

# Look at the entire check_inputs subroutine in simulation startup
sed -n '800,1200p' src/simulation/m_start_up.fpp | grep -A100 "subroutine s_check_inputs"

Repository: MFlowCode/MFC

Length of output: 39


🏁 Script executed:

# Check what mhd really is (parameter vs variable)
sed -n '120,140p' src/simulation/m_global_parameters.fpp

Repository: MFlowCode/MFC

Length of output: 1922


🏁 Script executed:

# Check if mhd is compile-time or runtime
rg -n "logical.*mhd\|integer.*mhd" src/simulation/m_global_parameters.fpp | head -20

Repository: MFlowCode/MFC

Length of output: 39


🏁 Script executed:

# Look for how hyper_cleaning relates to actual MHD physics
rg -n "hyper_cleaning" src/simulation/m_riemann_solvers.fpp -B5 -A5

Repository: MFlowCode/MFC

Length of output: 2561


🏁 Script executed:

# Check if there's any CMake or build-time constraint
fd "CMakeLists.txt" | head -5

Repository: MFlowCode/MFC

Length of output: 109


🏁 Script executed:

# Understand what happens when hyper_cleaning is true but mhd is not compiled
# Check if B_idx exists in non-MHD configurations
rg -n "B_idx" src/simulation/m_global_parameters.fpp | head -20

Repository: MFlowCode/MFC

Length of output: 515


🏁 Script executed:

# Check what code blocks are guarded by mhd check
rg -n "if.*mhd.*then" src/simulation/m_global_parameters.fpp -A5 | head -50

Repository: MFlowCode/MFC

Length of output: 771


Guard hyper_cleaning to require MHD at compile-time. The hyper_cleaning flag is a runtime parameter that unconditionally adds psi_idx to the system (lines 1164–1167) but is only meaningful when mhd is compiled in. While the code at line 649 in m_riemann_solvers.fpp has an inline comment linking hyper_cleaning to MHD, there is no runtime validation preventing hyper_cleaning=true when mhd is not compiled, which would introduce an unused equation. Add a runtime check (e.g., if (hyper_cleaning .and. .not. mhd) call s_mpi_abort(...)) to prevent this configuration.

🤖 Prompt for AI Agents
In `@src/simulation/m_global_parameters.fpp` around lines 1164 - 1168, The runtime
flag hyper_cleaning unconditionally adds psi_idx to sys_size even when MHD
support (mhd) is not compiled in; add a runtime guard that aborts when
hyper_cleaning is true but mhd is false (e.g., check hyper_cleaning .and. .not.
mhd and call s_mpi_abort with a descriptive message) before altering
psi_idx/sys_size so the extra equation cannot be enabled without MHD; update the
block that sets psi_idx/sys_size to first validate mhd and call s_mpi_abort if
invalid.

Comment on lines +987 to +998
if (hyper_cleaning) then
$:GPU_PARALLEL_LOOP(private='[j,k,l]', collapse=3)
do l = 0, p
do k = 0, n
do j = 0, m
rhs_vf(psi_idx)%sf(j, k, l) = rhs_vf(psi_idx)%sf(j, k, l) - &
q_prim_vf(psi_idx)%sf(j, k, l)/hyper_cleaning_tau
end do
end do
end do
$:END_GPU_PARALLEL_LOOP()
end if
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check for assignments between q_prim_vf and q_prim_qp in m_rhs.fpp
rg -n -B5 -A5 'q_prim_vf.*=.*q_prim_qp|q_prim_qp.*=.*q_prim_vf' src/simulation/m_rhs.fpp

Repository: MFlowCode/MFC

Length of output: 646


🏁 Script executed:

#!/bin/bash
# View the context around the hyper_cleaning block and check for earlier q_prim_vf updates
sed -n '950,1090p' src/simulation/m_rhs.fpp | cat -n

Repository: MFlowCode/MFC

Length of output: 6817


🏁 Script executed:

#!/bin/bash
# Search for updates to q_prim_vf in the entire file to understand when it's synchronized
# Show line numbers and context
rg -n 'q_prim_vf.*=' src/simulation/m_rhs.fpp | head -30

Repository: MFlowCode/MFC

Length of output: 148


🏁 Script executed:

#!/bin/bash
# Find the subroutine definition that contains the hyper_cleaning block
# Look backwards from line 987 to find the subroutine definition
sed -n '1,987p' src/simulation/m_rhs.fpp | grep -n 'subroutine\|function' | tail -5

Repository: MFlowCode/MFC

Length of output: 449


🏁 Script executed:

#!/bin/bash
# View the subroutine signature and variable declarations
sed -n '644,750p' src/simulation/m_rhs.fpp | cat -n

Repository: MFlowCode/MFC

Length of output: 5819


🏁 Script executed:

#!/bin/bash
# Search for psi_idx definition and usage
rg -n 'psi_idx' src/simulation/m_rhs.fpp | head -20

Repository: MFlowCode/MFC

Length of output: 501


Use current primitive psi value in divergence cleaning source term.

The damping term at line 993 reads from q_prim_vf(psi_idx)%sf, which is the passed-in parameter not synchronized with the current state. Since q_prim_qp%vf(psi_idx) is computed at line 71 during the conservative-to-primitive conversion and is already GPU-enabled, use it instead to ensure the damping applies the current divergence cleaning potential.

Change line 993 from:

q_prim_vf(psi_idx)%sf(j, k, l)/hyper_cleaning_tau

to:

q_prim_qp%vf(psi_idx)%sf(j, k, l)/hyper_cleaning_tau
🤖 Prompt for AI Agents
In `@src/simulation/m_rhs.fpp` around lines 987 - 998, The divergence-cleaning
damping term is using the stale passed-in primitive array q_prim_vf instead of
the up-to-date primitive computed during c2p; in the hyper_cleaning block update
the subtraction to read from q_prim_qp%vf(psi_idx)%sf(j,k,l) divided by
hyper_cleaning_tau so rhs_vf(psi_idx)%sf is reduced using the current primitive
psi value (keep the surrounding loop and GPU pragmas and reference psi_idx,
rhs_vf, q_prim_vf, q_prim_qp, and hyper_cleaning_tau when making the change).

Comment on lines +946 to +958
flux_rs${XYZ}$_vf(j, k, l, B_idx%beg + i) = (s_M*(vel_R(dir_idx(1))*B%R(i + 1) - vel_R(i + 1)*B%R(norm_dir)) - &
s_P*(vel_L(dir_idx(1))*B%L(i + 1) - vel_L(i + 1)*B%L(norm_dir)) + &
s_M*s_P*(B%L(i + 1) - B%R(i + 1)))/(s_M - s_P)
end do

if (hyper_cleaning) then
! propagate magnetic field divergence as a wave
flux_rs${XYZ}$_vf(j, k, l, B_idx%beg + norm_dir - 1) = flux_rs${XYZ}$_vf(j, k, l, B_idx%beg + norm_dir - 1) + &
(s_M*qR_prim_rs${XYZ}$_vf(j + 1, k, l, psi_idx) - s_P*qL_prim_rs${XYZ}$_vf(j, k, l, psi_idx))/(s_M - s_P)

flux_rs${XYZ}$_vf(j, k, l, psi_idx) = (hyper_cleaning_speed**2*(s_M*B%R(norm_dir) - s_P*B%L(norm_dir)) + s_M*s_P*(qL_prim_rs${XYZ}$_vf(j, k, l, psi_idx) - qR_prim_rs${XYZ}$_vf(j + 1, k, l, psi_idx)))/(s_M - s_P)
else
flux_rs${XYZ}$_vf(j, k, l, B_idx%beg + norm_dir - 1) = 0._wp ! Without hyperbolic cleaning, make sure flux of B_normal is identically zero
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Restore normal-B flux gating before adding psi

Line 946-948 now compute the normal-component induction flux before the psi correction, which reintroduces a non-zero HLL dissipation term in B_normal (this matches the rotor failure noted in the PR discussion). Reapply the (1 - dir_flg(i + 1)) gating (or explicitly zero B_normal) before adding the psi term.

🔧 Suggested fix
-                                        flux_rs${XYZ}$_vf(j, k, l, B_idx%beg + i) = (s_M*(vel_R(dir_idx(1))*B%R(i + 1) - vel_R(i + 1)*B%R(norm_dir)) - &
-                                                                                     s_P*(vel_L(dir_idx(1))*B%L(i + 1) - vel_L(i + 1)*B%L(norm_dir)) + &
-                                                                                     s_M*s_P*(B%L(i + 1) - B%R(i + 1)))/(s_M - s_P)
+                                        flux_rs${XYZ}$_vf(j, k, l, B_idx%beg + i) = (1._wp - dir_flg(i + 1))*( &
+                                                                                     s_M*(vel_R(dir_idx(1))*B%R(i + 1) - vel_R(i + 1)*B%R(norm_dir)) - &
+                                                                                     s_P*(vel_L(dir_idx(1))*B%L(i + 1) - vel_L(i + 1)*B%L(norm_dir)) + &
+                                                                                     s_M*s_P*(B%L(i + 1) - B%R(i + 1)))/(s_M - s_P)
🤖 Prompt for AI Agents
In `@src/simulation/m_riemann_solvers.fpp` around lines 946 - 958, The
normal-component magnetic flux (flux_rs${XYZ}$_vf(..., B_idx%beg + norm_dir -
1)) is being computed before applying the psi correction which reintroduces HLL
dissipation; update the block inside the hyper_cleaning branch to restore the
original gating by applying (1 - dir_flg(i + 1)) to the B_normal flux (or
explicitly set flux_rs${XYZ}$_vf(..., B_idx%beg + norm_dir - 1)=0._wp when
dir_flg(i + 1) is true) before you add the psi contribution, ensuring the psi
update that uses qL_prim_rs${XYZ}$_vf, qR_prim_rs${XYZ}$_vf and psi_idx only
modifies the non-gated value.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implements GLM (hyperbolic) divergence cleaning for 2D/3D MHD by introducing a new psi state variable and associated runtime parameters, replacing the prior Powell-based approach. Updates the toolchain schema/validation, simulation/pre/post-processing plumbing, documentation, and example/test cases to support the new capability.

Changes:

  • Introduces hyper_cleaning + hyper_cleaning_speed/hyper_cleaning_tau and threads a new psi_idx state variable through pre/sim/post pipelines.
  • Updates the MHD HLL flux path and adds psi damping behavior in the RHS; removes the Powell module.
  • Adds/updates example cases and test harness references; updates docs to cite Dedner et al. (2002).

Reviewed changes

Copilot reviewed 28 out of 31 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
toolchain/mfc/test/cases.py Updates MHD test entry to use the hyper-cleaning Orszag–Tang example.
toolchain/mfc/run/case_dicts.py Adds hyper_cleaning/speed/tau to the toolchain parameter schema; removes Powell key.
toolchain/mfc/case_validator.py Switches validation from powell to hyper_cleaning constraints.
tests/F057F8E6/golden-metadata.txt Adds generated golden metadata for a new/updated regression test.
tests/EC8F87D9/golden-metadata.txt Adds generated golden metadata for a new/updated regression test.
tests/B4DC99F9/golden-metadata.txt Adds generated golden metadata for a new/updated regression test.
src/simulation/m_time_steppers.fpp Allocates/deallocates psi primitive storage when hyper_cleaning is enabled.
src/simulation/m_start_up.fpp Removes Powell module init/finalize wiring; reads hyper-cleaning params.
src/simulation/m_riemann_solvers.fpp Adds hyper-cleaning wave-speed adjustment and GLM flux terms in HLL path.
src/simulation/m_rhs.fpp Adds psi handling in RHS setup and a decay source term.
src/simulation/m_mpi_proxy.fpp Broadcasts hyper_cleaning and its parameters via MPI.
src/simulation/m_mhd.fpp Removes Powell source-term module implementation.
src/simulation/m_global_parameters.fpp Adds hyper_cleaning, psi_idx, and GLM parameter definitions / sys_size accounting.
src/pre_process/m_start_up.fpp Reads hyper_cleaning and introduces a psi initialization hook.
src/pre_process/m_mpi_proxy.fpp Broadcasts hyper_cleaning via MPI in pre_process.
src/pre_process/m_initial_condition.fpp Initializes psi to zero when enabled.
src/pre_process/m_global_parameters.fpp Adds hyper_cleaning and psi_idx sizing in pre_process globals.
src/post_process/m_start_up.fpp Writes psi to formatted database output when enabled.
src/post_process/m_mpi_proxy.fpp Broadcasts hyper_cleaning via MPI in post_process.
src/post_process/m_global_parameters.fpp Adds hyper_cleaning and psi_idx sizing in post_process globals.
src/post_process/m_data_output.fpp Increments DB variable count when psi is included.
src/common/m_variables_conversion.fpp Propagates psi through primitive↔conservative conversion.
src/common/include/2dHardcodedIC.fpp Adds new hardcoded 2D MHD ICs (rotor, Gaussian divergence pulse, tilted shock tube) and reassigns the vortex case id.
examples/2D_orszag_tang_hyper_cleaning/case.py Adds Orszag–Tang example configured for hyperbolic cleaning.
examples/2D_mhd_rotor/case.py Adds a 2D MHD rotor example configured for hyperbolic cleaning.
examples/2D_mhd_magnetic_vortex/case.py Updates hardcoded IC id to match the vortex case renumbering.
docs/documentation/references.md Replaces Powell reference with Dedner et al. (2002).
docs/documentation/case.md Documents hyper_cleaning and its parameters in the case-input reference.
Comments suppressed due to low confidence (1)

src/simulation/m_global_parameters.fpp:298

  • psi_idx is used in GPU kernels (e.g., Riemann solver / RHS), but it is not included in the module’s GPU_DECLARE lists (and therefore won’t be present under default(present)), nor is it included in the GPU_UPDATE index updates. Add psi_idx to the appropriate GPU_DECLARE/GPU_UPDATE lists, and consider setting it to dflt_int when hyper_cleaning is false to avoid an uninitialized index.
    integer :: psi_idx                                 !< Index of hyperbolic cleaning state variable for MHD
    !> @}
    $:GPU_DECLARE(create='[sys_size,E_idx,n_idx,bub_idx,alf_idx,gamma_idx]')
    $:GPU_DECLARE(create='[pi_inf_idx,B_idx,stress_idx,xi_idx,b_size]')
    $:GPU_DECLARE(create='[tensor_size,species_idx,c_idx]')

self.prohibit(powell and fd_order is None,
"fd_order must be set if Powell's method is enabled")
self.prohibit(hyper_cleaning and not mhd,
"Hyperbolic cleaning requires mhd to be enabled")
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check_mhd_simulation currently allows hyper_cleaning = T with riemann_solver = 4 (HLLD), but the simulation-side hyperbolic cleaning fluxes appear to be implemented only in the HLL path. This can silently run with incorrect (missing) GLM fluxes. Add a constraint to prohibit hyper_cleaning when riemann_solver == 4 (or implement the corresponding HLLD+GLM fluxes).

Suggested change
"Hyperbolic cleaning requires mhd to be enabled")
"Hyperbolic cleaning requires mhd to be enabled")
self.prohibit(hyper_cleaning and riemann_solver == 4,
"Hyperbolic cleaning is not supported with HLLD (riemann_solver = 4)")

Copilot uses AI. Check for mistakes.
Comment on lines +688 to +691
self.prohibit(hyper_cleaning and not mhd,
"Hyperbolic cleaning requires mhd to be enabled")
self.prohibit(hyper_cleaning and n is not None and n == 0,
"Hyperbolic cleaning is not supported for 1D simulations")
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When hyper_cleaning is enabled, hyper_cleaning_speed and hyper_cleaning_tau are required for stability/correctness (and hyper_cleaning_tau is used as a divisor in the RHS). Add validation to require both parameters to be provided and strictly positive.

Copilot uses AI. Check for mistakes.
Comment on lines +847 to +856
! hard-coded psi
if (hyper_cleaning) then
do j = 0, m
do k = 0, n
q_cons_vf(psi_idx)%sf(j, k, 0) = 1d-2*exp(-(x_cc(j)**2 + y_cc(k)**2)/(2.0*0.05**2))
q_prim_vf(psi_idx)%sf(j, k, 0) = q_cons_vf(psi_idx)%sf(j, k, 0)
end do
end do
end if

Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This hard-coded psi initialization runs for every case with hyper_cleaning = T, overriding whatever the initial condition module produced (including the earlier "psi initialized to zero" logic). This makes psi nonzero by default for unrelated MHD cases. Consider removing this, or gating it behind a specific hard-coded IC/case id/patch parameter so it only applies to the intended validation case(s).

Suggested change
! hard-coded psi
if (hyper_cleaning) then
do j = 0, m
do k = 0, n
q_cons_vf(psi_idx)%sf(j, k, 0) = 1d-2*exp(-(x_cc(j)**2 + y_cc(k)**2)/(2.0*0.05**2))
q_prim_vf(psi_idx)%sf(j, k, 0) = q_cons_vf(psi_idx)%sf(j, k, 0)
end do
end do
end if

Copilot uses AI. Check for mistakes.
Comment on lines +851 to +852
q_cons_vf(psi_idx)%sf(j, k, 0) = 1d-2*exp(-(x_cc(j)**2 + y_cc(k)**2)/(2.0*0.05**2))
q_prim_vf(psi_idx)%sf(j, k, 0) = q_cons_vf(psi_idx)%sf(j, k, 0)
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Gaussian psi initialization uses default-kind literals (1d-2, 2.0, 0.05) and only writes l=0, which is inconsistent with the codebase’s real(wp) usage and will not correctly initialize 3D cases (p>0). Use _wp literals and loop over l=0,p (or assign the full 3D array) if this initialization is kept.

Copilot uses AI. Check for mistakes.
Comment on lines +987 to +991
if (hyper_cleaning) then
$:GPU_PARALLEL_LOOP(private='[j,k,l]', collapse=3)
do l = 0, p
do k = 0, n
do j = 0, m
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The psi damping source term is applied inside the dimensional-splitting loop, so in 2D/3D it will be applied num_dims times per RHS evaluation (over-damping by a factor of 2 or 3). Move this source term outside the directional loop, or split it consistently (e.g., divide by num_dims) if that’s the intended operator splitting.

Copilot uses AI. Check for mistakes.
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

7 issues found across 31 files

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="src/pre_process/m_start_up.fpp">

<violation number="1" location="src/pre_process/m_start_up.fpp:851">
P2: Hard-coded Gaussian psi initialization runs for every hyper_cleaning case after IC setup, overwriting any user/loaded psi values and only setting the z=0 plane. This makes non-Gaussian or 3D initial conditions incorrect. Gate this behind a specific case/parameter or move it to a case-specific IC routine.</violation>
</file>

<file name="src/pre_process/m_global_parameters.fpp">

<violation number="1" location="src/pre_process/m_global_parameters.fpp:892">
P2: `hyper_cleaning` can be enabled when `model_eqns` is not 2/3, leaving `psi_idx` uninitialized while other modules still use it under `hyper_cleaning` guards. Add validation or disable hyper_cleaning for non-MHD configurations to avoid invalid indexing.</violation>
</file>

<file name="tests/F057F8E6/golden-metadata.txt">

<violation number="1" location="tests/F057F8E6/golden-metadata.txt:29">
P2: Redact machine-specific absolute paths in committed golden metadata to avoid leaking developer environment details and to keep snapshots portable.</violation>
</file>

<file name="tests/EC8F87D9/golden-metadata.txt">

<violation number="1" location="tests/EC8F87D9/golden-metadata.txt:29">
P3: Avoid committing user-specific absolute paths in golden metadata; redact or replace with a portable placeholder so the test artifact doesn’t leak local environment details.</violation>
</file>

<file name="src/simulation/m_riemann_solvers.fpp">

<violation number="1" location="src/simulation/m_riemann_solvers.fpp:650">
P1: `c_fast%L` is forced negative via `min(..., -hyper_cleaning_speed)` even though `c_fast` is a positive speed, which can invert the left wave speed and destabilize the solver. Use a positive magnitude expansion instead.</violation>
</file>

<file name="src/simulation/m_global_parameters.fpp">

<violation number="1" location="src/simulation/m_global_parameters.fpp:294">
P2: psi_idx is introduced for hyperbolic cleaning but never added to GPU_DECLARE/GPU_UPDATE lists, even though it is used inside GPU_PARALLEL_LOOP kernels. This can leave psi_idx undefined on the device and cause incorrect indexing or GPU runtime failures when hyper_cleaning is enabled.</violation>
</file>

<file name="tests/B4DC99F9/golden-metadata.txt">

<violation number="1" location="tests/B4DC99F9/golden-metadata.txt:29">
P2: Golden metadata embeds a developer-specific absolute path (username and local directory). This leaks local environment details and makes the test artifact machine-specific. Consider redacting or replacing with a portable placeholder before committing.</violation>
</file>

Since this is your first cubic review, here's how it works:

  • cubic automatically reviews your code and comments on bugs and improvements
  • Teach cubic by replying to its comments. cubic learns from your replies and gets better over time
  • Ask questions if you need clarification on any suggestion

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

end if

if (hyper_cleaning) then ! mhd
c_fast%L = min(c_fast%L, -hyper_cleaning_speed)
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: c_fast%L is forced negative via min(..., -hyper_cleaning_speed) even though c_fast is a positive speed, which can invert the left wave speed and destabilize the solver. Use a positive magnitude expansion instead.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/simulation/m_riemann_solvers.fpp, line 650:

<comment>`c_fast%L` is forced negative via `min(..., -hyper_cleaning_speed)` even though `c_fast` is a positive speed, which can invert the left wave speed and destabilize the solver. Use a positive magnitude expansion instead.</comment>

<file context>
@@ -646,6 +646,11 @@ contains
                             end if
 
+                            if (hyper_cleaning) then ! mhd
+                                c_fast%L = min(c_fast%L, -hyper_cleaning_speed)
+                                c_fast%R = max(c_fast%R, hyper_cleaning_speed)
+                            end if
</file context>
Fix with Cubic

if (hyper_cleaning) then
do j = 0, m
do k = 0, n
q_cons_vf(psi_idx)%sf(j, k, 0) = 1d-2*exp(-(x_cc(j)**2 + y_cc(k)**2)/(2.0*0.05**2))
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Hard-coded Gaussian psi initialization runs for every hyper_cleaning case after IC setup, overwriting any user/loaded psi values and only setting the z=0 plane. This makes non-Gaussian or 3D initial conditions incorrect. Gate this behind a specific case/parameter or move it to a case-specific IC routine.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/pre_process/m_start_up.fpp, line 851:

<comment>Hard-coded Gaussian psi initialization runs for every hyper_cleaning case after IC setup, overwriting any user/loaded psi values and only setting the z=0 plane. This makes non-Gaussian or 3D initial conditions incorrect. Gate this behind a specific case/parameter or move it to a case-specific IC routine.</comment>

<file context>
@@ -842,6 +844,16 @@ contains
+        if (hyper_cleaning) then
+            do j = 0, m
+                do k = 0, n
+                    q_cons_vf(psi_idx)%sf(j, k, 0) = 1d-2*exp(-(x_cc(j)**2 + y_cc(k)**2)/(2.0*0.05**2))
+                    q_prim_vf(psi_idx)%sf(j, k, 0) = q_cons_vf(psi_idx)%sf(j, k, 0)
+                end do
</file context>
Fix with Cubic

sys_size = damage_idx
end if

if (hyper_cleaning) then
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: hyper_cleaning can be enabled when model_eqns is not 2/3, leaving psi_idx uninitialized while other modules still use it under hyper_cleaning guards. Add validation or disable hyper_cleaning for non-MHD configurations to avoid invalid indexing.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/pre_process/m_global_parameters.fpp, line 892:

<comment>`hyper_cleaning` can be enabled when `model_eqns` is not 2/3, leaving `psi_idx` uninitialized while other modules still use it under `hyper_cleaning` guards. Add validation or disable hyper_cleaning for non-MHD configurations to avoid invalid indexing.</comment>

<file context>
@@ -886,6 +889,11 @@ contains
                 sys_size = damage_idx
             end if
 
+            if (hyper_cleaning) then
+                psi_idx = sys_size + 1
+                sys_size = psi_idx
</file context>
Fix with Cubic

OpenACC : OFF
OpenMP : OFF

Fypp : /home/chris/source/MFC_mhd_hypercleaning/build/venv/bin/fypp
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Redact machine-specific absolute paths in committed golden metadata to avoid leaking developer environment details and to keep snapshots portable.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tests/F057F8E6/golden-metadata.txt, line 29:

<comment>Redact machine-specific absolute paths in committed golden metadata to avoid leaking developer environment details and to keep snapshots portable.</comment>

<file context>
@@ -0,0 +1,185 @@
+        OpenACC : OFF
+        OpenMP  : OFF
+    
+        Fypp    : /home/chris/source/MFC_mhd_hypercleaning/build/venv/bin/fypp
+        Doxygen : 
+    
</file context>
Fix with Cubic

type(int_bounds_info) :: species_idx !< Indexes of first & last concentration eqns.
integer :: c_idx !< Index of color function
integer :: damage_idx !< Index of damage state variable (D) for continuum damage model
integer :: psi_idx !< Index of hyperbolic cleaning state variable for MHD
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: psi_idx is introduced for hyperbolic cleaning but never added to GPU_DECLARE/GPU_UPDATE lists, even though it is used inside GPU_PARALLEL_LOOP kernels. This can leave psi_idx undefined on the device and cause incorrect indexing or GPU runtime failures when hyper_cleaning is enabled.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/simulation/m_global_parameters.fpp, line 294:

<comment>psi_idx is introduced for hyperbolic cleaning but never added to GPU_DECLARE/GPU_UPDATE lists, even though it is used inside GPU_PARALLEL_LOOP kernels. This can leave psi_idx undefined on the device and cause incorrect indexing or GPU runtime failures when hyper_cleaning is enabled.</comment>

<file context>
@@ -290,6 +291,7 @@ module m_global_parameters
     type(int_bounds_info) :: species_idx               !< Indexes of first & last concentration eqns.
     integer :: c_idx                                   !< Index of color function
     integer :: damage_idx                              !< Index of damage state variable (D) for continuum damage model
+    integer :: psi_idx                                 !< Index of hyperbolic cleaning state variable for MHD
     !> @}
     $:GPU_DECLARE(create='[sys_size,E_idx,n_idx,bub_idx,alf_idx,gamma_idx]')
</file context>
Fix with Cubic

OpenACC : OFF
OpenMP : OFF

Fypp : /home/chris/source/MFC_mhd_hypercleaning/build/venv/bin/fypp
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Golden metadata embeds a developer-specific absolute path (username and local directory). This leaks local environment details and makes the test artifact machine-specific. Consider redacting or replacing with a portable placeholder before committing.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tests/B4DC99F9/golden-metadata.txt, line 29:

<comment>Golden metadata embeds a developer-specific absolute path (username and local directory). This leaks local environment details and makes the test artifact machine-specific. Consider redacting or replacing with a portable placeholder before committing.</comment>

<file context>
@@ -0,0 +1,185 @@
+        OpenACC : OFF
+        OpenMP  : OFF
+    
+        Fypp    : /home/chris/source/MFC_mhd_hypercleaning/build/venv/bin/fypp
+        Doxygen : 
+    
</file context>
Fix with Cubic

OpenACC : OFF
OpenMP : OFF

Fypp : /home/dan/Documents/repos/MFC/build/venv/bin/fypp
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: Avoid committing user-specific absolute paths in golden metadata; redact or replace with a portable placeholder so the test artifact doesn’t leak local environment details.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tests/EC8F87D9/golden-metadata.txt, line 29:

<comment>Avoid committing user-specific absolute paths in golden metadata; redact or replace with a portable placeholder so the test artifact doesn’t leak local environment details.</comment>

<file context>
@@ -0,0 +1,193 @@
+        OpenACC : OFF
+        OpenMP  : OFF
+    
+        Fypp    : /home/dan/Documents/repos/MFC/build/venv/bin/fypp
+        Doxygen : 
+    
</file context>
Fix with Cubic

sbryngelson
sbryngelson previously approved these changes Jan 29, 2026
@codeant-ai
Copy link
Contributor

codeant-ai bot commented Feb 2, 2026

CodeAnt AI is reviewing your PR.


Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@codeant-ai codeant-ai bot added the size:XL This PR changes 500-999 lines, ignoring generated files label Feb 2, 2026
@codeant-ai
Copy link
Contributor

codeant-ai bot commented Feb 2, 2026

Nitpicks 🔍

🔒 No security issues identified
⚡ Recommended areas for review

  • Wrong variable used
    The new hyper-cleaning RHS subtracts psi using q_prim_vf(psi_idx)%sf(...), but earlier code associates and populates q_prim_qp%vf(psi_idx)%sf (and the Riemann/flux kernels use q_prim_qp). This likely reads the wrong array (and may be stale/uninitialized at this point), producing incorrect source values. Verify whether the intent is to use the QP primitive (q_prim_qp%vf) or the function argument q_prim_vf, and make it consistent.

  • GPU data movement mismatch
    Initialization attached and copy-in GPU directives for q_prim_qp%vf(psi_idx)%sf were added, but the hyper-cleaning loop uses q_prim_vf (a different array). This is an inconsistent GPU data/host usage pattern and can lead to wrong device memory being accessed or missing data on device. Ensure the variable used in the GPU loop is the one that was attached/copied, or add appropriate GPU data directives for the used array.

  • Division by zero
    The Gaussian divergence pulse (case 260) computes B using erf((x-0.5)/sigma) but sigma comes from input (patch_icpp(patch_id)%a(3)) and is not checked. If sigma is zero or extremely small this yields an invalid/inf argument and produces NaNs. The code should validate or guard against zero/near-zero sigma before dividing.

  • Hard-coded psi initialization
    A hard-coded initial condition for psi (via q_cons_vf(psi_idx) and q_prim_vf(psi_idx)) was added. Check for:

    • correct use of numeric kinds (mixing 1d-2 with default reals may cause precision/kind inconsistency),
    • correct indexing for 3D (assignment only uses third index 0 — will miss other z-slices when p>0),
    • validity of psi_idx (must be in bounds of q_cons_vf/q_prim_vf),
    • whether this initialization should be gated by old_ic or other input flags.
  • Possible Index OOB
    The new allocation/deallocation is gated on hyper_cleaning and addresses q_prim_vf(psi_idx)%sf. Ensure psi_idx is a valid index within the allocated range of q_prim_vf (which is allocated as 1:sys_size). If psi_idx can be outside 1:sys_size (or outside other index ranges used earlier such as adv_idx%end), this will lead to out-of-bounds access at allocation or later accesses. Confirm psi_idx is defined and bounded when hyper_cleaning is true.

Comment on lines +207 to +219
if (r_sq <= 0.1**2) then
! -- Inside the rotor --
! Set density uniformly to 10
q_prim_vf(contxb)%sf(i, j, 0) = 10._wp

! Set vup constant rotation of rate v=2
! v_x = -omega * (y - y_c)
! v_y = omega * (x - x_c)
q_prim_vf(momxb)%sf(i, j, 0) = -20._wp*(y_cc(j) - 0.5_wp)
q_prim_vf(momxb + 1)%sf(i, j, 0) = 20._wp*(x_cc(i) - 0.5_wp)

! taper width of 0.015
else if (r_sq <= 0.115**2) then
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: In the rotor initial condition, the comparison radii use default-kind real literals (0.1**2, 0.115**2) while r_sq is real(wp), causing mixed‑kind arithmetic and slight precision inconsistencies that can misclassify cells very close to the intended radius thresholds. Use _wp suffixed literals to keep everything in the same kind. [type error]

Severity Level: Major ⚠️
- ⚠️ Rotor initial condition (hcid=252) borderline cell classification.
- ⚠️ Small differences in initial rotor velocity/density assignments.
Suggested change
if (r_sq <= 0.1**2) then
! -- Inside the rotor --
! Set density uniformly to 10
q_prim_vf(contxb)%sf(i, j, 0) = 10._wp
! Set vup constant rotation of rate v=2
! v_x = -omega * (y - y_c)
! v_y = omega * (x - x_c)
q_prim_vf(momxb)%sf(i, j, 0) = -20._wp*(y_cc(j) - 0.5_wp)
q_prim_vf(momxb + 1)%sf(i, j, 0) = 20._wp*(x_cc(i) - 0.5_wp)
! taper width of 0.015
else if (r_sq <= 0.115**2) then
if (r_sq <= 0.1_wp**2) then
! -- Inside the rotor --
! Set density uniformly to 10
q_prim_vf(contxb)%sf(i, j, 0) = 10._wp
! Set vup constant rotation of rate v=2
! v_x = -omega * (y - y_c)
! v_y = omega * (x - x_c)
q_prim_vf(momxb)%sf(i, j, 0) = -20._wp*(y_cc(j) - 0.5_wp)
q_prim_vf(momxb + 1)%sf(i, j, 0) = 20._wp*(x_cc(i) - 0.5_wp)
! taper width of 0.015
else if (r_sq <= 0.115_wp**2) then
Steps of Reproduction ✅
1. Configure a patch with hardcoded initial condition id hcid=252 (2D MHD Rotor) in the
input (Hardcoded2D is defined in src/common/include/2dHardcodedIC.fpp).

2. Run the solver to reach initialization where the Hardcoded2D macro executes; the rotor
branch starts at src/common/include/2dHardcodedIC.fpp lines around 192..227 and computes
r_sq at line 204: "r_sq = (x_cc(i) - 0.5_wp)**2 + (y_cc(j) - 0.5_wp)**2".

3. Initialization evaluates the radius tests at lines 207 ("if (r_sq <= 0.1**2) then") and
219 ("else if (r_sq <= 0.115**2) then") to classify inside/transition/outside cells.

4. Because the literals 0.1**2 and 0.115**2 are default‑kind reals while r_sq and other
variables use kind _wp, cells with sqrt(r_sq) numerically extremely close to 0.1 or 0.115
can be classified differently than when using _wp‑suffixed literals; this reproduces the
misclassification at initialization for boundary grid cells. (This is a marginal numerical
precision issue localized to hcid=252; the code path and lines above show the exact
location.)
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** src/common/include/2dHardcodedIC.fpp
**Line:** 207:219
**Comment:**
	*Type Error: In the rotor initial condition, the comparison radii use default-kind real literals (`0.1**2`, `0.115**2`) while `r_sq` is `real(wp)`, causing mixed‑kind arithmetic and slight precision inconsistencies that can misclassify cells very close to the intended radius thresholds. Use `_wp` suffixed literals to keep everything in the same kind.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.


eps_mhd = patch_icpp(patch_id)%a(2)
sigma = patch_icpp(patch_id)%a(3)
C_mhd = eps_mhd*sigma*sqrt(pi)*0.5_wp
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: In the Gaussian divergence pulse case, sigma is read directly from input and then used as a divisor without validation; if the user forgets to set it or sets it to a non-positive value, this will cause a division by zero or mathematically invalid configuration at runtime. [possible bug]

Severity Level: Major ⚠️
- ❌ Initialization for Gaussian divergence pulse (hcid=260) fails.
- ⚠️ Produces NaN/INF B‑field if sigma is zero or negative.
Suggested change
C_mhd = eps_mhd*sigma*sqrt(pi)*0.5_wp
if (sigma <= 0._wp) then
call s_mpi_abort("Gaussian Divergence Pulse (hcid=260): sigma must be positive.")
end if
Steps of Reproduction ✅
1. Configure a patch to use hardcoded initial condition hcid=260 (Gaussian Divergence
Pulse) in the input so the case (260) branch in src/common/include/2dHardcodedIC.fpp is
executed.

2. Provide patch parameters with a(3)=0 or omit setting a(3) so patch_icpp(patch_id)%a(3)
resolves to zero; the code assigns sigma at src/common/include/2dHardcodedIC.fpp line 249:
"sigma = patch_icpp(patch_id)%a(3)".

3. Initialization computes the B‑field using erf((x_cc(i) - 0.5_wp)/sigma) at line 254.
With sigma == 0 the division produces an invalid argument (INF/NaN) or a runtime fault
depending on compiler/FP settings, observable as NaNs in B arrays or an initialization
failure.

4. Observe the failure by running the solver to initialization and inspecting the
initialized q_prim_vf(B_idx) array or checking for abnormal program termination. Adding
the explicit check (sigma <= 0) at line 251 and aborting with a clear message prevents the
invalid initialization and makes the failure mode explicit.
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** src/common/include/2dHardcodedIC.fpp
**Line:** 251:251
**Comment:**
	*Possible Bug: In the Gaussian divergence pulse case, `sigma` is read directly from input and then used as a divisor without validation; if the user forgets to set it or sets it to a non-positive value, this will cause a division by zero or mathematically invalid configuration at runtime.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.

if (cont_damage) dbvars = dbvars + 1

! Hyperbolic cleaning for MHD
if (hyper_cleaning) dbvars = dbvars + 1
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The hyperbolic cleaning scalar is counted as an output variable whenever hyper_cleaning is true, even if MHD is disabled; this can make the declared number of binary output variables (dbvars) larger than the number actually written, corrupting the binary file layout for non‑MHD runs where hyper_cleaning might be (mis)enabled. [logic error]

Severity Level: Major ⚠️
- ❌ Binary formatted database header mismatches written data.
- ❌ Binary post-processing output files become unreadable.
- ⚠️ Visualization tools reading Binary may mis-interpret data.
- ⚠️ Regression tests comparing Binary dumps may fail.
Suggested change
if (hyper_cleaning) dbvars = dbvars + 1
if (mhd .and. hyper_cleaning) dbvars = dbvars + 1
Steps of Reproduction ✅
1. Configure a run that requests Binary formatted post-processing output (format == 2) and
enable hyperbolic cleaning while MHD is disabled. The dbvars counter is computed inside
s_initialize_data_output_module in src/post_process/m_data_output.fpp; the hyper_cleaning
increment is at the lines shown above (lines 366-367).

2. Start the application so that s_initialize_data_output_module runs and sets dbvars (the
code path that computes dbvars includes the two lines at 366-367). Because hyper_cleaning
is true, dbvars is incremented unconditionally at 367 even though mhd is false.

3. The code path that writes the Binary file header in s_open_formatted_database_file
(src/post_process/m_data_output.fpp) writes the declared number of variables into the file
header: write (dbfile) m, n, p, dbvars. This write uses the dbvars value computed above.

4. Later the routines that actually write per-variable binary blocks
(s_write_variable_to_formatted_database_file and related code in the same file) skip
MHD-related variables when mhd is false, so fewer variable blocks are emitted than the
header advertised. The result is a mismatch between header (contains extra hyper_cleaning
variable) and actual data blocks; binary readers or downstream post-processing will
mis-interpret file layout, causing corrupted reads or failures.
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** src/post_process/m_data_output.fpp
**Line:** 367:367
**Comment:**
	*Logic Error: The hyperbolic cleaning scalar is counted as an output variable whenever `hyper_cleaning` is true, even if MHD is disabled; this can make the declared number of binary output variables (`dbvars`) larger than the number actually written, corrupting the binary file layout for non‑MHD runs where `hyper_cleaning` might be (mis)enabled.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.

damage_idx = dflt_int
end if

if (hyper_cleaning) then
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The hyperbolic cleaning variable is appended to the system state vector whenever hyper_cleaning is true, regardless of whether MHD is enabled or the problem is multidimensional, which can create a state layout that does not match the assumptions of the MHD hyperbolic cleaning solver (implemented only for 2D/3D MHD), leading to inconsistent indexing and potential downstream runtime errors. [logic error]

Severity Level: Major ⚠️
- ❌ State-vector layout corruption during accidental hyper_cleaning usage.
- ⚠️ MPI/IO allocation and indexing (MPI_IO_DATA) affected.
Suggested change
if (hyper_cleaning) then
if (hyper_cleaning .and. mhd .and. n > 0) then
Steps of Reproduction ✅
1. Configure a run where model_eqns selects the branch that executes the state-vector
annotation code in impure subroutine s_initialize_global_parameters_module (the large
routine that sets up cont_idx, mom_idx, B_idx, etc.). The psi_idx insertion block is at
lines 818..823 in that routine and will be executed when control reaches the sys_size
annotation logic inside the model_eqns handling.

2. Enable hyper_cleaning in the case input (so the module variable hyper_cleaning becomes
true by the time s_initialize_global_parameters_module runs). The module does not enforce
that hyper_cleaning implies mhd or multidimensional geometry anywhere in this file
(hyper_cleaning is an independent logical declared at line ~120).

3. With hyper_cleaning = .true. and mhd = .false. (or with a 1D run where n == 0), the
existing code at lines 818..823 will append psi_idx to sys_size even though the MHD state
(B_idx) or multidimensionality may not be present — changing the global sys_size and
therefore the expected state-vector layout.

4. The mismatch becomes observable when downstream code that assembles or solves the
system (or any IO/serialization expecting a specific ordering) assumes no psi state unless
MHD/multidimensionality is active. In practice this misconfiguration is realistic (user
sets hyper_cleaning without toggling mhd), so the current guardless insertion can produce
incorrect indexing. Guarding the insertion with ".and. mhd .and. n > 0" prevents adding
the psi field when MHD or multidimensional geometry is not active.
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** src/post_process/m_global_parameters.fpp
**Line:** 818:818
**Comment:**
	*Logic Error: The hyperbolic cleaning variable is appended to the system state vector whenever `hyper_cleaning` is true, regardless of whether MHD is enabled or the problem is multidimensional, which can create a state layout that does not match the assumptions of the MHD hyperbolic cleaning solver (implemented only for 2D/3D MHD), leading to inconsistent indexing and potential downstream runtime errors.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.

varname(:) = ' '
end if

if (hyper_cleaning) then
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The psi output is guarded only by the hyper_cleaning flag, but not by the mhd flag; if a user accidentally enables hyper_cleaning in a non-MHD post-processing run (where no psi variable exists in q_cons_vf), this block will attempt to access q_cons_vf(psi_idx) and can cause out-of-bounds or undefined memory access. Guarding the write with mhd as well ensures psi is only accessed when the MHD variables, including psi, are present. [logic error]

Severity Level: Major ⚠️
- ❌ Formatted database psi output may crash post-processor.
- ⚠️ Writes may produce invalid file corruption.
- ⚠️ Affects post-processing of non-MHD cases only.
- ⚠️ Suggestion prevents misconfiguration crashes.
Suggested change
if (hyper_cleaning) then
if (hyper_cleaning .and. mhd) then
Steps of Reproduction ✅
1. Enable hyperbolic cleaning in the post-process namelist: edit post_process.inp to set
hyper_cleaning = .true. while leaving mhd = .false. (the namelist contains hyper_cleaning
in src/post_process/m_start_up.fpp around the namelist declaration shown at lines 112-115
of the PR hunk).

2. Run the post-processor for a non-MHD case (a case whose conserved/primitive arrays do
not include MHD fields). The namelist is read by subroutine s_read_input_file in
src/post_process/m_start_up.fpp (the file includes the user_inputs namelist that declares
hyper_cleaning at the lines shown in the diff).

3. When data is written, subroutine s_save_data in src/post_process/m_start_up.fpp
executes the block that writes psi: the code at lines 603-609 (if (hyper_cleaning) then
... q_cons_vf(psi_idx)%sf(...) ... end if) is reached and attempts to index
q_cons_vf(psi_idx) even though MHD variables were not present.

4. Expectation: indexing q_cons_vf(psi_idx) in s_save_data
(src/post_process/m_start_up.fpp: lines 603-605) will access an invalid or out-of-range
descriptor (psi_idx not defined/populated for non-MHD runs), causing an out-of-bounds
access or crash / invalid output. This reproduces deterministically by running the
post-processor with hyper_cleaning true and mhd false.
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** src/post_process/m_start_up.fpp
**Line:** 603:603
**Comment:**
	*Logic Error: The psi output is guarded only by the `hyper_cleaning` flag, but not by the `mhd` flag; if a user accidentally enables `hyper_cleaning` in a non-MHD post-processing run (where no psi variable exists in `q_cons_vf`), this block will attempt to access `q_cons_vf(psi_idx)` and can cause out-of-bounds or undefined memory access. Guarding the write with `mhd` as well ensures psi is only accessed when the MHD variables, including psi, are present.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.

end if

if (hyper_cleaning) then
psi_idx = sys_size + 1
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The hyperbolic cleaning index is added whenever the flag is set during model_eqns=2 or 3, even though MHD support (magnetic field indices) is only configured for model_eqns=2 with mhd=.true.; this allows an inconsistent state where a cleaning variable exists without corresponding MHD variables, which can cause downstream logic that assumes both to be present to malfunction or access uninitialized indices. [logic error]

Severity Level: Major ⚠️
- ❌ State-vector layout inconsistent for MHD-related simulations.
- ❌ MPI_IO_DATA allocation sizes become incorrect.
- ⚠️ Post-process output indices shift unexpectedly.
- ⚠️ Initialization fails for misconfigured hyper_cleaning cases.
Suggested change
psi_idx = sys_size + 1
if (.not. mhd .or. model_eqns /= 2) then
stop 'hyper_cleaning is only supported for model_eqns=2 with MHD enabled'
end if
Steps of Reproduction ✅
1. Prepare a case that enables hyperbolic cleaning but not MHD: set hyper_cleaning =
.true. in the case configuration that is included by the build (case file included via the
module header 'src/pre_process/m_global_parameters.fpp' -> see include 'case.fpp'). The
default value is set in s_assign_default_values_to_user_inputs at
src/pre_process/m_global_parameters.fpp:379 where hyper_cleaning is initialized (line
shown in PR hunk).

2. Start the initialization routine that builds the state-vector layout: the program calls
m_global_parameters::s_initialize_global_parameters_module. During the model_eqns handling
the code reaches the new block shown at src/pre_process/m_global_parameters.fpp:894-897
(the exact lines added in the PR) which unconditionally assigns psi_idx when
hyper_cleaning is true.

3. Observe that psi_idx has been appended to sys_size even when the magnetic-field indices
(B_idx) were not created because MHD was not enabled. The magnetic-field indices are only
created under the guarded mhd branch inside the same s_initialize_global_parameters_module
(the code creates B_idx only when the logical mhd is true inside the model_eqns==2
branch), while psi_idx is created unconditionally inside the model_eqns==2/3 handling (see
psi_idx declaration at src/pre_process/m_global_parameters.fpp:125 and hyper_cleaning
declaration at src/pre_process/m_global_parameters.fpp:103).

4. Downstream code that assumes a consistent MHD layout (for example routines that iterate
magnetic field components or expect psi colocated with B fields) will see sys_size and
index offsets changed by psi_idx while B_idx is absent, producing mismatches in state
indexing, unexpected accesses, or incorrect MPI_IO allocation sizes (sys_size used to size
MPI_IO_DATA). This reproduces the inconsistent-system-layout problem caused by adding
psi_idx without verifying mhd and model_eqns.
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** src/pre_process/m_global_parameters.fpp
**Line:** 895:895
**Comment:**
	*Logic Error: The hyperbolic cleaning index is added whenever the flag is set during model_eqns=2 or 3, even though MHD support (magnetic field indices) is only configured for model_eqns=2 with mhd=.true.; this allows an inconsistent state where a cleaning variable exists without corresponding MHD variables, which can cause downstream logic that assumes both to be present to malfunction or access uninitialized indices.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.

call s_generate_initial_condition()

! hard-coded psi
if (hyper_cleaning) then
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The hyper-cleaning psi initialization always overwrites the psi field, even when old_ic is true and an existing initial condition has been read from disk, which breaks restartability by discarding the saved psi state whenever hyper-cleaning is enabled. [logic error]

Severity Level: Critical 🚨
- ❌ Restart psi state discarded on hyper_cleaning-enabled restarts.
- ❌ Results reproducibility compromised after restart.
- ⚠️ Validation testcases using saved psi affected.
Suggested change
if (hyper_cleaning) then
if (hyper_cleaning .and. .not. old_ic) then
Steps of Reproduction ✅
1. Prepare a restart case: create a pre-process folder with old_ic = .true. in
pre_process.inp (old_ic is a namelist variable read by s_read_input_file; see
src/pre_process/m_start_up.fpp namelist around lines ~150–156).

2. Ensure the restart data contains a saved psi field (the code reads existing IC files
when old_ic .true. via the call "if (old_ic) call s_read_ic_data_files(q_cons_vf,
ib_markers)" that appears immediately before the grid/initialization steps in
s_apply_initial_condition in src/pre_process/m_start_up.fpp (the call is just above the
call s_generate_initial_condition at ~line 844–845)).

3. Run the same pre-process startup so s_apply_initial_condition executes:
s_read_ic_data_files will load the saved psi into q_cons_vf(psi_idx). Immediately after,
the hard-coded psi block at lines 847–855 executes unconditionally when hyper_cleaning is
true and overwrites psi with the Gaussian.

4. Observe that the saved psi state from the restart is discarded even though it was
explicitly loaded from disk at the start of s_apply_initial_condition. The concrete code
locations are: read existing IC (src/pre_process/m_start_up.fpp: ~844), then hard-coded
overwrite (src/pre_process/m_start_up.fpp: 847–855). This demonstrates lost restart state
in real executions.
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** src/pre_process/m_start_up.fpp
**Line:** 848:848
**Comment:**
	*Logic Error: The hyper-cleaning psi initialization always overwrites the psi field, even when `old_ic` is true and an existing initial condition has been read from disk, which breaks restartability by discarding the saved psi state whenever hyper-cleaning is enabled.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.

do k = 0, n
do j = 0, m
rhs_vf(psi_idx)%sf(j, k, l) = rhs_vf(psi_idx)%sf(j, k, l) - &
q_prim_vf(psi_idx)%sf(j, k, l)/hyper_cleaning_tau
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The hyperbolic cleaning source term uses the subroutine argument array of primitive variables, which is not updated during RHS assembly, instead of the freshly converted primitive field used everywhere else, so psi is damped using stale or inconsistent values. [logic error]

Severity Level: Critical 🚨
- ❌ Psi damping uses stale primitive values.
- ⚠️ s_compute_rhs physics inconsistent for hyper_cleaning.
- ⚠️ Debugging/tracing of psi behaviour becomes misleading.
Suggested change
q_prim_vf(psi_idx)%sf(j, k, l)/hyper_cleaning_tau
q_prim_qp%vf(psi_idx)%sf(j, k, l)/hyper_cleaning_tau
Steps of Reproduction ✅
1. Inspect src/simulation/m_rhs.fpp: s_compute_rhs has two different primitive-variable
stores — the subroutine argument q_prim_vf (declared on the subroutine signature) and the
working field q_prim_qp%vf which is populated by the call to
s_convert_conservative_to_primitive_variables earlier in s_compute_rhs.

2. In a normal RHS assembly (see the conversion call "call
s_convert_conservative_to_primitive_variables" earlier in s_compute_rhs), q_prim_qp%vf is
updated from the conservative state and used for all subsequent flux/source calculations.

3. With hyper_cleaning enabled the existing code at lines 993–994 uses q_prim_vf(...) (the
incoming subroutine array) rather than q_prim_qp%vf(...). Unless the caller has already
synchronized q_prim_vf with q_prim_qp (the file only copies q_prim_qp -> q_prim_vf
conditionally later under run_time_info/probe_wrt/ib/bubbles_lagrange), q_prim_vf will be
stale or inconsistent.

4. Reproduce by running a hyper_cleaning-enabled MHD timestep where q_prim_vf is not
forced to match q_prim_qp (typical default): check computed damping term at lines 993–994
and compare its values to q_prim_qp%vf(psi_idx)%sf — they differ, demonstrating psi is
being damped with old values.
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** src/simulation/m_rhs.fpp
**Line:** 994:994
**Comment:**
	*Logic Error: The hyperbolic cleaning source term uses the subroutine argument array of primitive variables, which is not updated during RHS assembly, instead of the freshly converted primitive field used everywhere else, so psi is damped using stale or inconsistent values.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.

cfl_adap_dt, cfl_const_dt, cfl_target, &
surface_tension, bubbles_lagrange, lag_params, &
hyperelasticity, R0ref, num_bc_patches, Bx0, powell, &
hyperelasticity, R0ref, num_bc_patches, Bx0, &
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The variable R0ref is listed twice in the same user_inputs NAMELIST group, which is non‑standard and can lead to compiler errors or non‑portable behavior; it should only appear once in the NAMELIST definition. [possible bug]

Severity Level: Major ⚠️
- ❌ Bubble model initialisation reads ambiguous R0ref.
- ⚠️ GPU update lists include duplicated R0ref token.
- ⚠️ Namelist read may emit runtime warnings/errors.
Suggested change
hyperelasticity, R0ref, num_bc_patches, Bx0, &
hyperelasticity, num_bc_patches, Bx0, &
Steps of Reproduction ✅
1. Inspect the NAMELIST /user_inputs/ in src/simulation/m_start_up.fpp and observe
duplicate R0ref: the later occurrence is at lines 183-187 in the NAMELIST (see lines shown
in file at src/simulation/m_start_up.fpp:183-187 where "hyperelasticity, R0ref,
num_bc_patches, Bx0, &" appears).

2. Start the program so proc_rank == 0 executes initialization (entry point
s_initialize_mpi_domain calls s_read_input_file). s_initialize_mpi_domain triggers
s_read_input_file which performs the namelist read. In s_read_input_file the code performs
open(1, FILE=trim(file_path), ...) followed by read(1, NML=user_inputs, iostat=iostatus)
(this read consumes the /user_inputs/ namelist defined in the same file).

3. Provide a simulation.inp that sets a value for R0ref in the namelist. When the read(1,
NML=user_inputs, iostat=iostatus) in s_read_input_file processes the file it will
encounter a namelist with two entries named R0ref (one earlier in the same /user_inputs/
definition and the later one at lines 183-187). Depending on the compiler/runtime, the
duplicate name can cause a warning, nondeterministic assignment, or tooling/parsing
failure when the namelist is processed.

4. Observe outcome: either a namelist read warning/error from the Fortran runtime or the
value assigned to R0ref is ambiguous (first or last occurrence used). If the code later
uses R0ref (e.g., bubble / R0ref usage in GPU update lists and bubble initialization
elsewhere in m_start_up), behavior will differ from expectation. If this suggestion is
considered frivolous: note that having R0ref twice is unlikely intentional — this is a
duplicated declaration in the same NAMELIST and not an established design pattern in the
codebase.
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** src/simulation/m_start_up.fpp
**Line:** 184:184
**Comment:**
	*Possible Bug: The variable `R0ref` is listed twice in the same `user_inputs` NAMELIST group, which is non‑standard and can lead to compiler errors or non‑portable behavior; it should only appear once in the NAMELIST definition.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.

@codeant-ai
Copy link
Contributor

codeant-ai bot commented Feb 2, 2026

CodeAnt AI finished reviewing your PR.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/common/m_variables_conversion.fpp (1)

1173-1178: ⚠️ Potential issue | 🟡 Minor

Fix typos in parameter docs to unblock spell check.
The typos check flags iz; align the names with the actual arguments (is1/is2/is3).

✏️ Doc-only fix
-        !!  `@param` ix Index bounds in the first coordinate direction
-        !!  `@param` iy Index bounds in the second coordinate direction
-        !!  `@param` iz Index bounds in the third coordinate direction
+        !!  `@param` is1 Index bounds in the first coordinate direction
+        !!  `@param` is2 Index bounds in the second coordinate direction
+        !!  `@param` is3 Index bounds in the third coordinate direction

@danieljvickers
Copy link
Member

@sbryngelson Can I get approval for benchmark?

@sbryngelson sbryngelson merged commit a6d3f37 into MFlowCode:master Feb 3, 2026
42 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Review effort 4/5 size:XL This PR changes 500-999 lines, ignoring generated files

Development

Successfully merging this pull request may close these issues.

3 participants