[SPH][NIMHD] Add Non-Ideal MHD terms#1765
Conversation
|
Thanks @y-lapeyre for opening this PR! You can do multiple things directly here: Once the workflow completes a message will appear displaying informations related to the run. Also the PR gets automatically reviewed by gemini, you can: |
There was a problem hiding this comment.
Code Review
This pull request introduces support for Non-Ideal Magnetohydrodynamics (NIMHD) by adding Ohmic resistivity, Hall effect, and Ambipolar Diffusion terms. Key changes include the addition of NIMHD configuration parameters, mathematical kernels for current and heating terms, and a new CFL timestep constraint for Ambipolar Diffusion. Review feedback highlighted a critical architectural issue regarding the pairwise calculation of electric current, which should be a field quantity requiring a two-pass approach. Additionally, several potential division-by-zero bugs were identified in the mathematical kernels and timestep calculations, along with a likely incorrect gradient index in the induction term and misleading argument names in the Python bindings.
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces support for Non-Ideal Magnetohydrodynamics (NIMHD) in the SPH solver, adding configuration parameters, mathematical formulations for current density and resistivity, and the corresponding derivative updates. However, several critical issues must be addressed: a bug where the current density J_a is passed by value instead of reference in MagCurrentJ_sum (discarding accumulated values), a race condition and event synchronization bug in the CFL timestep calculation due to variable shadowing, and potential out-of-bounds reads in compute_J from iterating over ghost particles that lack neighbor lists. Additionally, minor improvements are recommended, such as removing unused parameters in B_NI_terms, cleaning up a temporary comment, and renaming a Python binding parameter for consistency.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
Workflow reportworkflow report corresponding to commit 8b64f74 Pre-commit check reportPre-commit check: ✅ Test pipeline can run. Clang-tidy diff reportDoxygen diff with
|
|
/gemini review |
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Code Review
This pull request introduces Non-Ideal Magnetohydrodynamics (NIMHD) support to the SPH solver, including configurations for Ohmic resistivity, Hall effect, and Ambipolar diffusion, as well as the calculation of electric current density and non-ideal heating/induction terms. The review feedback highlights critical issues with SYCL event shadowing that can lead to concurrent kernel execution and data races on the timestep buffer. Other recommendations include evaluating the NIMHD flag at compile-time, skipping self-interactions in SPH neighbor loops, removing commented-out code, and resolving a developer query about the correct kernel gradient.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| // first, finish the outer kernel and write in cfl_dt | ||
| // then, compute psi_dt and write in cfl_dt | ||
| depends_list.add_event(e); | ||
| auto e = q.submit(depends_list, [&](sycl::handler &cgh) { |
There was a problem hiding this comment.
The variable e is shadowed here using auto e = ... inside the if (has_psi_field) block. This prevents the outer e variable (from the first CFL kernel) from being updated. Consequently, when entering the if (do_NIMHD) block, depends_list.add_event(e) will add the first CFL kernel's event instead of the psi cleaning kernel's event. This causes the NIMHD kernel and the psi cleaning kernel to run concurrently, leading to a data race on cfl_dt and potential undefined behavior. Removing auto to reassign the outer e variable resolves this issue.
| auto e = q.submit(depends_list, [&](sycl::handler &cgh) { | |
| e = q.submit(depends_list, [&](sycl::handler &cgh) { |
|
|
||
| if (do_NIMHD) { | ||
| depends_list.add_event(e); | ||
| auto e = q.submit(depends_list, [&](sycl::handler &cgh) { |
There was a problem hiding this comment.
Similarly, e is shadowed here using auto e = ... inside the if (do_NIMHD) block. This prevents the outer e from being updated to the NIMHD kernel's event, meaning the subsequent complete_event_state(e) calls on lines 2614-2616 may not wait for the NIMHD kernel to finish. Removing auto to reassign the outer e variable resolves this issue.
| auto e = q.submit(depends_list, [&](sycl::handler &cgh) { | |
| e = q.submit(depends_list, [&](sycl::handler &cgh) { |
| u32 iB_on_rho_interf = ghost_layout.get_field_idx<Tvec>("B/rho"); | ||
| u32 ipsi_on_ch_interf = ghost_layout.get_field_idx<Tscal>("psi/ch"); | ||
|
|
||
| bool do_NIMHD = solver_config.do_NIMHD(); |
There was a problem hiding this comment.
do_NIMHD is evaluated at runtime using solver_config.do_NIMHD(). Since MHD_mode is a template parameter of update_derivs_MHD_impl, this can be evaluated at compile-time as constexpr bool do_NIMHD = (MHD_mode == shamrock::sph::mhd::MHDType::NonIdeal);. This allows the compiler to optimize away the Non-Ideal branches when compiling the Ideal MHD version.
| bool do_NIMHD = solver_config.do_NIMHD(); | |
| constexpr bool do_NIMHD = (MHD_mode == shamrock::sph::mhd::MHDType::NonIdeal); |
| particle_looper.for_each_object(id_a, [&](u32 id_b) { | ||
| Tvec dr = xyz_a - xyz[id_b]; | ||
| Tscal rab2 = sycl::dot(dr, dr); | ||
| Tscal h_b = hpart[id_b]; |
There was a problem hiding this comment.
In SPH neighbor loops, self-interaction (id_a == id_b) can be skipped early to avoid redundant calculations (since B_a - B_b is zero, the contribution to J_a is mathematically zero).
particle_looper.for_each_object(id_a, [&](u32 id_b) {
if (id_a == id_b) {
return;
}
Tvec dr = xyz_a - xyz[id_b];
Tscal rab2 = sycl::dot(dr, dr);
Tscal h_b = hpart[id_b];| particle_looper.for_each_object(id_a, [&](u32 id_b) { | ||
| Tvec dr = xyz_a - xyz[id_b]; | ||
| Tscal rab2 = sycl::dot(dr, dr); | ||
| Tscal h_b = hpart[id_b]; |
There was a problem hiding this comment.
| // Tvec J_a = MagCurrentJ<Tvec, Tscal, MHD_mode>( | ||
| // pmass, B_a, B_b, r_ab_unit * dWab_a, sub_fact_a, mu_0); | ||
| // Tvec J_b = MagCurrentJ<Tvec, Tscal, MHD_mode>( | ||
| // pmass, B_a, B_b, r_ab_unit * dWab_b, sub_fact_b, mu_0); |
| += v_ab * dB_on_rho_induction_term(pmass, rho_a_sq, B_a, omega_a, r_ab_unit * dWab_b); | ||
| dB_on_rho_dt += v_ab | ||
| * dB_on_rho_induction_term( | ||
| pmass, rho_a_sq, B_a, omega_a, r_ab_unit * dWab_a); // @@@ dWab_b ? |
There was a problem hiding this comment.
The developer left a query // @@@ dWab_b ? regarding whether dWab_a or dWab_b should be used. Since the induction term is evaluated for particle a using particle a's properties (rho_a_sq, B_a, omega_a), the kernel gradient must be evaluated with respect to particle a's smoothing length (h_a), which corresponds to dWab_a. Thus, dWab_a is mathematically correct, and the comment can be removed.
| pmass, rho_a_sq, B_a, omega_a, r_ab_unit * dWab_a); // @@@ dWab_b ? | |
| pmass, rho_a_sq, B_a, omega_a, r_ab_unit * dWab_a); |
Add and enable the use of no-ideal MHD in the MHD solver, using the structure I previously set out for it.
Tests:
Could be merged as is and fixed with a 2nd PR that adds the wave damping CI test.