Skip to content

Conversation

@ChrisZYJ
Copy link
Contributor

@ChrisZYJ ChrisZYJ commented Dec 13, 2025

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

  • Implements hyperbolic divergence cleaning for MHD to replace Powell's method

  • Adds new state variable psi_idx for hyperbolic cleaning throughout codebase

  • Introduces three new MHD test cases: Gaussian divergence pulse, tilted shock tube, 2D rotor

  • Adds Orszag-Tang vortex validation cases with multiple WENO schemes

  • Includes hard-coded initial condition for psi with Gaussian profile

  • Modifies Riemann solver to include hyperbolic cleaning flux contributions


Diagram Walkthrough

flowchart LR
  A["MHD System"] --> B["Add psi State Variable"]
  B --> C["Initialize psi to Zero"]
  C --> D["Hard-code Gaussian IC"]
  A --> E["Riemann Solver"]
  E --> F["Add Hyperbolic Cleaning Flux"]
  F --> G["Update B-field Flux"]
  G --> H["Exponential Decay of psi"]
  A --> I["Global Parameters"]
  I --> J["hyper_cleaning Flag"]
  J --> K["hyper_cleaning_speed & tau"]
  K --> H
  H --> L["Output psi Variable"]
Loading

File Walkthrough

Relevant files
Enhancement
19 files
2dHardcodedIC.fpp
Add new MHD test cases with hardcoded ICs                               
+98/-2   
m_variables_conversion.fpp
Handle psi variable in primitive-conservative conversion 
+3/-0     
m_data_output.fpp
Include psi in database output variable count                       
+3/-0     
m_global_parameters.fpp
Add hyper_cleaning flag and psi_idx parameter                       
+10/-0   
m_mpi_proxy.fpp
Broadcast hyper_cleaning flag across MPI processes             
+1/-1     
m_start_up.fpp
Write psi variable to formatted database output                   
+9/-1     
m_global_parameters.fpp
Add hyper_cleaning flag and psi_idx to pre-processor         
+8/-0     
m_initial_condition.fpp
Initialize psi to zero in initial condition setup               
+7/-0     
m_mpi_proxy.fpp
Broadcast hyper_cleaning flag in pre-processor MPI             
+1/-1     
m_start_up.fpp
Hard-code Gaussian psi initialization and read hyper_cleaning
+14/-2   
m_data_output.fpp
Add psi to simulation data output (commented TODO)             
+5/-0     
m_global_parameters.fpp
Add hyper_cleaning parameters and GPU declarations             
+20/-1   
m_mhd.fpp
Rename Powell module to generic cleaning module                   
+6/-6     
m_mpi_proxy.fpp
Broadcast hyper_cleaning parameters across MPI                     
+4/-2     
m_riemann_solvers.fpp
Add hyperbolic cleaning flux and wave speed modifications
+33/-2   
m_start_up.fpp
Initialize hyperbolic cleaning module and read parameters
+3/-2     
m_time_steppers.fpp
Allocate psi and apply exponential decay time stepping     
+23/-0   
case.py
Update magnetic vortex case to use new case ID                     
+1/-1     
case_dicts.py
Add hyper_cleaning parameters to case dictionary                 
+3/-0     
Tests
8 files
case.py
Orszag-Tang vortex test with WENO3 mapped scheme                 
+80/-0   
case.py
Orszag-Tang vortex test with WENO5 mapped scheme                 
+80/-0   
case.py
Orszag-Tang vortex without hyperbolic cleaning                     
+80/-0   
case.py
Orszag-Tang vortex test with WENO5Z scheme                             
+80/-0   
case.py
Orszag-Tang vortex test with WENO7 mapped scheme                 
+80/-0   
case.py
2D MHD rotor problem test case configuration                         
+84/-0   
case.py
Gaussian divergence pulse test case setup                               
+76/-0   
case.py
Tilted MHD shock tube test case configuration                       
+80/-0   
Additional files
1 files
m_rhs.fpp +7/-0     

Summary by CodeRabbit

  • New Features

    • Added new MHD test cases: 2D rotor, Gaussian divergence pulse, tilted shock-tube, and Blob initial conditions.
    • Introduced Orszag-Tang vortex test cases with WENO order variations (WENO3, WENO5, WENO7).
    • Added hyperbolic cleaning for MHD with configurable speed and tau parameters to enforce divergence-free magnetic fields.
  • Updates

    • Refined MHD module naming and parameter structure for improved clarity.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 13, 2025

📝 Walkthrough

Walkthrough

This PR introduces hyperbolic cleaning for MHD simulations with a new psi state variable to control magnetic divergence. It adds eight new test case configurations for 2D Orszag-Tang and rotor simulations, renames Powell MHD modules to Clean, and integrates the feature across preprocessing, simulation, and post-processing modules via parameter additions and state variable handling.

Changes

Cohort / File(s) Summary
New Test Case Configurations
case_new/2D_orszag_tang_weno3m/case.py, case_new/2D_orszag_tang_weno5m/case.py, case_new/2D_orszag_tang_weno5m_nohyper/case.py, case_new/2D_orszag_tang_weno5z/case.py, case_new/2D_orszag_tang_weno7m/case.py, case_new/2D_rotor_weno5m/case.py, case_new/gauss_div_pulse_2/case.py, case_new/slanted_shock_tube/case.py
Eight standalone case configuration scripts generating JSON parameters for 2D MHD test cases with varying WENO orders; one existing test case updated (magnetic vortex hcid parameter).
Hardcoded Initial Condition Expansions
src/common/include/2dHardcodedIC.fpp
Expands rotor-based MHD initialization (case 252); adds three new IC cases: 260 (Gaussian Divergence Pulse), 261 (Blob), 262 (Tilted 2D shock-tube); introduces rotor-specific variables and computation logic.
Hyperbolic Cleaning State Variables — Core Parameters
src/simulation/m_global_parameters.fpp, src/pre_process/m_global_parameters.fpp, src/post_process/m_global_parameters.fpp
Adds hyper_cleaning (logical), psi_idx (integer index), hyper_cleaning_speed (real), hyper_cleaning_tau (real) with system size adjustments and GPU device mapping.
Hyperbolic Cleaning MPI Broadcasting
src/simulation/m_mpi_proxy.fpp, src/pre_process/m_mpi_proxy.fpp, src/post_process/m_mpi_proxy.fpp
Extends MPI_BCAST to include hyper_cleaning logical flag and speed/tau parameters across distributed processes.
Hyperbolic Cleaning State Variable Conversion
src/common/m_variables_conversion.fpp
Adds psi component propagation between conservative and primitive variables when hyper_cleaning is enabled.
Hyperbolic Cleaning Initialization
src/pre_process/m_initial_condition.fpp, src/pre_process/m_start_up.fpp, src/simulation/m_rhs.fpp, src/simulation/m_time_steppers.fpp
Initializes psi state variables to zero; adds GPU data transfer setup; applies exponential decay during time stepping.
MHD Module Renaming
src/simulation/m_mhd.fpp
Renames s_initialize_mhd_powell_module/s_finalize_mhd_powell_module to s_initialize_mhd_clean_module/s_finalize_mhd_clean_module.
MHD Module Activation Logic
src/simulation/m_start_up.fpp
Simplifies MHD clean module initialization from if (mhd .and. powell) to if (powell) only.
Riemann Solver Enhancements
src/simulation/m_riemann_solvers.fpp
Adds hyper_cleaning corrections to magnetosonic speeds and flux calculations; introduces Powell-condition branching alongside cylindrical coordinates for flux updates.
Output Infrastructure
src/post_process/m_data_output.fpp, src/post_process/m_start_up.fpp, src/simulation/m_data_output.fpp
Increments binary variable count when hyper_cleaning enabled; adds psi output handling in formatted/binary DB writes; includes commented placeholder for MPI reduction.
Configuration Schema
toolchain/mfc/run/case_dicts.py
Registers three new parameter definitions: hyper_cleaning (LOG), hyper_cleaning_speed (REAL), hyper_cleaning_tau (REAL) in case dictionary schema.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • Make fluid variables uniform through the code #1063 — Modifies the same variable conversion module (src/common/m_variables_conversion.fpp) to standardize fluid variables, providing context for state variable propagation patterns used in this PR's psi handling.

Suggested labels

Review effort 4/5, size:XXL

Suggested reviewers

  • sbryngelson
  • wilfonba

Poem

🐰 Hark! New cases bound and rotor spins with grace,
Psi cleans the divergence from magnetic space,
Powell wears a "Clean" name, shiny and refined,
MPI broadcasts preach what hyperbolic minds designed,
Eight test beds flourish where WENO orders play!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is incomplete relative to the template: many critical checklist items are unchecked (no comments added, no docstrings, no documentation updates, no regression tests, no example cases in examples/, no format check, incomplete GPU testing), and the author explicitly notes the PR needs rebasing and GPU work. Complete the unchecked items in the checklist: add code comments/docstrings, update documentation, add regression tests and example cases, run mfc.sh format, verify GPU compilation and testing on multiple architectures, and confirm no code duplication.
✅ Passed checks (2 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.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing touches
  • 📝 Generate docstrings

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 29.03226% with 44 lines in your changes missing coverage. Please review.
✅ Project coverage is 44.08%. Comparing base (944aa2f) to head (bd12ee0).

Files with missing lines Patch % Lines
src/simulation/m_riemann_solvers.fpp 33.33% 8 Missing and 4 partials ⚠️
src/simulation/m_time_steppers.fpp 0.00% 7 Missing and 2 partials ⚠️
src/post_process/m_start_up.fpp 0.00% 4 Missing and 1 partial ⚠️
src/pre_process/m_start_up.fpp 0.00% 4 Missing and 1 partial ⚠️
src/post_process/m_global_parameters.fpp 25.00% 2 Missing and 1 partial ⚠️
src/pre_process/m_global_parameters.fpp 25.00% 2 Missing and 1 partial ⚠️
src/pre_process/m_initial_condition.fpp 0.00% 2 Missing and 1 partial ⚠️
src/simulation/m_rhs.fpp 33.33% 2 Missing ⚠️
src/common/m_variables_conversion.fpp 0.00% 1 Missing ⚠️
src/post_process/m_data_output.fpp 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1086      +/-   ##
==========================================
- Coverage   44.16%   44.08%   -0.09%     
==========================================
  Files          71       71              
  Lines       20417    20473      +56     
  Branches     1991     2006      +15     
==========================================
+ Hits         9018     9026       +8     
- Misses      10252    10287      +35     
- Partials     1147     1160      +13     

☔ 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.

@danieljvickers danieljvickers marked this pull request as ready for review January 24, 2026 16:21
@qodo-code-review
Copy link
Contributor

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

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

Possible Issue

psi is being initialized with a hard-coded Gaussian profile whenever hyper_cleaning is enabled, which can silently override user-specified initial conditions/patch logic and makes results case-dependent in a non-obvious way. This should likely be removed or driven via a proper IC option/patch parameter rather than being unconditional.

! 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
Possible Issue

The modification of c_fast%L/c_fast%R under hyper_cleaning appears to force the left wave speed negative via min(c_fast%L, -hyper_cleaning_speed), which can invert/over-widen the Riemann fan depending on the sign convention used for c_fast. This deserves validation against the intended GLM/hyperbolic-cleaning wave speed inclusion.

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
Integration Gap

MHD initialization/finalization for the divergence-control module is gated on powell rather than hyper_cleaning, even though this PR introduces hyper_cleaning as the new approach. This risks not allocating/initializing needed MHD-related buffers for hyperbolic cleaning runs (or keeping Powell-era resources/paths active unintentionally).

if (powell) call s_initialize_mhd_clean_module

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: Remove the hard-coded, test-specific initialization for the hyperbolic cleaning variable psi. [possible issue, importance: 7]

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
! 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

Comment on lines +637 to +647
if (hyper_cleaning) then
do l = 0, p
do k = 0, n
do j = 0, m
q_cons_ts(1)%vf(psi_idx)%sf(j, k, l) = &
q_cons_ts(1)%vf(psi_idx)%sf(j, k, l)* &
exp(-dt/hyper_cleaning_tau)
end do
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: Move the hyperbolic cleaning damping term into the RHS flux calculation to ensure it is applied at each stage of the Runge-Kutta time integration, rather than only at the end of the full step. [possible issue, importance: 9]

Suggested change
if (hyper_cleaning) then
do l = 0, p
do k = 0, n
do j = 0, m
q_cons_ts(1)%vf(psi_idx)%sf(j, k, l) = &
q_cons_ts(1)%vf(psi_idx)%sf(j, k, l)* &
exp(-dt/hyper_cleaning_tau)
end do
end do
end do
end if
if (hyper_cleaning) then
do l = 0, p
do k = 0, n
do j = 0, m
flux_vf(psi_idx)%sf(j,k,l) = flux_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 if

if (hyperelasticity) call s_initialize_hyperelastic_module()

if (mhd .and. powell) call s_initialize_mhd_powell_module
if (powell) call s_initialize_mhd_clean_module
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggestion: Correct the condition for initializing the divergence-cleaning module to use the hyper_cleaning flag instead of the powell flag. [general, importance: 8]

Suggested change
if (powell) call s_initialize_mhd_clean_module
if (hyper_cleaning) call s_initialize_mhd_clean_module

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: 7

Caution

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

⚠️ Outside diff range comments (4)
src/simulation/m_mhd.fpp (1)

41-45: Double allocation of arrays will cause runtime error in 3D.

Line 41 allocates dw_dx_mhd, dw_dy_mhd, dw_dz_mhd unconditionally, but line 44 allocates them again when p > 0. This will cause a double allocation error in 3D simulations.

🐛 Proposed fix
-        @:ALLOCATE(du_dx_mhd(0:m,0:n,0:p), dv_dx_mhd(0:m,0:n,0:p), dw_dx_mhd(0:m,0:n,0:p))
+        @:ALLOCATE(du_dx_mhd(0:m,0:n,0:p), dv_dx_mhd(0:m,0:n,0:p))
         @:ALLOCATE(du_dy_mhd(0:m,0:n,0:p), dv_dy_mhd(0:m,0:n,0:p), dw_dy_mhd(0:m,0:n,0:p))
         if (p > 0) then
-            @:ALLOCATE(dw_dx_mhd(0:m,0:n,0:p), dw_dy_mhd(0:m,0:n,0:p), dw_dz_mhd(0:m,0:n,0:p))
+            @:ALLOCATE(dw_dx_mhd(0:m,0:n,0:p), dw_dz_mhd(0:m,0:n,0:p))
+            @:ALLOCATE(du_dz_mhd(0:m,0:n,0:p), dv_dz_mhd(0:m,0:n,0:p))
         end if

Note: Looking at the declared module variables (lines 24-26), you may need to review which derivatives are actually needed for 2D vs 3D and adjust accordingly. The du_dz_mhd, dv_dz_mhd, and dw_dz_mhd arrays are declared but du_dz_mhd and dv_dz_mhd don't appear to be allocated anywhere.

src/pre_process/m_start_up.fpp (1)

829-855: Hard-coded psi init overrides IC and only initializes l=0.
This clobbers any preexisting/loaded IC and leaves other z-planes untouched for 3D. It also mixes literal kinds. Consider moving this to a case/IC patch or at least initialize all planes with _wp literals.

🐛 Proposed fix (initialize all planes; use wp literals)
-        integer :: j, k
+        integer :: j, k, l
...
-        ! 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
+        ! hard-coded psi (consider moving to case/IC patch)
+        if (hyper_cleaning) then
+            do l = 0, p
+                do k = 0, n
+                    do j = 0, m
+                        q_cons_vf(psi_idx)%sf(j, k, l) = 1.0e-2_wp * &
+                            exp(-(x_cc(j)**2 + y_cc(k)**2 + z_cc(l)**2)/(2._wp*0.05_wp**2))
+                        q_prim_vf(psi_idx)%sf(j, k, l) = q_cons_vf(psi_idx)%sf(j, k, l)
+                    end do
+                end do
+            end do
+        end if
src/simulation/m_riemann_solvers.fpp (1)

4971-4984: Uninitialized components in Powell flux_gsrc copy.
When powell=.true., only component 1 of flux_gsrc_rs*_vf is assigned (line 962), but lines 4971–4984 copy all sys_size components into flux_gsrc_vf. Components 2 through sys_size will contain uninitialized memory. Either zero all components of flux_gsrc_rs*_vf after allocation or initialize only the required components before copying.

src/simulation/m_global_parameters.fpp (1)

294-298: Add psi_idx and damage_idx to the GPU global parameter declarations.

Both psi_idx and damage_idx are state variable indices used in GPU device contexts (GPU_ENTER_DATA in m_rhs.fpp, @:ACC_SETUP_SFs in m_time_steppers.fpp, and Riemann solver kernels). They follow the same pattern as other state variable indices (E_idx, B_idx, stress_idx, etc.) that are explicitly included in the GPU_DECLARE statements. Add them to the appropriate GPU_DECLARE line for consistency and to ensure availability in GPU device code.

🤖 Fix all issues with AI agents
In `@case_new/gauss_div_pulse_2/case.py`:
- Around line 12-18: The comment claiming a 1D domain ("n=1" and using a
non-ASCII ×) is inconsistent with the actual grid settings where "m": 100 and
"n": 100; either update the comment to accurately describe a 2D computational
domain (e.g., replace the non-ASCII × with 'x' and state m=100, n=100 over
x_domain%beg/end and y_domain%beg/end) or change the grid parameter "n" to 1 if
the intent was a 1D x-only run; locate the grid dict entries ("x_domain%beg",
"x_domain%end", "y_domain%beg", "y_domain%end", "m", "n") and make the
corresponding comment/text consistent with the chosen configuration.
- Around line 56-70: Add the missing hard-coded initial condition ID and
parameters so the Gaussian divergence pulse is actually activated: set hcid to
260 and add the two missing patch_icpp parameters patch_icpp(1)%a(2) and
patch_icpp(1)%a(3) (use the same numeric values used in your validation run),
alongside the existing patch_icpp(1)%a(1) entry so the IC block matches the
hardcoded expectation.

In `@case_new/slanted_shock_tube/case.py`:
- Around line 5-24: The file and meta should be consistent: decide whether this
is a 1D Gaussian divergence pulse or a 2D slanted shock tube and update the
header/comment, hcid, and domain/grid accordingly. If you mean a 1D y-slice
Gaussian pulse, change the header from "Gaussian Divergence Pulse (1D via
uniform y-slice)" to match the filename or vice versa, set "y_domain%end" to a
very small extent (e.g. equal to a single cell), reduce "n" to 1 (or minimal),
keep "m" as the long x resolution, and ensure hcid reflects the Gaussian pulse
case; alternatively, if this is the slanted shock tube test, update the
header/comment and hcid to describe a 2D slanted shock tube and keep the current
2D "y_domain%end" and "n"/"m" values. Locate and edit the JSON keys
"y_domain%end", "n", "m", the header comment block, and the hcid identifier to
make them consistent.

In `@src/common/include/2dHardcodedIC.fpp`:
- Around line 242-254: In the Gaussian divergence pulse case (case 260) validate
patch_icpp(patch_id)%a(3) (sigma) before using it: check for zero or near-zero
and either set a safe minimum (e.g., sigma_min) or raise/log an error and abort
per .fpp guidelines; then compute C_mhd = eps_mhd*sigma*sqrt(pi)*0.5_wp and use
that safe sigma for the erf denominator and any other divisions so
q_prim_vf(B_idx%beg)%sf(i, j, 0) = 1 + C_mhd*erf((x_cc(i)-0.5_wp)/sigma) cannot
produce NaNs. Ensure the check is colocated with the lines that set eps_mhd,
sigma and C_mhd in the Gaussian Divergence Pulse block.

In `@src/simulation/m_rhs.fpp`:
- Around line 248-253: The pointer association block under "if (hyper_cleaning)
then" must be guarded by the non-IGR path and must avoid leaking an existing
allocation: change the condition to check ".not. igr" (e.g. if (hyper_cleaning
.and. .not. igr) then) so q_cons_qp%vf(psi_idx)%sf is only aliased when it's
allocated, and before doing the association for q_prim_qp%vf(psi_idx)%sf ensure
you either deallocate q_prim_qp%vf(psi_idx)%sf or skip its earlier allocation in
the generic allocation loop so the subsequent pointer assignment does not leak;
keep using the same GPU directives ($:GPU_ENTER_DATA) but only inside this
guarded branch.

In `@src/simulation/m_riemann_solvers.fpp`:
- Around line 649-652: The current hyper_cleaning branch incorrectly sets
c_fast%L = min(c_fast%L, -hyper_cleaning_speed) which forces a negative left
bound and can shrink the HLL envelope; instead ensure the magnitude is applied
as a positive bound on speeds by using max for c_fast%L and min for c_fast%R (or
apply the magnitude directly when computing s_L/s_R). Update the hyper_cleaning
block around c_fast%L, c_fast%R so c_fast%L = max(c_fast%L,
-hyper_cleaning_speed) is not used — rather clamp magnitudes correctly (e.g.,
c_fast%L = -max(abs(c_fast%L), hyper_cleaning_speed) or set c_fast%L =
-hyper_cleaning_speed with c_fast%R = hyper_cleaning_speed) ensuring s_L = vel -
c_fast and s_R = vel + c_fast produce a proper HLL envelope; modify the code
referencing c_fast%L/c_fast%R in this block accordingly.

In `@src/simulation/m_time_steppers.fpp`:
- Around line 637-647: The decay loop runs on the host but q_cons_ts is on the
device in GPU builds; wrap the triple loop in the GPU loop macro (e.g., the
project's DEVICE/GPU_LOOP macro) and precompute the decay factor into a
loop-local variable (e.g., real :: decay = exp(-dt/hyper_cleaning_tau)) before
entering the GPU loop; then replace repeated exp() calls with that decay and
mark any loop-local scalars (like decay and the loop indices if required) as
private so the loops over j,k,l that update q_cons_ts(1)%vf(psi_idx)%sf(j,k,l)
execute correctly and efficiently on the device.
♻️ Duplicate comments (2)
src/simulation/m_riemann_solvers.fpp (2)

5027-5041: Same concern as the y-direction Powell copy.
Line 5027 duplicates the full-array copy for powell; please ensure the copied components are fully initialized. (See Line 4971 comment.)


5083-5096: Same concern as the y-direction Powell copy.
Line 5083 duplicates the full-array copy for powell; please ensure the copied components are fully initialized. (See Line 4971 comment.)

🧹 Nitpick comments (7)
case_new/2D_orszag_tang_weno5m_nohyper/case.py (2)

12-15: Minor type inconsistency in domain bounds.

x_domain%beg and y_domain%beg are integers (0) while x_domain%end and y_domain%end are floats (1.0). For consistency and to avoid potential type-handling issues downstream, consider using 0.0 for the begin values.

Suggested fix
             # Computational Domain Parameters
-            "x_domain%beg": 0,
+            "x_domain%beg": 0.0,
             "x_domain%end": 1.0,
-            "y_domain%beg": 0,
+            "y_domain%beg": 0.0,
             "y_domain%end": 1.0,

32-32: Inconsistent quote style.

This line uses single quotes 'T' while all other boolean string values use double quotes "T". While functionally equivalent in Python, consistent formatting improves readability.

Suggested fix
-            "mapped_weno": 'T',
+            "mapped_weno": "T",
src/simulation/m_mhd.fpp (1)

39-39: Consider updating error message to match renamed module.

The error message still references "Powell correction" but the module has been renamed to "clean". Consider updating for consistency.

♻️ Suggested fix
-        if (n == 0) call s_mpi_abort('Fatal Error: Powell correction is not applicable for 1D')
+        if (n == 0) call s_mpi_abort('Fatal Error: MHD divergence cleaning is not applicable for 1D')
src/simulation/m_data_output.fpp (1)

1494-1498: Remove commented-out placeholder code or implement the TODO.

This commented-out block is a non-functional stub that doesn't reduce any meaningful variable. Either implement the actual hyper_cleaning reduction logic or remove the placeholder to avoid dead code accumulation.

♻️ Suggested fix (remove placeholder)
                 if (cont_damage) then
                     tmp = damage_state
                     call s_mpi_allreduce_sum(tmp, damage_state)
                 end if
-
-                ! if (hyper_cleaning) then
-                !     tmp = 0._wp
-                !     call s_mpi_allreduce_sum(tmp, tmp) ! TODO
-                ! end if
             end if

If hyper_cleaning requires probe output support, would you like me to help implement the actual reduction logic?

src/pre_process/m_global_parameters.fpp (1)

378-379: Initialize psi_idx with a safe default.

psi_idx is new but never assigned in the defaults block. Setting it to dflt_int (or -1) reduces accidental use when hyper_cleaning = .false..

♻️ Proposed fix
-        cont_damage = .false.
-        hyper_cleaning = .false.
+        cont_damage = .false.
+        hyper_cleaning = .false.
+        psi_idx = dflt_int
src/simulation/m_global_parameters.fpp (1)

1166-1169: Consider adding else clause for psi_idx initialization.

Unlike the post-processing module (which sets psi_idx = dflt_int when hyper_cleaning is disabled), this simulation module leaves psi_idx uninitialized when the feature is off. While this matches the pattern for damage_idx, it could lead to undefined behavior if code inadvertently references psi_idx without first checking hyper_cleaning.

This is consistent with existing code, so it's likely intentional, but worth noting for awareness.

♻️ Optional: Add else clause for consistency
             if (hyper_cleaning) then
                 psi_idx = sys_size + 1
                 sys_size = psi_idx
+            else
+                psi_idx = dflt_int
             end if
case_new/2D_orszag_tang_weno3m/case.py (1)

5-78: Consider extracting shared case defaults to reduce duplication.
These new case_new/*/case.py files are nearly identical; a small helper to build a base dict and override per-case knobs would reduce drift and make future updates safer.

Comment on lines +12 to +18
# Computational Domain: [0,1]×[0,1] but n=1 → effectively 1D in x
"x_domain%beg":-0.5,
"x_domain%end": 0.5,
"y_domain%beg":-0.5,
"y_domain%end": 0.5,
"m": 100,
"n": 100,
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 | 🟡 Minor

Comment doesn’t match the grid settings (and uses a non-ASCII ×).
Line 12 claims n=1, but n=100; please align the comment with the actual setup (or set n=1 if that was the intent).

✏️ Possible tweak
-            # Computational Domain: [0,1]×[0,1] but n=1 → effectively 1D in x
+            # Computational Domain: [-0.5,0.5] x [-0.5,0.5]; IC is x-only so the solution stays 1D
🧰 Tools
🪛 Ruff (0.14.13)

12-12: Comment contains ambiguous × (MULTIPLICATION SIGN). Did you mean x (LATIN SMALL LETTER X)?

(RUF003)

🤖 Prompt for AI Agents
In `@case_new/gauss_div_pulse_2/case.py` around lines 12 - 18, The comment
claiming a 1D domain ("n=1" and using a non-ASCII ×) is inconsistent with the
actual grid settings where "m": 100 and "n": 100; either update the comment to
accurately describe a 2D computational domain (e.g., replace the non-ASCII ×
with 'x' and state m=100, n=100 over x_domain%beg/end and y_domain%beg/end) or
change the grid parameter "n" to 1 if the intent was a 1D x-only run; locate the
grid dict entries ("x_domain%beg", "x_domain%end", "y_domain%beg",
"y_domain%end", "m", "n") and make the corresponding comment/text consistent
with the chosen configuration.

Comment on lines +56 to +70
# --- Patch 1: Gaussian Divergence Pulse IC ---
"patch_icpp(1)%geometry": 3, # Cartesian
"patch_icpp(1)%x_centroid": 0.0,
"patch_icpp(1)%y_centroid": 0.0,
"patch_icpp(1)%length_x": 1.0,
"patch_icpp(1)%length_y": 1.0,
"patch_icpp(1)%vel(1)": 0.0,
"patch_icpp(1)%vel(2)": 0.0,
"patch_icpp(1)%vel(3)": 0.0,
"patch_icpp(1)%pres": 1.0,
"patch_icpp(1)%alpha_rho(1)": 1.0,
"patch_icpp(1)%alpha(1)": 1.0,
"patch_icpp(1)%Bx": 0.0,
"patch_icpp(1)%By": 0.0,
"patch_icpp(1)%Bz": 0.0,
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

Gaussian divergence pulse IC isn’t activated.
The hardcoded IC expects hcid=260 plus patch_icpp%a(2) and a(3); without them this case stays uniform and won’t validate divergence cleaning. Please wire the hcid and parameters.

🛠️ Suggested wiring (set values to the ones used in your validation)
             # --- Patch 1: Gaussian Divergence Pulse IC ---
+            "patch_icpp(1)%hcid": 260,
+            "patch_icpp(1)%a(2)": 0.1,  # eps_mhd (∂Bx/∂x amplitude) — adjust to intended value
+            "patch_icpp(1)%a(3)": 0.05, # sigma — adjust to intended value
             "patch_icpp(1)%geometry": 3,   # Cartesian
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# --- Patch 1: Gaussian Divergence Pulse IC ---
"patch_icpp(1)%geometry": 3, # Cartesian
"patch_icpp(1)%x_centroid": 0.0,
"patch_icpp(1)%y_centroid": 0.0,
"patch_icpp(1)%length_x": 1.0,
"patch_icpp(1)%length_y": 1.0,
"patch_icpp(1)%vel(1)": 0.0,
"patch_icpp(1)%vel(2)": 0.0,
"patch_icpp(1)%vel(3)": 0.0,
"patch_icpp(1)%pres": 1.0,
"patch_icpp(1)%alpha_rho(1)": 1.0,
"patch_icpp(1)%alpha(1)": 1.0,
"patch_icpp(1)%Bx": 0.0,
"patch_icpp(1)%By": 0.0,
"patch_icpp(1)%Bz": 0.0,
# --- Patch 1: Gaussian Divergence Pulse IC ---
"patch_icpp(1)%hcid": 260,
"patch_icpp(1)%a(2)": 0.1, # eps_mhd (∂Bx/∂x amplitude) — adjust to intended value
"patch_icpp(1)%a(3)": 0.05, # sigma — adjust to intended value
"patch_icpp(1)%geometry": 3, # Cartesian
"patch_icpp(1)%x_centroid": 0.0,
"patch_icpp(1)%y_centroid": 0.0,
"patch_icpp(1)%length_x": 1.0,
"patch_icpp(1)%length_y": 1.0,
"patch_icpp(1)%vel(1)": 0.0,
"patch_icpp(1)%vel(2)": 0.0,
"patch_icpp(1)%vel(3)": 0.0,
"patch_icpp(1)%pres": 1.0,
"patch_icpp(1)%alpha_rho(1)": 1.0,
"patch_icpp(1)%alpha(1)": 1.0,
"patch_icpp(1)%Bx": 0.0,
"patch_icpp(1)%By": 0.0,
"patch_icpp(1)%Bz": 0.0,
🤖 Prompt for AI Agents
In `@case_new/gauss_div_pulse_2/case.py` around lines 56 - 70, Add the missing
hard-coded initial condition ID and parameters so the Gaussian divergence pulse
is actually activated: set hcid to 260 and add the two missing patch_icpp
parameters patch_icpp(1)%a(2) and patch_icpp(1)%a(3) (use the same numeric
values used in your validation run), alongside the existing patch_icpp(1)%a(1)
entry so the IC block matches the hardcoded expectation.

Comment on lines +5 to +24
# -----------------------------------------------------------------------------
# 2.1 Gaussian Divergence Pulse (1D via uniform y-slice)
# -----------------------------------------------------------------------------
print(
json.dumps(
{
# Logistics
"run_time_info": "T",
# Computational Domain:
"x_domain%beg": -1.0,
"x_domain%end": 2.0,
"y_domain%beg":-1.0,
# "y_domain%end": 2.0/256.0,
"y_domain%end": 2.0,
"m": 256*3,
"n": 256*3,
"dt": 0.08/math.sqrt(5.0)/170.0/2.0,
"t_step_start": 0,
"t_step_stop": 170*2,
"t_step_save": 10*2,
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

Mismatch between filename, description, and domain setup.
This file is named slanted_shock_tube but the header and hcid indicate a Gaussian divergence pulse, and the domain/grid are 2D rather than a 1D y-slice. Please align (a) the filename, (b) the header/comments, and (c) the y-extent + n values with the intended test to avoid running the wrong validation case.

🤖 Prompt for AI Agents
In `@case_new/slanted_shock_tube/case.py` around lines 5 - 24, The file and meta
should be consistent: decide whether this is a 1D Gaussian divergence pulse or a
2D slanted shock tube and update the header/comment, hcid, and domain/grid
accordingly. If you mean a 1D y-slice Gaussian pulse, change the header from
"Gaussian Divergence Pulse (1D via uniform y-slice)" to match the filename or
vice versa, set "y_domain%end" to a very small extent (e.g. equal to a single
cell), reduce "n" to 1 (or minimal), keep "m" as the long x resolution, and
ensure hcid reflects the Gaussian pulse case; alternatively, if this is the
slanted shock tube test, update the header/comment and hcid to describe a 2D
slanted shock tube and keep the current 2D "y_domain%end" and "n"/"m" values.
Locate and edit the JSON keys "y_domain%end", "n", "m", the header comment
block, and the hcid identifier to make them consistent.

Comment on lines +242 to +254
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.

⚠️ Potential issue | 🟠 Major

Guard against σ=0 in the Gaussian divergence pulse.
sigma is used in a denominator; if unset/zero it will produce NaNs. Please validate it before use (per .fpp guidelines).

🛡️ Suggested safety check
         eps_mhd = patch_icpp(patch_id)%a(2)
         sigma = patch_icpp(patch_id)%a(3)
+        @:ASSERT(sigma > 0._wp, "Gaussian divergence pulse requires patch_icpp%a(3) (sigma) > 0")
         C_mhd = eps_mhd*sigma*sqrt(pi)*0.5_wp
🤖 Prompt for AI Agents
In `@src/common/include/2dHardcodedIC.fpp` around lines 242 - 254, In the Gaussian
divergence pulse case (case 260) validate patch_icpp(patch_id)%a(3) (sigma)
before using it: check for zero or near-zero and either set a safe minimum
(e.g., sigma_min) or raise/log an error and abort per .fpp guidelines; then
compute C_mhd = eps_mhd*sigma*sqrt(pi)*0.5_wp and use that safe sigma for the
erf denominator and any other divisions so q_prim_vf(B_idx%beg)%sf(i, j, 0) = 1
+ C_mhd*erf((x_cc(i)-0.5_wp)/sigma) cannot produce NaNs. Ensure the check is
colocated with the lines that set eps_mhd, sigma and C_mhd in the Gaussian
Divergence Pulse block.

Comment on lines +248 to +253
if (hyper_cleaning) then
q_prim_qp%vf(psi_idx)%sf => &
q_cons_qp%vf(psi_idx)%sf
$:GPU_ENTER_DATA(copyin='[q_prim_qp%vf(psi_idx)%sf]')
$:GPU_ENTER_DATA(attach='[q_prim_qp%vf(psi_idx)%sf]')
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 | 🟠 Major

Guard psi aliasing to avoid unallocated access and leaks.

Line 248 currently runs even when igr is true, but q_cons_qp%vf(psi_idx)%sf is only allocated inside the .not. igr path. Also, in the non-IGR path q_prim_qp%vf(psi_idx)%sf is already allocated in the generic allocation loop, so re-aliasing here leaks that allocation. Please guard the block with .not. igr and deallocate (or skip allocating) q_prim_qp%vf(psi_idx)%sf before the pointer association.

🐛 Proposed fix
-        if (hyper_cleaning) then
+        if (.not. igr .and. hyper_cleaning) then
+            if (associated(q_prim_qp%vf(psi_idx)%sf)) then
+                @:DEALLOCATE(q_prim_qp%vf(psi_idx)%sf)
+            end if
             q_prim_qp%vf(psi_idx)%sf => &
                 q_cons_qp%vf(psi_idx)%sf
             $:GPU_ENTER_DATA(copyin='[q_prim_qp%vf(psi_idx)%sf]')
             $:GPU_ENTER_DATA(attach='[q_prim_qp%vf(psi_idx)%sf]')
         end if
🤖 Prompt for AI Agents
In `@src/simulation/m_rhs.fpp` around lines 248 - 253, The pointer association
block under "if (hyper_cleaning) then" must be guarded by the non-IGR path and
must avoid leaking an existing allocation: change the condition to check ".not.
igr" (e.g. if (hyper_cleaning .and. .not. igr) then) so q_cons_qp%vf(psi_idx)%sf
is only aliased when it's allocated, and before doing the association for
q_prim_qp%vf(psi_idx)%sf ensure you either deallocate q_prim_qp%vf(psi_idx)%sf
or skip its earlier allocation in the generic allocation loop so the subsequent
pointer assignment does not leak; keep using the same GPU directives
($:GPU_ENTER_DATA) but only inside this guarded branch.

Comment on lines +649 to +652
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
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

Fix sign of c_fast bounding for hyper_cleaning.
Line 650 drives c_fast%L negative, which flips the left-going wave bound used in s_L = vel - c_fast and can shrink the HLL envelope. If hyper_cleaning_speed is a magnitude, bound by max (or apply to s_L/s_R directly).

🔧 Suggested fix
-                            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
+                            if (hyper_cleaning) then ! mhd
+                                c_fast%L = max(c_fast%L, hyper_cleaning_speed)
+                                c_fast%R = max(c_fast%R, hyper_cleaning_speed)
+                            end if
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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
if (hyper_cleaning) then ! mhd
c_fast%L = max(c_fast%L, hyper_cleaning_speed)
c_fast%R = max(c_fast%R, hyper_cleaning_speed)
end if
🤖 Prompt for AI Agents
In `@src/simulation/m_riemann_solvers.fpp` around lines 649 - 652, The current
hyper_cleaning branch incorrectly sets c_fast%L = min(c_fast%L,
-hyper_cleaning_speed) which forces a negative left bound and can shrink the HLL
envelope; instead ensure the magnitude is applied as a positive bound on speeds
by using max for c_fast%L and min for c_fast%R (or apply the magnitude directly
when computing s_L/s_R). Update the hyper_cleaning block around c_fast%L,
c_fast%R so c_fast%L = max(c_fast%L, -hyper_cleaning_speed) is not used — rather
clamp magnitudes correctly (e.g., c_fast%L = -max(abs(c_fast%L),
hyper_cleaning_speed) or set c_fast%L = -hyper_cleaning_speed with c_fast%R =
hyper_cleaning_speed) ensuring s_L = vel - c_fast and s_R = vel + c_fast produce
a proper HLL envelope; modify the code referencing c_fast%L/c_fast%R in this
block accordingly.

Comment on lines +637 to +647
if (hyper_cleaning) then
do l = 0, p
do k = 0, n
do j = 0, m
q_cons_ts(1)%vf(psi_idx)%sf(j, k, l) = &
q_cons_ts(1)%vf(psi_idx)%sf(j, k, l)* &
exp(-dt/hyper_cleaning_tau)
end do
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.

⚠️ Potential issue | 🟠 Major

Decay loop must run on device (missing GPU macro).
q_cons_ts lives on the device in GPU builds, so the host-side loop can be incorrect and will definitely be slow. Wrap this in the GPU loop macro and precompute the decay factor. As per coding guidelines, GPU loops should use the macros and declare loop-local variables via private when needed.

✅ Proposed fix (GPU loop + precomputed decay)
-        if (hyper_cleaning) then
-            do l = 0, p
-                do k = 0, n
-                    do j = 0, m
-                        q_cons_ts(1)%vf(psi_idx)%sf(j, k, l) = &
-                            q_cons_ts(1)%vf(psi_idx)%sf(j, k, l)* &
-                            exp(-dt/hyper_cleaning_tau)
-                    end do
-                end do
-            end do
-        end if
+        if (hyper_cleaning) then
+            psi_decay = exp(-dt/hyper_cleaning_tau)
+            $:GPU_PARALLEL_LOOP(collapse=3)
+            do l = 0, p
+                do k = 0, n
+                    do j = 0, m
+                        q_cons_ts(1)%vf(psi_idx)%sf(j, k, l) = &
+                            q_cons_ts(1)%vf(psi_idx)%sf(j, k, l) * psi_decay
+                    end do
+                end do
+            end do
+            $:END_GPU_PARALLEL_LOOP()
+        end if
🤖 Prompt for AI Agents
In `@src/simulation/m_time_steppers.fpp` around lines 637 - 647, The decay loop
runs on the host but q_cons_ts is on the device in GPU builds; wrap the triple
loop in the GPU loop macro (e.g., the project's DEVICE/GPU_LOOP macro) and
precompute the decay factor into a loop-local variable (e.g., real :: decay =
exp(-dt/hyper_cleaning_tau)) before entering the GPU loop; then replace repeated
exp() calls with that decay and mark any loop-local scalars (like decay and the
loop indices if required) as private so the loops over j,k,l that update
q_cons_ts(1)%vf(psi_idx)%sf(j,k,l) execute correctly and efficiently on the
device.

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.

4 issues found across 28 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="case_new/gauss_div_pulse_2/case.py">

<violation number="1" location="case_new/gauss_div_pulse_2/case.py:18">
P2: The case is documented as a 1D y-slice, but n is set to 100, making it 2D. If the intent is a 1D slice, set n to 1 to match the stated setup.</violation>
</file>

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

<violation number="1" location="src/pre_process/m_start_up.fpp:847">
P2: The new hyper_cleaning block hard-codes a Gaussian psi and overwrites any psi set by the initial-condition generator or restart files. This makes hyper_cleaning runs ignore user-provided psi values and breaks restart fidelity. Consider removing this block or gating it behind a dedicated input option so default runs preserve the provided initial condition.</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 but never declared/copied to the GPU, yet it is used inside GPU kernels. This can leave psi_idx undefined on device and cause out-of-bounds indexing when hyper_cleaning is enabled. Add psi_idx to the GPU_DECLARE and GPU_UPDATE lists used for scalar indices.</violation>
</file>

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

<violation number="1" location="src/simulation/m_rhs.fpp:249">
P2: q_prim_qp%vf(psi_idx)%sf is already allocated earlier when hyper_cleaning is enabled, and this pointer reassignment discards that allocation, leaking a full-sized scalar field. This can waste significant memory on large grids. Consider deallocating before aliasing or skipping the initial allocation for psi_idx.</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.

"y_domain%beg":-0.5,
"y_domain%end": 0.5,
"m": 100,
"n": 100,
Copy link
Contributor

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

Choose a reason for hiding this comment

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

P2: The case is documented as a 1D y-slice, but n is set to 100, making it 2D. If the intent is a 1D slice, set n to 1 to match the stated setup.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At case_new/gauss_div_pulse_2/case.py, line 18:

<comment>The case is documented as a 1D y-slice, but n is set to 100, making it 2D. If the intent is a 1D slice, set n to 1 to match the stated setup.</comment>

<file context>
@@ -0,0 +1,76 @@
+            "y_domain%beg":-0.5,
+            "y_domain%end": 0.5,
+            "m": 100,
+            "n": 100,
+            "p": 0,
+            "dt": 0.005,
</file context>
Fix with Cubic


call s_generate_initial_condition()

! hard-coded psi
Copy link
Contributor

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

Choose a reason for hiding this comment

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

P2: The new hyper_cleaning block hard-codes a Gaussian psi and overwrites any psi set by the initial-condition generator or restart files. This makes hyper_cleaning runs ignore user-provided psi values and breaks restart fidelity. Consider removing this block or gating it behind a dedicated input option so default runs preserve the provided initial condition.

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 847:

<comment>The new hyper_cleaning block hard-codes a Gaussian psi and overwrites any psi set by the initial-condition generator or restart files. This makes hyper_cleaning runs ignore user-provided psi values and breaks restart fidelity. Consider removing this block or gating it behind a dedicated input option so default runs preserve the provided initial condition.</comment>

<file context>
@@ -842,6 +844,16 @@ contains
 
         call s_generate_initial_condition()
 
+        ! hard-coded psi
+        if (hyper_cleaning) then
+            do j = 0, m
</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 24, 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 but never declared/copied to the GPU, yet it is used inside GPU kernels. This can leave psi_idx undefined on device and cause out-of-bounds indexing when hyper_cleaning is enabled. Add psi_idx to the GPU_DECLARE and GPU_UPDATE lists used for scalar indices.

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 but never declared/copied to the GPU, yet it is used inside GPU kernels. This can leave psi_idx undefined on device and cause out-of-bounds indexing when hyper_cleaning is enabled. Add psi_idx to the GPU_DECLARE and GPU_UPDATE lists used for scalar indices.</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

end if

if (hyper_cleaning) then
q_prim_qp%vf(psi_idx)%sf => &
Copy link
Contributor

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

Choose a reason for hiding this comment

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

P2: q_prim_qp%vf(psi_idx)%sf is already allocated earlier when hyper_cleaning is enabled, and this pointer reassignment discards that allocation, leaking a full-sized scalar field. This can waste significant memory on large grids. Consider deallocating before aliasing or skipping the initial allocation for psi_idx.

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

<comment>q_prim_qp%vf(psi_idx)%sf is already allocated earlier when hyper_cleaning is enabled, and this pointer reassignment discards that allocation, leaking a full-sized scalar field. This can waste significant memory on large grids. Consider deallocating before aliasing or skipping the initial allocation for psi_idx.</comment>

<file context>
@@ -245,6 +245,13 @@ contains
         end if
 
+        if (hyper_cleaning) then
+            q_prim_qp%vf(psi_idx)%sf => &
+                q_cons_qp%vf(psi_idx)%sf
+            $:GPU_ENTER_DATA(copyin='[q_prim_qp%vf(psi_idx)%sf]')
</file context>
Fix with Cubic

@danieljvickers
Copy link
Member

Looks like it passes tests. I stupidly didn't move the new cases to examples. Once I generate more golden files and it passes, we can rerun the test suite and hopefully this will be wrapped up.

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

Development

Successfully merging this pull request may close these issues.

3 participants