Skip to content

[AMR][RefinementHandler] Main PR with sod and amr tests#1719

Open
Akos299 wants to merge 22 commits intoShamrock-code:mainfrom
Akos299:features/all-amr-dev
Open

[AMR][RefinementHandler] Main PR with sod and amr tests#1719
Akos299 wants to merge 22 commits intoShamrock-code:mainfrom
Akos299:features/all-amr-dev

Conversation

@Akos299
Copy link
Copy Markdown
Collaborator

@Akos299 Akos299 commented Mar 20, 2026

This PR is the main from which modular ones will be extract from.

  • [ ]Sedov AMR test
sedov_amr_clean_v1.mp4
  • Sod AMR test
sod_tube_3_1_baryonic_density

@Akos299 Akos299 requested review from bcommerc and tdavidcl March 20, 2026 14:53
@github-actions
Copy link
Copy Markdown
Contributor

Thanks @Akos299 for opening this PR!

You can do multiple things directly here:
1 - Comment pre-commit.ci run to run pre-commit checks.
2 - Comment pre-commit.ci autofix to apply fixes.
3 - Add label autofix.ci to fix authorship & pre-commit for every commit made.
4 - Add label light-ci to only trigger a reduced & faster version of the CI (need the full one before merge).
5 - Add label trigger-ci to create an empty commit to trigger the CI.

Once the workflow completes a message will appear displaying informations related to the run.

Also the PR gets automatically reviewed by gemini, you can:
1 - Comment /gemini review to trigger a review
2 - Comment /gemini summary for a summary
3 - Tag it using @gemini-code-assist either in the PR or in review comments on files

@Akos299 Akos299 requested a review from nbrucy March 20, 2026 14:54
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the Adaptive Mesh Refinement (AMR) capabilities of the RAMSES solver by introducing a new pseudo-gradient based refinement criterion. It includes comprehensive C++ backend modifications to support this new mode, alongside new Python examples that validate its functionality for common hydrodynamic test cases. The changes also refine the grid management process by enforcing the 2:1 refinement rule and updating internal data structures to better track and utilize AMR levels.

Highlights

  • New AMR Refinement Mode: Introduced a 'Pseudo-Gradient Based' Adaptive Mesh Refinement (AMR) mode, allowing for more dynamic and intelligent grid refinement based on error gradients.
  • New AMR Test Examples: Added two new Python example scripts, sedo_amr.py and sod_amr_reflective.py, to demonstrate the application of AMR for Sedov blast wave and Sod shock tube problems, respectively.
  • 2:1 Refinement Rule Enforcement: Implemented and enforced the 2:1 refinement rule within the AMRGridRefinementHandler to maintain grid consistency and prevent large jumps in resolution between adjacent cells.
  • Solver Internal Data Updates: Modified the solver's internal storage to include a rho_primitive field and amr_block_levels, which are crucial for the new AMR logic and calculations.
  • Gradient Utility Functions: Added new utility functions like get_pseudo_grad and baryonic_normalized_slope_criterion to assist in calculating refinement criteria based on field gradients.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a new pseudo-gradient-based AMR refinement mode, including new PseudoGradientBased structs and related methods in SolverConfig.hpp and AMRGridRefinementHandler.hpp, along with corresponding Python bindings. It also adds new utility functions for pseudo-gradient calculation in SlopeLimitedGradientUtilities.hpp and a rho_primitive field to SolverStorage.hpp and Solver.cpp for use in the new AMR logic. The changes also include updates to ConsToPrimGas to handle this new field. Review comments highlight a critical bug where refine_list is moved multiple times, leading to undefined behavior, and several improvement opportunities such as removing unused parameters and redundant computations, clarifying confusing comments, addressing potential logical inconsistencies in err_min/err_max values, and suggesting more robust convergence checks for iteration loops in the AMR refinement process.

enforce_two_to_one_refinement(std::move(refine_list));

/////// enforce 2:1 for derefinement //////
enforce_two_to_one_derefinement(std::move(derefine_list), std::move(refine_list));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

critical

The refine_list is moved to enforce_two_to_one_refinement on line 1246 and then moved again to enforce_two_to_one_derefinement here. Moving an already moved object results in undefined behavior. If the content of refine_list is still needed after the first move, it should be passed by reference or copied.

using namespace sham;
using namespace sham::details;

auto get_avg_neigh = [&](auto &graph_links, u32 dir) -> T {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The u32 dir parameter in the get_avg_neigh lambda is unused. Please remove it to improve code clarity and avoid potential confusion.

Suggested change
auto get_avg_neigh = [&](auto &graph_links, u32 dir) -> T {
auto get_avg_neigh = [&](auto &graph_links) -> T {



def rhovel_map(rmin, rmax):
rho = rho_map(rmin, rmax)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The rho variable is calculated here but is not used within the rhovel_map function. This is a redundant computation and can be removed.

next_dt = model.evolve_once_override_time(t, dt)
if i == 0:
dic0 = convert_to_cell_coords(ctx.collect_data())
dX0.append(dic0["ymax"][i] - dic0["ymin"][i])
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

This line appends to dX0 inside a loop, but the condition if i == 0 means it will only execute once, causing dX0 to contain a single element repeated many times. If dX0 is meant to store initial cell sizes, it should be populated only once outside the loop, or its purpose needs clarification.

Comment on lines +282 to +293
auto get_coord = [](u32 i) -> std::array<u32, dim> {
constexpr u32 NsideBlockPow = 1;
constexpr u32 Nside = 1U << NsideBlockPow;
constexpr u32 side_size = Nside;
constexpr u32 block_size = shambase::pow_constexpr<dim>(Nside);

if constexpr (dim == 3) {
const u32 tmp = i >> NsideBlockPow;
// This line is why derefinement never happens
// return {i % Nside, (tmp) % Nside, (tmp ) >> NsideBlockPow};
return {(tmp) >> NsideBlockPow, (tmp) % Nside, i % Nside};
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The commented-out line // This line is why derefinement never happens is confusing. If this was a bug that has been fixed, the comment should be removed. If it's still a potential issue or a historical note, please clarify its meaning and current relevance.

// // // enforce 2:1 at parent level
// ///////////////////////////////////////////////////////////////////////////////////

for (int it = 0; it < 3; it++) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The for (int it = 0; it < 3; it++) loop uses a fixed number of iterations for convergence. Similar to the refinement loop, consider adding a convergence check or a more dynamic stopping condition for robustness.

u64 id_patch,
shamrock::patch::Patch p,
shamrock::patch::PatchDataLayer &pdat,
Tscal err_min,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The p parameter in the RefineCritPseudoGradientAccessor constructor is unused. Please remove it.

Suggested change
Tscal err_min,
u64 id_patch,

u64 id_patch,
shamrock::patch::Patch p,
shamrock::patch::PatchDataLayer &pdat,
Tscal err_min,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The p parameter in the RefineCritPseudoGradientAccessor::finalize method is unused. Please remove it.

Suggested change
Tscal err_min,
u64 id_patch,

Comment on lines +37 to +38
err_min = 0.25
err_max = 0.15
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Similar to sedo_amr.py, err_min (0.25) is greater than err_max (0.15). This configuration is unusual for refinement/derefinement thresholds and could lead to unexpected behavior. Please confirm if this is the intended logic.

@Akos299 Akos299 force-pushed the features/all-amr-dev branch from 0f59064 to cc83d20 Compare April 1, 2026 12:05
@Akos299 Akos299 force-pushed the features/all-amr-dev branch from cc83d20 to 9f73d96 Compare April 1, 2026 12:43
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 8, 2026

Workflow report

workflow report corresponding to commit 8cb10b7
Commiter email is leodasce.sewanou@ens-lyon.fr

Light CI is enabled. This will only run the basic tests and not the full tests.
Merging a PR require the job "on PR / all" to pass which is disabled in this case.

Pre-commit check report

Some failures were detected in base source checks checks.
Check the On PR / Linting / Base source checks (pull_request) job in the tests for more detailled output

❌ trailing-whitespace

Fixing examples/TO_MIGRATE/ramses/sod_amr_thesis.py

❌ ruff-format

1 file reformatted, 210 files left unchanged

Suggested changes

Detailed changes :
diff --git a/examples/TO_MIGRATE/ramses/sod_amr_thesis.py b/examples/TO_MIGRATE/ramses/sod_amr_thesis.py
index 42152416..2b3dacb7 100644
--- a/examples/TO_MIGRATE/ramses/sod_amr_thesis.py
+++ b/examples/TO_MIGRATE/ramses/sod_amr_thesis.py
@@ -11,6 +11,7 @@ ctx.pdata_layout_new()
 
 model = shamrock.get_Model_Ramses(context=ctx, vector_type="f64_3", grid_repr="i64_3")
 
+
 def run_case(nb_blocks, amr_lev, case="amr"):
     multx = 1
     multy = 1
@@ -31,11 +32,10 @@ def run_case(nb_blocks, amr_lev, case="amr"):
     cfg.set_boundary_condition("z", "reflective")
     cfg.set_riemann_solver_hllc()
 
-
     cfg.set_slope_lim_minmod()
     cfg.set_face_time_interpolation(True)
 
-    if(case == "amr"):
+    if case == "amr":
         err_min = 0.25
         err_max = 0.15
         cfg.set_amr_mode_pseudo_gradient_based(error_min=err_min, error_max=err_max)
@@ -47,7 +47,6 @@ def run_case(nb_blocks, amr_lev, case="amr"):
         (0, 0, 0), (cell_size, cell_size, cell_size), (base * multx, base * multy, base * multz)
     )
 
-
     def rho_map(rmin, rmax):
         x, y, z = rmin
         if x < 0.5:
@@ -55,11 +54,9 @@ def run_case(nb_blocks, amr_lev, case="amr"):
         else:
             return 0.125
 
-
     etot_L = 1.0 / (gamma - 1)
     etot_R = 0.1 / (gamma - 1)
 
-
     def rhoetot_map(rmin, rmax):
         x, y, z = rmin
         if x < 0.5:
@@ -67,16 +64,13 @@ def run_case(nb_blocks, amr_lev, case="amr"):
         else:
             return etot_R
 
-
     def rhovel_map(rmin, rmax):
         return (0, 0, 0)
 
-
     model.set_field_value_lambda_f64("rho", rho_map)
     model.set_field_value_lambda_f64("rhoetot", rhoetot_map)
     model.set_field_value_lambda_f64_3("rhovel", rhovel_map)
 
-
     def convert_to_cell_coords(dic):
 
         cmin = dic["cell_min"]
@@ -117,7 +111,6 @@ def run_case(nb_blocks, amr_lev, case="amr"):
 
         return dic
 
-
     t_target = 0.245
 
     dt = 0
@@ -198,20 +191,22 @@ def run_case(nb_blocks, amr_lev, case="amr"):
 
         ## density
         ax00_1 = axs[0, 0]
-        if(case == "amr"):
+        if case == "amr":
             ax00_2 = ax00_1.twinx()
             ax00_2.set_ylim(np.floor(l.min()), np.ceil(l.max()))  # optional but important
             ax00_2.yaxis.set_major_locator(ticker.MultipleLocator(1))
             ax00_2.yaxis.set_major_formatter(ticker.FormatStrFormatter("%d"))
 
-        ax00_1.scatter(X, rho, rasterized=True, s=12 * np.ones(X.shape), color="red", label="numeric")
+        ax00_1.scatter(
+            X, rho, rasterized=True, s=12 * np.ones(X.shape), color="red", label="numeric"
+        )
         ax00_1.plot(arr_x, arr_rho, ls="--", lw=2.0, color="black", label="exact")
         ax00_1.set_xlabel("$X$")
         ax00_1.set_ylabel("density")
         ax00_1.legend(loc=0)
         ax00_1.grid()
 
-        if(case == "amr"):
+        if case == "amr":
             idx = np.argsort(X)
             ax00_2.plot(X[idx], l[idx], color="purple", marker="*", linewidth=1.0, ls="-.")
             ax00_2.set_ylabel("AMR level")
@@ -219,7 +214,7 @@ def run_case(nb_blocks, amr_lev, case="amr"):
 
         ## velocity
         ax01_1 = axs[0, 1]
-        if(case=="amr"):
+        if case == "amr":
             ax01_2 = ax01_1.twinx()
             ax01_2.set_ylim(np.floor(l.min()), np.ceil(l.max()))  # optional but important
             ax01_2.yaxis.set_major_locator(ticker.MultipleLocator(1))
@@ -232,7 +227,7 @@ def run_case(nb_blocks, amr_lev, case="amr"):
         ax01_1.legend(loc=0)
         ax01_1.grid()
 
-        if(case=="amr"):
+        if case == "amr":
             idx = np.argsort(X)
             ax01_2.plot(X[idx], l[idx], color="purple", marker="*", linewidth=1.0, ls="-.")
             ax01_2.set_ylabel("AMR level")
@@ -240,7 +235,7 @@ def run_case(nb_blocks, amr_lev, case="amr"):
 
         ## pressure
         ax11_1 = axs[1, 1]
-        if(case =="amr"):
+        if case == "amr":
             ax11_2 = ax11_1.twinx()
             ax11_2.set_ylim(np.floor(l.min()), np.ceil(l.max()))  # optional but important
             ax11_2.yaxis.set_major_locator(ticker.MultipleLocator(1))
@@ -254,8 +249,8 @@ def run_case(nb_blocks, amr_lev, case="amr"):
         ax11_1.set_ylabel("pressure")
         ax11_1.legend(loc=0)
         ax11_1.grid()
-        
-        if(case == "amr"):
+
+        if case == "amr":
             idx = np.argsort(X)
             ax11_2.plot(X[idx], l[idx], color="purple", marker="*", linewidth=1.0, ls="-.")
             ax11_2.set_ylabel("AMR level")
@@ -263,14 +258,19 @@ def run_case(nb_blocks, amr_lev, case="amr"):
 
         ## internal energy
         ax10_1 = axs[1, 0]
-        if(case=="amr"):
+        if case == "amr":
             ax10_2 = ax10_1.twinx()
             ax10_2.set_ylim(np.floor(l.min()), np.ceil(l.max()))  # optional but important
             ax10_2.yaxis.set_major_locator(ticker.MultipleLocator(1))
             ax10_2.yaxis.set_major_formatter(ticker.FormatStrFormatter("%d"))
 
         ax10_1.scatter(
-            X, internal_energy, rasterized=True, s=12 * np.ones(X.shape), color="red", label="numeric"
+            X,
+            internal_energy,
+            rasterized=True,
+            s=12 * np.ones(X.shape),
+            color="red",
+            label="numeric",
         )
         ax10_1.plot(
             arr_x, arr_P / (arr_rho * (gamma - 1.0)), ls="--", lw=2.0, color="black", label="exact"
@@ -280,7 +280,7 @@ def run_case(nb_blocks, amr_lev, case="amr"):
         ax10_1.legend(loc=0)
         ax10_1.grid()
 
-        if(case=="amr"):
+        if case == "amr":
             idx = np.argsort(X)
             ax10_2.plot(X[idx], l[idx], color="purple", marker="*", linewidth=1.0, ls="-.")
             ax10_2.set_ylabel("AMR level")
@@ -289,4 +289,4 @@ def run_case(nb_blocks, amr_lev, case="amr"):
         plt.savefig(f"sod-{case}.pdf")
 
 
-run_case(16, 3,"uniform")
+run_case(16, 3, "uniform")

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants