Skip to content

Update to 3D #960

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 12 commits into
base: master
Choose a base branch
from
Draft

Update to 3D #960

wants to merge 12 commits into from

Conversation

nicolasaunai
Copy link
Member

@nicolasaunai nicolasaunai commented Feb 20, 2025

updates PHARE to 3D
final PR description is TBD
puts back the level ghost refinement field advance test : #748

@nicolasaunai nicolasaunai added this to the PHARE 3D milestone Feb 20, 2025
Copy link

coderabbitai bot commented Feb 20, 2025

📝 Walkthrough

Walkthrough

The changes span core build configurations, library functionality, AMR and particle refinement, and test infrastructure. Updates include modifications to CMake workflows, additions of new methods and classes to support 3D operations (e.g., mesh coordinate generation, ghost box handling, and dispatcher-to-pattern renaming), and enhanced error handling. Several new configuration files and test suites have been added to validate 3D simulations, while existing tests have been updated (e.g., removal of skip decorators). Minor documentation and formatting improvements are also introduced throughout the codebase.

Changes

File(s) Change Summary
.github/workflows/cmake_macos.yml, res/cmake/funcs.cmake, res/cmake/options.cmake, tools/python3/cmake.py, tools/python3/git.py Updates to CI/CD workflows and CMake build/test utilities; added new options (e.g., highResourceTests) and Python modules for Git and CMake operations.
pyphare/pyphare/core/box.py, pyphare/pyphare/core/gridlayout.py, pyphare/pyphare/pharesee/geometry.py, pyphare/pyphare/pharesee/hierarchy/hierarchy.py, pyphare/pyphare/pharesee/run/utils.py, src/core/data/grid/gridlayoutdefs.hpp, src/core/data/ndarray/ndarray_vector.hpp, src/core/utilities/box/box.hpp, src/core/utilities/meta/meta_utilities.hpp, src/core/data/particles/particle.hpp Core library enhancements to support 3D: new methods (e.g., remove_all, select, meshCoords, zstart, zend, fill3D), improved ghost box and periodicity handling, updated templates and member initializations, and enhanced documentation.
src/amr/data/field/coarsening/coarsen_weighter.hpp, src/amr/data/field/coarsening/magnetic_field_coarsener.hpp, src/amr/data/particles/refine/particles_data_split.hpp, src/amr/data/particles/refine/split.hpp, src/amr/data/particles/refine/split_1d.hpp, src/amr/data/particles/refine/split_2d.hpp, src/amr/data/particles/refine/split_3d.hpp, src/amr/data/particles/refine/splitter.hpp Refactoring of AMR and particle refinement: added new overloads for 1D/2D/3D splitting, renamed dispatcher structures to pattern classes, integrated additional include directives, and improved inline documentation.
res/amr/splitting.yml, tests/amr/data/particles/refine/input/input_3d_ratio_2.txt, tests/functional/harris/harris_3d.py New configuration and simulation setup files to define 3D grid geometry and plasma simulation parameters.
(Numerous test files under tests/) Extensive test updates for 3D functionality: new test suites and files for masked views, interpolation, field/particle advancement and initialization, removal of skip decorators, improved error handling for unsupported 3D cases, and new diagnostic test scripts.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Caller
    participant Dispatcher as PatternDispatcher
    participant Pattern as Pattern Object
    Client->>Dispatcher: Invoke operator() with coarse particle info
    Dispatcher->>Dispatcher: Call dispatch_ method
    loop For each pattern
        Dispatcher->>Pattern: Retrieve weight and delta values
        Pattern-->>Dispatcher: Return pattern details
    end
    Dispatcher-->>Client: Return refined particle attributes
Loading
sequenceDiagram
    participant User
    participant PH as PatchHierarchy
    User->>PH: Call plot()
    alt ndim equals 3
        PH-->>User: Raise NotImplementedError("cannot make 3D plots")
    else
        PH-->>User: Proceed with plotting operations
    end
Loading

Suggested labels

feature, refactoring, test, python, pharesee

Suggested reviewers

  • PhilipDeegan
✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai plan to trigger planning for file edits and PR creation.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@nicolasaunai nicolasaunai marked this pull request as draft February 20, 2025 15:39
@nicolasaunai
Copy link
Member Author

on top of #591

@@ -0,0 +1,48 @@
from tools.python3 import decode_bytes, run
Copy link
Member

Choose a reason for hiding this comment

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

this file doesn't exist on my branch anymore

https://github.com/PhilipDeegan/PHARE/tree/3d_base/tools/python3

Copy link
Member Author

Choose a reason for hiding this comment

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

ok good to know, I think I will use meld to see the diff more easily.

Copy link

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

🔭 Outside diff range comments (1)
tools/python3/cmake.py (1)

47-49: ⚠️ Potential issue

Fix undefined variable in run_test function.

The run_test function uses an undefined variable cmd. This will cause a runtime error.

Apply this diff to fix the issue:

-def run_test(test, verbose=False, capture_output=False):
-    run(test_cmd(cmd, verbose=verbose), capture_output=capture_output)
+def run_test(test, verbose=False, capture_output=False):
+    run(test_cmd(test, verbose=verbose), capture_output=capture_output)
🧰 Tools
🪛 Ruff (0.8.2)

48-48: Undefined name cmd

(F821)

🧹 Nitpick comments (34)
src/amr/data/particles/refine/split_3d.hpp (3)

43-63: Leverage descriptive function names or inline comments for clarity.

In PurplePattern<DimConst<3>>, each constructor loop sets corner-based offsets for the deltas. Consider more explicit naming for variables or comments to describe how each coordinate is derived. This helps maintainers quickly grasp the connection between i % 2 ? -1 : 1 and corner offsets, especially in 3D splits.


90-114: Check class vs. struct consistency for pattern types.

WhitePattern<DimConst<3>> is declared as a class, whereas other patterns (PinkPattern, PurplePattern, LimePattern) are declared as structs. Functionally, this is not an error because everything is public, but for consistency and readability, you might consider using struct here or explaining why class is intended.


164-174: Consider consolidating repeated pattern dispatchers.

Splitter<DimConst<3>, InterpConst<1>, RefinedParticlesConst<27>> references four pattern types (BlackPattern, PinkPattern, PurplePattern, LimePattern). If multiple Splitter specializations frequently combine the same patterns, you could introduce a shared alias or central definition to reduce duplication and ease maintenance if new patterns or logic are introduced later.

src/amr/data/field/coarsening/magnetic_field_coarsener.hpp (1)

226-333: Keep 3D coarsening logic maintainable.

The 3D coarsen method uses multiple nested conditionals to handle primal/dual layout combinations for each B-component. While comprehensive, it can become a maintenance burden if new layouts or partial-edge cases appear. Extracting these computations into well-named helper functions or a small lookup table can aid clarity and reduce future duplication across the 1D/2D/3D variants.

tests/amr/data/field/coarsening/coarsening_utils.py (2)

25-37: Enhance variable naming for self-documentation.

In lines 25-37, the usage of is_primal and simple helper functions (coarse_indices, fineLocal, coarseLocal) is clear, but variable names like index, indexX, and indexY could be more descriptive. For instance, consider naming them coarseX, fineX, etc., to highlight the difference between coarse and fine indexing.


181-182: Flatten nested conditionals per static analysis (SIM102).

Ruff suggests merging these nested checks:

elif not is_primal[0] and is_primal[1] and not is_primal[2]:
    if qty == "By":
        ...

into a single condition:

-        elif not is_primal[0] and is_primal[1] and not is_primal[2]:
-            if qty == "By":
+        elif (not is_primal[0]
+              and is_primal[1]
+              and not is_primal[2]
+              and qty == "By"):

This eliminates a layer of nesting and aligns with SIM102.

🧰 Tools
🪛 Ruff (0.8.2)

181-182: Use a single if statement instead of nested if statements

(SIM102)

pyphare/pyphare/pharesee/geometry.py (5)

63-74: Improve clarity for naming 3D faces.
These lines add left, right, bottom, top, front, and back boxes for 3D domains. It might be helpful to add comments explaining which physical dimension corresponds to "front" and "back" to avoid confusion when reading or extending the code.


87-88: Clarify the re-initialization of shifts.
Re-initializing shifts to an empty dictionary for all dimensions is fine, but consider adding a brief comment describing why it's reset here for better maintainability.


100-102: Check dimension branching logic.
Explicitly handling ndim == 3 and then grouping ndim > 1 in the subsequent block can be confusing if future expansions occur. Consider restructuring or adding comments to differentiate 2D vs. 3D handling more distinctly.


150-150: Consider naming consistency for combined shift keys.
The new key "leftrightbottomtop" may be difficult to parse. A more structured or descriptive naming strategy (e.g., "composite_shifts_lr_bt") might be clearer for maintainers.


161-180: Rename loop variables for readability.
The static analysis hints mention ambiguous variable names (e.g., l). Variable naming such as (key, shiftList) instead of (k, l) helps readability. Also, ensure that the newly added front, back, and combined shift logic is well-documented to avoid confusion for new contributors.

🧰 Tools
🪛 Ruff (0.8.2)

161-161: Ambiguous variable name: l

(E741)


164-164: Ambiguous variable name: l

(E741)


167-167: Ambiguous variable name: l

(E741)

src/core/data/ndarray/ndarray_vector.hpp (2)

386-425: Evaluate performance in triple-nested fill3D loops.
This expanded logic for 3D borders is straightforward but potentially costly. If performance becomes critical, consider batched or SIMD approaches. Otherwise, the clarity of straightforward nested loops is acceptable.


529-595: Comprehensive 3D copy logic in operator>>.
The new block handles 3D corner and edge copying. The approach is thorough but quite extensive. Consider extracting it into a helper function for readability and easier testing.

tests/diagnostic/job_3d.py.in (1)

3-14: Validate 3D diagnostic workflow.
This new script sets up a 3D domain, defines an isothermal electron model, and calls dump_all_diags. It's a solid start for 3D diagnostic tests. Consider adding assertions or checks in the script to confirm that the output data is generated as expected, boosting test reliability.

tools/python3/git.py (1)

19-24: Remove unused exception variable.

The function logic is correct, but the exception variable is unused.

Apply this diff to fix the issue:

 def branch_exists(branch):
     try:
         run(f"git show-branch {branch}", check=True)
-    except subprocess.CalledProcessError as e:
+    except subprocess.CalledProcessError:
         return False  # exit failure means branch does not exist
     return True
🧰 Tools
🪛 Ruff (0.8.2)

22-22: Local variable e is assigned to but never used

Remove assignment to unused variable e

(F841)

tools/python3/cmake.py (1)

4-5: Implement the version() function.

The version() function is currently empty. Consider implementing it to return the CMake version for compatibility checks.

Would you like me to help implement the version function?

tests/simulator/initialize/test_fields_init_3d.py (1)

40-45: Address high RAM usage in density test.

The comment indicates that this test uses excessive RAM. Consider:

  1. Reducing the test matrix size
  2. Adding memory usage checks
  3. Making it an optional heavy test

You could add memory usage checks using this script:

#!/usr/bin/env python3
import psutil
import numpy as np

def estimate_memory(cells, ppc_array):
    # Estimate memory usage for 3D grid with particles
    max_ppc = np.max(ppc_array)
    grid_points = cells ** 3
    particles = grid_points * max_ppc
    memory_bytes = particles * (8 * 6)  # 6 doubles per particle (position + velocity)
    return memory_bytes / (1024 * 1024)  # Convert to MB

cells = 20
ppc_array = np.asarray([10, 25, 50, 75])
estimated_mb = estimate_memory(cells, ppc_array)
print(f"Estimated memory usage: {estimated_mb:.2f} MB")
src/core/data/grid/gridlayoutdefs.hpp (1)

5-5: LGTM! Enhanced type safety with fixed-width integer.

The changes improve type safety and ABI stability by:

  1. Including the necessary header for fixed-width integers
  2. Specifying a fixed-width underlying type for the enum class

Consider adding static assertions to validate the enum's size requirements:

 enum class QtyCentering : std::uint16_t { primal = 0, dual = 1 };
+static_assert(sizeof(QtyCentering) == sizeof(std::uint16_t), "QtyCentering must be 16 bits");

Also applies to: 18-18

tests/simulator/initialize/test_particles_init_3d.py (1)

77-84: Implement the missing patch ghost particle tests.

The TODO3D comment indicates that patch ghost particle tests need to be implemented for 3D scenarios.

Would you like me to help implement these tests or create an issue to track this task?

tests/simulator/per_test.hpp (1)

111-117: Consider expanding 3D test parameters for better coverage.

The 3D test parameters are currently limited to 3 configurations with a fixed nbRefinePart=6, while 1D and 2D tests have more varied configurations. Consider adding more test cases with different nbRefinePart values for better coverage.

Example expansion:

 using Simulator3d = testing::Types<SimulatorTestParam<3, 1, 6>, SimulatorTestParam<3, 2, 6>,
-                                  SimulatorTestParam<3, 3, 6>>;
+                                  SimulatorTestParam<3, 3, 6>,
+                                  SimulatorTestParam<3, 1, 8>, SimulatorTestParam<3, 2, 8>,
+                                  SimulatorTestParam<3, 3, 8>>;
tests/simulator/advance/test_fields_advance_3d.py (1)

18-18: Consider increasing particles per cell for better accuracy.

The ppc value is set to 10, which is lower than the 2D tests (25). Consider increasing this value for better statistical accuracy in particle-based calculations.

-ppc, cells = 10, 30
+ppc, cells = 25, 30  # Match 2D test parameters for consistency
src/core/utilities/meta/meta_utilities.hpp (1)

90-97: Consider completing the TODO for 3D particle permutations.

The TODO comment indicates that additional 3D particle permutations need to be added.

Would you like me to help implement the remaining 3D particle permutations or create an issue to track this task?

src/amr/data/particles/refine/splitter.hpp (1)

145-163: Consider implementing empty pattern structs.

The pattern structs are currently empty. They should be implemented to define their specific splitting behaviors.

Would you like me to help implement these pattern structs or create an issue to track this task?

tests/functional/harris/harris_3d.py (3)

62-63: Rename ambiguous variable l for better readability.

The single-letter variable name l is ambiguous and could be confused with the number 1 or uppercase I.

-def S(y, y0, l):
-    return 0.5 * (1.0 + np.tanh((y - y0) / l))
+def S(y, y0, length):
+    return 0.5 * (1.0 + np.tanh((y - y0) / length))
🧰 Tools
🪛 Ruff (0.8.2)

62-62: Ambiguous variable name: l

(E741)


177-188: Address TODO comments regarding 3D plotting.

The plotting functionality needs to be reimplemented after the removal of hierarchy plot3d.

Would you like me to help implement an alternative 3D plotting solution or create an issue to track this task?


106-108: Consider adding error handling for temperature calculation.

The temperature calculation uses an assertion to check for positive values, but it might be better to handle this case more gracefully.

-        temp = 1.0 / density(x, y, z) * (K - b2(x, y, z) * 0.5)
-        assert np.all(temp > 0)
-        return temp
+        temp = 1.0 / density(x, y, z) * (K - b2(x, y, z) * 0.5)
+        if not np.all(temp > 0):
+            raise ValueError("Temperature calculation resulted in non-positive values")
+        return temp
tests/simulator/refined_particle_nbr.py (1)

140-145: Consider adding test suite ordering documentation.

The test suite execution order is enforced but not documented. Consider adding a comment explaining why test_values must run first.

-  # the following ensures the order of execution so delta/weights are verified first
+  # test_values must run first to verify delta/weights before dimension-specific tests
+  # because it validates the core splitting parameters used by all other tests
pyphare/pyphare/core/box.py (1)

206-207: Use more descriptive variable names.

The variable name 'l' is ambiguous and could be confused with the number '1'.

-def select(data, box):
-    return data[tuple([slice(l, u + 1) for l,u in zip(box.lower, box.upper)])]
+def select(data, box):
+    return data[tuple([slice(lower, upper + 1) for lower, upper in zip(box.lower, box.upper)])]
🧰 Tools
🪛 Ruff (0.8.2)

207-207: Ambiguous variable name: l

(E741)

res/cmake/funcs.cmake (1)

107-108: Consider documenting empty function purpose.

The empty add_phare_build_test function lacks documentation explaining its purpose or future use.

+  # Placeholder for future build-time test functionality
   function(add_phare_build_test binary directory)
   endfunction(add_phare_build_test)
pyphare/pyphare/core/gridlayout.py (1)

369-382: Add docstring to the meshCoords method.

The method implementation is correct, but it lacks documentation explaining its purpose, parameters, and return values.

Add a docstring to improve code maintainability:

 def meshCoords(self, qty):
+    """
+    Compute mesh coordinates for a given quantity in 1D, 2D, or 3D.
+    
+    Args:
+        qty: The quantity for which to compute coordinates
+        
+    Returns:
+        For 1D: array of x coordinates
+        For 2D: array of shape (nx, ny, 2) containing (x,y) coordinates
+        For 3D: array of shape (nx, ny, nz, 3) containing (x,y,z) coordinates
+    """
tests/core/numerics/interpolator/test_main.cpp (1)

722-787: Test suite for 3D particle weight deposition needs to be uncommented.

The test case DepositCorrectlyTheirWeight_3d is currently commented out. Consider implementing and enabling the test to ensure proper 3D functionality.

Would you like me to help implement the test case? The implementation would be similar to the existing 1D and 2D test cases but adapted for 3D coordinates.

tests/simulator/test_initialization.py (1)

427-428: Consider adjusting particle numbers for better 3D performance.

The number of particles for 3D (20) seems quite low compared to 1D (10000) and 2D (1000). This might affect the accuracy of the simulation.

Consider increasing the number of particles for 3D cases if performance allows:

-        nbParts = {1: 10000, 2: 1000, 3: 20}
+        nbParts = {1: 10000, 2: 1000, 3: 100}  # Increased 3D particles
tests/simulator/test_advance.py (1)

290-290: Consider renaming unused loop variable.

The loop control variable ilvl is not used within the loop body.

-        for ilvl, overlaps in hierarchy_overlaps(datahier, coarsest_time).items():
+        for _ilvl, overlaps in hierarchy_overlaps(datahier, coarsest_time).items():
🧰 Tools
🪛 Ruff (0.8.2)

290-290: Loop control variable ilvl not used within loop body

Rename unused ilvl to _ilvl

(B007)

res/amr/splitting.yml (1)

102-142: Introducing 3D Particle Splitting Parameters

The new dimension_3 section is well structured and mirrors the formatting used in the existing dimensions. It clearly defines three interpolations (interp_1, interp_2, and interp_3) with specific delta and weight values for each particle configuration.

A few points to consider:

  • YAML list representation: The weight fields are specified as space-separated values (e.g., .125 .0625). Ensure that the YAML parser or the custom configuration loader interprets these values correctly. If any ambiguity is encountered, consider using a standard YAML list format (e.g., [.125, .0625]) for enhanced clarity and maintainability.
  • Value precision: Double-check that the differing precisions on weight (for example, .099995 .055301 in interp_2 versus .104047 .05564 in interp_3) are intentional and calibrated for your 3D simulation requirements.
  • Integration Validation: Please verify that all downstream modules (e.g., particle refinement in 3D, ghost field computations) are updated to correctly consume these new parameters and that relevant tests are in place to confirm the expected behavior.
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3cfdddb and b69e607.

📒 Files selected for processing (68)
  • .github/workflows/cmake_macos.yml (1 hunks)
  • pyphare/pyphare/core/box.py (2 hunks)
  • pyphare/pyphare/core/gridlayout.py (1 hunks)
  • pyphare/pyphare/pharesee/geometry.py (7 hunks)
  • pyphare/pyphare/pharesee/hierarchy/hierarchy.py (1 hunks)
  • pyphare/pyphare/pharesee/run/utils.py (1 hunks)
  • res/amr/splitting.yml (1 hunks)
  • res/cmake/funcs.cmake (1 hunks)
  • res/cmake/options.cmake (2 hunks)
  • src/amr/data/field/coarsening/coarsen_weighter.hpp (1 hunks)
  • src/amr/data/field/coarsening/magnetic_field_coarsener.hpp (1 hunks)
  • src/amr/data/particles/refine/particles_data_split.hpp (2 hunks)
  • src/amr/data/particles/refine/split.hpp (1 hunks)
  • src/amr/data/particles/refine/split_1d.hpp (10 hunks)
  • src/amr/data/particles/refine/split_2d.hpp (17 hunks)
  • src/amr/data/particles/refine/split_3d.hpp (1 hunks)
  • src/amr/data/particles/refine/splitter.hpp (5 hunks)
  • src/core/data/grid/grid.hpp (1 hunks)
  • src/core/data/grid/gridlayoutdefs.hpp (2 hunks)
  • src/core/data/ndarray/ndarray_vector.hpp (8 hunks)
  • src/core/utilities/box/box.hpp (1 hunks)
  • src/core/utilities/meta/meta_utilities.hpp (2 hunks)
  • tests/amr/data/field/coarsening/CMakeLists.txt (0 hunks)
  • tests/amr/data/field/coarsening/coarsening_utils.py (1 hunks)
  • tests/amr/data/field/coarsening/input/input_1d_ratio_10.txt (0 hunks)
  • tests/amr/data/field/coarsening/input/input_1d_ratio_2.txt (0 hunks)
  • tests/amr/data/field/coarsening/input/input_1d_ratio_3.txt (0 hunks)
  • tests/amr/data/field/coarsening/input/input_1d_ratio_4.txt (0 hunks)
  • tests/amr/data/field/coarsening/input/input_1d_ratio_6.txt (0 hunks)
  • tests/amr/data/field/coarsening/input/input_1d_ratio_7.txt (0 hunks)
  • tests/amr/data/field/coarsening/input/input_1d_ratio_8.txt (0 hunks)
  • tests/amr/data/field/coarsening/input/input_1d_ratio_9.txt (0 hunks)
  • tests/amr/data/field/coarsening/input_config.h.in (0 hunks)
  • tests/amr/data/field/coarsening/test_coarsening_main.cpp (0 hunks)
  • tests/amr/data/field/coarsening/test_linear_coarsen.hpp (0 hunks)
  • tests/amr/data/field/coarsening/test_weighter_coarsen.hpp (1 hunks)
  • tests/amr/data/field/refine/CMakeLists.txt (1 hunks)
  • tests/amr/data/field/refine/test_default_field_refinement.cpp (4 hunks)
  • tests/amr/data/field/refine/test_field_refinement_on_hierarchy.cpp (1 hunks)
  • tests/amr/data/particles/refine/input/input_3d_ratio_2.txt (2 hunks)
  • tests/amr/data/particles/refine/test_split.cpp (1 hunks)
  • tests/core/data/ndarray/test_main.cpp (1 hunks)
  • tests/core/numerics/interpolator/test_main.cpp (1 hunks)
  • tests/diagnostic/CMakeLists.txt (1 hunks)
  • tests/diagnostic/job_3d.py.in (1 hunks)
  • tests/diagnostic/test-diagnostics_3d.cpp (1 hunks)
  • tests/functional/harris/harris_3d.py (1 hunks)
  • tests/simulator/CMakeLists.txt (1 hunks)
  • tests/simulator/__init__.py (3 hunks)
  • tests/simulator/advance/CMakeLists.txt (1 hunks)
  • tests/simulator/advance/test_fields_advance_1d.py (1 hunks)
  • tests/simulator/advance/test_fields_advance_2d.py (1 hunks)
  • tests/simulator/advance/test_fields_advance_3d.py (1 hunks)
  • tests/simulator/advance/test_particles_advance_3d.py (1 hunks)
  • tests/simulator/initialize/CMakeLists.txt (1 hunks)
  • tests/simulator/initialize/test_fields_init_2d.py (1 hunks)
  • tests/simulator/initialize/test_fields_init_3d.py (1 hunks)
  • tests/simulator/initialize/test_particles_init_1d.py (0 hunks)
  • tests/simulator/initialize/test_particles_init_2d.py (1 hunks)
  • tests/simulator/initialize/test_particles_init_3d.py (1 hunks)
  • tests/simulator/per_test.hpp (1 hunks)
  • tests/simulator/refined_particle_nbr.py (2 hunks)
  • tests/simulator/test_advance.py (15 hunks)
  • tests/simulator/test_initialization.py (13 hunks)
  • tests/simulator/utilities/field_coarsening.py (0 hunks)
  • tests/simulator/utilities/test_refine_field.py (2 hunks)
  • tools/python3/cmake.py (1 hunks)
  • tools/python3/git.py (1 hunks)
💤 Files with no reviewable changes (14)
  • tests/amr/data/field/coarsening/input_config.h.in
  • tests/amr/data/field/coarsening/input/input_1d_ratio_4.txt
  • tests/amr/data/field/coarsening/input/input_1d_ratio_3.txt
  • tests/amr/data/field/coarsening/test_coarsening_main.cpp
  • tests/amr/data/field/coarsening/input/input_1d_ratio_7.txt
  • tests/amr/data/field/coarsening/input/input_1d_ratio_9.txt
  • tests/simulator/initialize/test_particles_init_1d.py
  • tests/amr/data/field/coarsening/input/input_1d_ratio_6.txt
  • tests/amr/data/field/coarsening/input/input_1d_ratio_10.txt
  • tests/amr/data/field/coarsening/input/input_1d_ratio_2.txt
  • tests/amr/data/field/coarsening/CMakeLists.txt
  • tests/amr/data/field/coarsening/input/input_1d_ratio_8.txt
  • tests/simulator/utilities/field_coarsening.py
  • tests/amr/data/field/coarsening/test_linear_coarsen.hpp
✅ Files skipped from review due to trivial changes (6)
  • src/amr/data/particles/refine/split.hpp
  • src/core/data/grid/grid.hpp
  • src/amr/data/field/coarsening/coarsen_weighter.hpp
  • src/core/utilities/box/box.hpp
  • src/amr/data/particles/refine/particles_data_split.hpp
  • src/amr/data/particles/refine/split_2d.hpp
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.hpp`: Review the C++ code, point out issues relative t...

**/*.hpp: Review the C++ code, point out issues relative to principles of clean code, expressiveness, and performance.

  • src/core/data/grid/gridlayoutdefs.hpp
  • src/core/utilities/meta/meta_utilities.hpp
  • tests/simulator/per_test.hpp
  • src/core/data/ndarray/ndarray_vector.hpp
  • tests/amr/data/field/coarsening/test_weighter_coarsen.hpp
  • src/amr/data/particles/refine/split_3d.hpp
  • src/amr/data/particles/refine/splitter.hpp
  • src/amr/data/field/coarsening/magnetic_field_coarsener.hpp
  • src/amr/data/particles/refine/split_1d.hpp
🪛 Ruff (0.8.2)
tools/python3/git.py

22-22: Local variable e is assigned to but never used

Remove assignment to unused variable e

(F841)


29-29: Undefined name delete_branch

(F821)

tests/simulator/__init__.py

326-326: Do not use mutable data structures for argument defaults

Replace with None; initialize within function

(B006)

pyphare/pyphare/core/box.py

207-207: Ambiguous variable name: l

(E741)

tests/functional/harris/harris_3d.py

62-62: Ambiguous variable name: l

(E741)

tools/python3/cmake.py

48-48: Undefined name cmd

(F821)

pyphare/pyphare/pharesee/geometry.py

161-161: Ambiguous variable name: l

(E741)


164-164: Ambiguous variable name: l

(E741)


167-167: Ambiguous variable name: l

(E741)


188-188: Ambiguous variable name: l

(E741)

tests/simulator/test_advance.py

290-290: Loop control variable ilvl not used within loop body

Rename unused ilvl to _ilvl

(B007)

tests/amr/data/field/coarsening/coarsening_utils.py

181-182: Use a single if statement instead of nested if statements

(SIM102)

tests/simulator/test_initialization.py

394-394: Function definition does not bind loop variable select

(B023)


396-396: Function definition does not bind loop variable patch

(B023)


396-396: Function definition does not bind loop variable nbrGhosts

(B023)


397-397: Function definition does not bind loop variable select

(B023)


582-582: Loop control variable pi not used within loop body

Rename unused pi to _pi

(B007)


586-586: Yoda condition detected

Rewrite as ppc == H

(SIM300)


664-664: Local variable out is assigned to but never used

Remove assignment to unused variable out

(F841)

🪛 Cppcheck (2.10-2)
tests/diagnostic/test-diagnostics_3d.cpp

[performance] 24-24: Function parameter 'path' should be passed by const reference.

(passedByValue)

⏰ Context from checks skipped due to timeout of 90000ms (5)
  • GitHub Check: build (macos-latest)
  • GitHub Check: Analyze (cpp)
  • GitHub Check: build (macos-14)
  • GitHub Check: build (ubuntu-latest)
  • GitHub Check: build (macos-13)
🔇 Additional comments (65)
src/amr/data/particles/refine/split_3d.hpp (1)

19-41: Ensure index usage remains within bounds.

In PinkPattern<DimConst<3>>, you are using for (std::size_t i = 0; i < 2; i++) and then calculating std::size_t offset = i * 3. Since RefinedParticlesConst<6> makes exactly six elements, the usage of offsets [0..5] should be safe. If any future refactor changes the base class template parameters (e.g., if you introduce more or fewer refined particles), remember to revisit these loops to prevent out-of-range writes.

src/amr/data/field/coarsening/magnetic_field_coarsener.hpp (2)

33-36: Clarify assumptions about Yee layout.

Lines 33-36 explicitly warn about relying on Yee layout centering. Though this is correct for typical magnetic field allocations, consider providing a safeguard check or static assertion to confirm that centering_ matches the expected pattern. This ensures that future developers don’t mistakenly pass other layouts to this class.


65-79: Validate indexing in fine_start_index().

The function multiplies coarseIndex by ratio_ and then performs an AMR index transformation. Ensure that each dimension of sourceBox_ truly accommodates these multiplied indices before usage, especially for corner or boundary cells where coarseIndex might push the resulting index out of range.

pyphare/pyphare/pharesee/geometry.py (3)

59-60: Ensure domain boundaries for 2D dimensions.
These lines correctly add bottom and top Box definitions for 2D. Make sure that (0, 0) as a lower bound is intended for all 2D scenarios, as any negative or shifted domain might require a different approach.


184-188: Evaluate the dictionary key transformation.
The assertion ensures no duplication in sorted shift keys, and the subsequent line replaces keys with "".join(sorted(k)). While this is functionally clever, it can reduce clarity if keys become cryptic. Document this transformation or consider an alternative, more explicit approach.

🧰 Tools
🪛 Ruff (0.8.2)

188-188: Ambiguous variable name: l

(E741)


290-290: Confirm sorted usage of patch border keys.
Constructing the in_sides string by sorting the patch border keys is consistent with the shift dictionary logic. However, watch out for subtle discrepancies if border naming conventions change in the future.

src/core/data/ndarray/ndarray_vector.hpp (3)

123-125: Document new 3D boundary helpers.
The newly added zstart() and zend() methods improve consistency with the x and y boundaries. Adding short docstrings can help future contributors understand how these methods interact with the 3D fill operations.


348-353: Ensure validity for 1D fill loops.
The loops correctly fill the left and right masks in a 1D array from min_ to max_ and from shape[0] - 1 - max_ to shape[0] - 1 - min_. Double-check that min_ <= max_ is always guaranteed to avoid an empty or unintended range.


469-470: Double-check assertion coverage for boundary offsets.
These new asserts verify inner is strictly within outer in x and y dimensions. Confirm that the rest of the code cannot pass invalid or equal boundary values.

Also applies to: 477-480

tests/diagnostic/job_3d.py.in (1)

1-2: Confirm environment for Python 3.
Using a shebang with /usr/bin/env python3 is a best practice. Ensure that your CI or local environment consistently has Python 3 available under this path.

tests/diagnostic/test-diagnostics_3d.cpp (3)

1-7: LGTM!

The includes and static constants are well-defined and follow the naming convention.


9-27: LGTM!

The test functions are well-organized, consistent, and properly typed. Each test has a clear purpose and follows the same pattern.

🧰 Tools
🪛 Cppcheck (2.10-2)

[performance] 24-24: Function parameter 'path' should be passed by const reference.

(passedByValue)


30-35: LGTM!

The main function properly initializes both Google Test and PHARE lifecycle.

tests/amr/data/particles/refine/test_split.cpp (1)

25-25: LGTM!

The addition of Splitter<3, 1, 27> aligns with the PR objective of transitioning to 3D and follows the established pattern for splitter parameters.

tools/python3/git.py (3)

5-6: LGTM!

The function correctly retrieves the current Git branch name.


9-10: LGTM!

The function correctly retrieves commit hashes with a sensible default parameter.


13-16: LGTM!

The function correctly retrieves Git log entries with helpful documentation and sensible defaults.

tests/simulator/advance/test_particles_advance_3d.py (3)

1-19: LGTM!

The file is well-documented with clear imports and well-defined constants.


21-22: LGTM!

The helper function correctly generates test cases for different interpolation orders.


25-51: LGTM!

The test class is well-structured with appropriate test methods and parameters. The use of data-driven testing enhances test coverage.

tests/simulator/initialize/test_fields_init_2d.py (1)

26-26: LGTM! Parameter renaming improves consistency.

The change from nbr_part_per_cell to ppc aligns with the parameter naming used in the new 3D test files, making the codebase more consistent.

tests/amr/data/field/coarsening/test_weighter_coarsen.hpp (1)

6-6: LGTM! Simplified header dependencies.

Consolidating multiple includes into a single coarsen_weighter.hpp header improves maintainability and reduces potential include-order dependencies.

tests/simulator/initialize/test_fields_init_3d.py (1)

16-19: LGTM! Well-structured test configuration.

The test configuration with ndim=3 and reasonable defaults for ppc and cells is well-thought-out for 3D testing.

tests/simulator/advance/test_fields_advance_1d.py (1)

96-96:

✅ Verification successful

LGTM! Test has been re-enabled.

The test for field level ghosts via subcycles and coarser interpolation is now active, aligning with issue #748.

Let's verify the test coverage for this functionality across all dimensions:


🏁 Script executed:

#!/bin/bash
# Description: Check test coverage for field level ghosts across dimensions
rg -l "test_field_level_ghosts_via_subcycles_and_coarser_interpolation" tests/simulator/advance/

Length of output: 246


LGTM! The previously skipped test for field level ghosts via subcycles and coarser interpolation is now re-enabled and verified to be active across 1D, 2D, and 3D (see files: tests/simulator/advance/test_fields_advance_1d.py, tests/simulator/advance/test_fields_advance_2d.py, and tests/simulator/advance/test_fields_advance_3d.py). This change aligns with the objectives of issue #748.

tests/simulator/initialize/test_particles_init_2d.py (1)

26-26: LGTM! Class name now correctly reflects 2D context.

The class name has been updated to accurately represent the two-dimensional nature of these tests.

tests/simulator/initialize/test_particles_init_3d.py (1)

1-100: LGTM! Well-structured test suite for 3D particle initialization.

The test implementation follows the established patterns from 1D and 2D tests, providing good coverage for:

  • Particle count verification
  • Level ghost particles
  • Domain particles
tests/simulator/advance/test_fields_advance_2d.py (1)

101-101:

✅ Verification successful

Verify test readiness for moments.

The commented-out skip decorator suggests that this test should now work with moments. Please verify that the necessary changes have been made to support moments in this test.


🏁 Script executed:

#!/bin/bash
# Description: Check if moments-related changes have been implemented.
# Search for moment-related code in the test file and its base class.

echo "Checking for moment-related code in test files..."
rg -A 5 "moment" tests/simulator/advance/test_fields_advance_2d.py tests/simulator/test_advance.py

Length of output: 1824


Test Ready for Moments Integration

The execution confirms that moment-related functionality is implemented in the base tests (see tests/simulator/test_advance.py), and the removal of the skip decorator in tests/simulator/advance/test_fields_advance_2d.py appears appropriate. Please proceed with these changes.

tests/simulator/advance/test_fields_advance_3d.py (1)

102-102:

❓ Verification inconclusive

Verify test readiness for moments in 3D.

Similar to the 2D test, this test has a commented skip decorator about moments. Please verify that the necessary changes have been made to support moments in 3D.


🏁 Script executed:

#!/bin/bash
# Description: Check if moments-related changes have been implemented for 3D.
# Search for moment-related code in the test file and its base class.

echo "Checking for moment-related code in 3D test files..."
rg -A 5 "moment" tests/simulator/advance/test_fields_advance_3d.py tests/simulator/test_advance.py

Length of output: 1828


Verify moments readiness in 3D tests

  • In tests/simulator/advance/test_fields_advance_3d.py, the skip decorator remains commented-out (# @unittest.skip("should change to work on moments")), which suggests that the test may not be fully updated for 3D moments support.
  • Meanwhile, tests/simulator/test_advance.py contains moments-related logic (e.g., checking for "moments" and processing hierarchies), indicating that part of the moments support is in place.
  • Action: Please confirm that the 3D test now fully exercises the moments functionality. If the implementation is complete, remove the outdated skip comment and ensure the test validations cover the intended moments behavior in 3D. Otherwise, update the test accordingly.
res/cmake/options.cmake (1)

71-74: Clarify interaction between lowResourceTests and highResourceTests options.

Having both lowResourceTests and highResourceTests options might lead to confusion. Consider documenting how these options interact or consolidating them into a single option with multiple values.

Please clarify:

  1. What happens if both options are ON?
  2. What happens if both options are OFF?
  3. Should these be mutually exclusive?
src/core/utilities/meta/meta_utilities.hpp (2)

9-11: LGTM! Good use of preprocessor directive for configuration.

The default value of 3 for PHARE_SIMULATORS allows for maximum dimensionality while providing flexibility to reduce it during compilation.


83-88: LGTM! Well-structured conditional compilation for 2D simulators.

The code effectively uses preprocessor directives to conditionally include 2D simulator options, improving compilation efficiency.

src/amr/data/particles/refine/splitter.hpp (3)

18-40: LGTM! Well-documented and well-structured pattern definition.

The SplitPattern struct provides a clear geometric description of refined particle placement with comprehensive documentation.


43-104: LGTM! Excellent implementation of pattern combination.

The PatternDispatcher class effectively manages multiple patterns with proper error handling and type safety.


124-132: LGTM! Good use of template specialization prevention.

The deleted constructor effectively prevents instantiation of unspecialized templates, enforcing proper usage.

tests/amr/data/field/refine/test_field_refinement_on_hierarchy.cpp (2)

7-8: LGTM! Good cleanup of includes.

Replacing multiple specific includes with the comprehensive grid.hpp improves maintainability.


11-15: LGTM! Clear and informative test documentation.

The comment block effectively explains the test's purpose and scope, particularly regarding the DEFAULT field refinement operator.

tests/simulator/refined_particle_nbr.py (2)

33-37: LGTM! Good cleanup practice.

The tearDown method ensures proper cleanup of simulator resources after each test, which is essential for test isolation.


44-48:

❓ Verification inconclusive

Verify the 3D case implementation.

The TODO comment indicates that the 3D case might need verification. The current implementation assumes a simple multiplication by cellNbr[2] * 2.

Please verify if this calculation is correct for 3D particle refinement by running the test suite:


🏁 Script executed:

#!/bin/bash
# Run the test suite focusing on 3D cases
python3 -m pytest tests/simulator/refined_particle_nbr.py -v -k "test_3d"

Length of output: 114


Re-check 3D Calculation in Particle Refinement

The current 3D implementation still multiplies by cellNbr[2] * 2 as a simple extension of the 2D calculation. However, the test suite did not run because the pytest module is missing. Please ensure that pytest is installed in your environment so that tests for the 3D case can be executed correctly. Until the tests run successfully, a manual verification of the 3D calculation is recommended.

  • File: tests/simulator/refined_particle_nbr.py (Lines 44-48)
src/amr/data/particles/refine/split_1d.hpp (1)

20-21: LGTM! Improved naming clarity.

The renaming from *Dispatcher to *Pattern better reflects the purpose of these structures, as they define splitting patterns rather than just dispatching logic.

Also applies to: 33-34, 52-53, 71-72, 89-90, 108-109, 127-128, 145-146, 164-165, 183-185

pyphare/pyphare/core/box.py (2)

185-198: LGTM! Efficient implementation of multiple box removal.

The remove_all function efficiently handles multiple box removals by iteratively processing each box in the to_remove list.


209-224: LGTM! Well-documented data selection interface.

The DataSelector class provides a clean interface for data selection and modification using box objects.

res/cmake/funcs.cmake (2)

23-27: LGTM! Important developer note.

Clear documentation warning about not using CMake variable function arguments for Python unit test executions.


37-42: LGTM! Proper test environment setup.

The set_exe_paths_ function correctly sets up PYTHONPATH and ASAN options for test execution.

tests/simulator/__init__.py (1)

98-108: LGTM! The 3D density function follows the established pattern.

The implementation correctly extends the 1D and 2D density calculations to 3D, maintaining consistency with the existing code structure.

tests/core/data/ndarray/test_main.cpp (1)

395-446: LGTM! Comprehensive test coverage for 3D masked views.

The test case thoroughly validates 3D array masking operations with:

  • Clear initialization and boundary testing
  • Well-documented cell count calculations
  • Multiple mask operation scenarios
  • Proper verification of expected results
tests/amr/data/field/refine/test_default_field_refinement.cpp (1)

11-11: LGTM! Improved code organization and documentation.

The changes enhance code readability and maintainability through:

  • Addition of necessary Grid header
  • Clear documentation of test scope
  • Better visual separation of test sections

Also applies to: 14-17, 25-26

pyphare/pyphare/core/gridlayout.py (1)

369-382: LGTM! Well-structured coordinate generation.

The implementation correctly handles all dimensionality cases and uses appropriate numpy functions for coordinate generation.

pyphare/pyphare/pharesee/run/utils.py (3)

362-362: LGTM! Shape calculation updated for multi-dimensional support.

The shape calculation now correctly uses nbrGhosts multiplied by ndim to accommodate different dimensions.


365-365: LGTM! Improved error handling for 1D case.

The error message now clearly indicates that the functionality is not used in 1D.


371-371: LGTM! Clear error message for unsupported dimensions.

The error message now clearly indicates that the functionality is not implemented and questions its usefulness.

tests/simulator/utilities/test_refine_field.py (2)

105-107: LGTM! Added 3D electric field refinement handling.

The code now explicitly handles 3D electric field refinement by raising a NotImplementedError, making it clear that this functionality is not yet implemented.


178-181: LGTM! Added 3D magnetic field refinement handling.

The code now explicitly handles 3D magnetic field refinement by raising a NotImplementedError, and includes a clear error message for impossible layouts.

tests/simulator/test_initialization.py (3)

254-272: LGTM! Added performance measurements and improved parameter handling.

The code now includes timing measurements for initialization and updates parameter handling to support 3D cases.


329-352: LGTM! Added comprehensive 3D magnetic field testing.

The code now properly tests magnetic field components in 3D using appropriate mesh grid calculations and assertions.


399-416: Verify the bulk velocity calculation in 3D.

The bulk velocity calculation has been updated for 3D support, but there might be potential division by zero if ni (ion density) is zero.

# Add a safety check for zero density
if np.any(ni == 0):
    raise ValueError("Zero ion density detected, cannot calculate bulk velocity")
pyphare/pyphare/pharesee/hierarchy/hierarchy.py (1)

564-565: LGTM! Clear handling of 3D plots.

The addition of an explicit error for 3D plots is a good practice, making it clear to users that this functionality is not yet supported.

tests/simulator/test_advance.py (1)

285-340: LGTM! Improved error handling and code clarity.

The refactoring enhances readability and provides better error diagnostics with detailed information when assertions fail.

🧰 Tools
🪛 Ruff (0.8.2)

290-290: Loop control variable ilvl not used within loop body

Rename unused ilvl to _ilvl

(B007)

tests/amr/data/particles/refine/input/input_3d_ratio_2.txt (1)

3-6: LGTM! Consistent 3D configuration.

The configuration has been properly updated to support 3D operations with appropriate domain boxes, coordinates, and ratios.

Also applies to: 19-19, 23-23, 41-41

tests/simulator/initialize/CMakeLists.txt (1)

21-24: LGTM! Well-structured test configuration.

The addition of 3D tests under the highResourceTests condition follows the established pattern and maintains good organization of test resources.

tests/simulator/advance/CMakeLists.txt (1)

21-24: Enable 3D Tests with High Resource Flag: The new conditional block that invokes tests for 3D fields and particles when highResourceTests is enabled is well integrated. This addition clearly delineates 3D tests from lower-resource scenarios. Please ensure that the corresponding 3D test scripts (e.g., test_fields_advance_3d.py and test_particles_advance_3d.py) are updated and documented regarding their resource requirements.

tests/diagnostic/CMakeLists.txt (2)

37-37: Add 3D Diagnostics Test: Introducing _add_diagnostics_test(test-diagnostics_3d) expands the diagnostic test coverage into 3D scenarios. Confirm that the new 3D diagnostic test (and its corresponding C++ test implementation) adheres to the framework’s expectations.


41-41: Configure 3D Job File: The added configuration step using configure_file for job_3d.py.in to generate job_3d.py is a solid move to extend the test setup into 3D. Please verify that job_3d.py.in is properly set up to reflect the parameters required for 3D simulations.

tests/amr/data/field/refine/CMakeLists.txt (1)

49-49: Update Test Naming for Field Refinement: Updating the test call from the old naming convention to _add_serial_amr_field_refine_test(test_default_field_refinement) clarifies the test’s intent. Ensure that any accompanying documentation or references to the old test name are updated accordingly.

tests/simulator/CMakeLists.txt (1)

8-8: Include Utilities Directory in Build: Adding the command to copy the "utilities" directory to the binary directory ensures that necessary helper scripts or resources are available during runtime. Verify that the "utilities" folder remains up-to-date and is correctly referenced by other build or test scripts.

.github/workflows/cmake_macos.yml (1)

81-81: Extend CMake CXX Flags for Simulator Support: Appending the flag -DPHARE_SIMULATORS=2 alongside -DPHARE_DIAG_DOUBLES=1 in the CMake configuration expands the build's configurability to support multiple simulator options. Please confirm that this change aligns with other modules relying on these flags and that the associated documentation is updated.

Comment on lines +144 to +290

if not is_primal[0] and not is_primal[1] and is_primal[2]:
if qty == "Bz":
coarseData[
coarseLocalIndexX, coarseLocalIndexY, coarseLocalIndexZ
] = 0.25 * (
fineData[fineIndexX, fineIndexY, fineIndexZ]
+ fineData[fineIndexX + 1, fineIndexY, fineIndexZ]
+ fineData[fineIndexX, fineIndexY + 1, fineIndexZ]
+ fineData[fineIndexX + 1, fineIndexY + 1, fineIndexZ]
)

elif not is_primal[0] and is_primal[1] and is_primal[2]: # Ex
value = 0.0
# dual in X means taking the two dual fine nodes
# around the coarse one we want the value for.
ishift = (0, 1)
iweights = (0.5, 0.5)

jshift = (-1, 0, 1)
jweights = (0.25, 0.5, 0.25)

kshift = (-1, 0, 1)
kweights = (0.25, 0.5, 0.25)
for i, iweight in zip(ishift, iweights):
yvalue = 0.0
for j, jweight in zip(jshift, jweights):
zvalue = 0.0
for k, kweight in zip(kshift, kweights):
zvalue += (
fineData[fineIndexX + i][fineIndexY + j][
fineIndexZ + k
]
* kweight
)
yvalue += zvalue * jweight
value += yvalue * iweight
coarseData[
coarseLocalIndexX, coarseLocalIndexY, coarseLocalIndexZ
] = value

elif (
is_primal[0] and not is_primal[1] and is_primal[2]
): # Ey is PDP
value = 0.0
ishift = (-1, 0, 1)
iweights = (0.25, 0.5, 0.25)

# dual in Y means taking the two dual fine nodes
# around the coarse one we want the value for.
jshift = (0, 1)
jweights = (0.5, 0.5)

kshift = (-1, 0, 1)
kweights = (0.25, 0.5, 0.25)
for i, iweight in zip(ishift, iweights):
yvalue = 0.0
for j, jweight in zip(jshift, jweights):
zvalue = 0.0
for k, kweight in zip(kshift, kweights):
zvalue += (
fineData[fineIndexX + i][fineIndexY + j][
fineIndexZ + k
]
* kweight
)
yvalue += zvalue * jweight
value += yvalue * iweight
coarseData[
coarseLocalIndexX, coarseLocalIndexY, coarseLocalIndexZ
] = value

elif (
is_primal[0] and is_primal[1] and not is_primal[2]
): # Ez is PPD
value = 0.0
ishift = (-1, 0, 1)
iweights = (0.25, 0.5, 0.25)

jshift = (-1, 0, 1)
jweights = (0.25, 0.5, 0.25)

# dual in Z means taking the two dual fine nodes
# around the coarse one we want the value for.
kshift = (0, 1)
kweights = (0.5, 0.5)
for i, iweight in zip(ishift, iweights):
yvalue = 0.0
for j, jweight in zip(jshift, jweights):
zvalue = 0.0
for k, kweight in zip(kshift, kweights):
zvalue += (
fineData[fineIndexX + i][fineIndexY + j][
fineIndexZ + k
]
* kweight
)
yvalue += zvalue * jweight
value += yvalue * iweight
coarseData[
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider systematically generating coarsening weights.

Between lines 144 and 290, various blocks compute weighted averages depending on primal/dual states. Repetitive patterns, such as ishift, jshift, and kshift arrays, could be encapsulated in a function or dictionary keyed by dimension and primal/dual flags. Doing so can reduce code duplication, centralize the weighting logic, and simplify future expansions or bug fixes.

🧰 Tools
🪛 Ruff (0.8.2)

181-182: Use a single if statement instead of nested if statements

(SIM102)

Comment on lines +361 to 381
for (auto i = min_; i <= max_; ++i)
for (auto j = min_; j <= shape[1] - 1 - max_; ++j)
array(i, j) = val;

// right border
for (std::size_t i = shape[0] - 1 - max_; i <= shape[0] - 1 - min_; ++i)
for (std::size_t j = min_; j <= shape[1] - 1 - max_; ++j)
for (auto i = shape[0] - 1 - max_; i <= shape[0] - 1 - min_; ++i)
for (auto j = min_; j <= shape[1] - 1 - max_; ++j)
array(i, j) = val;


for (std::size_t i = min_; i <= shape[0] - 1 - min_; ++i)
for (auto i = min_; i <= shape[0] - 1 - min_; ++i)
{
// bottom border
for (std::size_t j = min_; j <= max_; ++j)
for (auto j = min_; j <= max_; ++j)
array(i, j) = val;

// top border
for (std::size_t j = shape[1] - 1 - max_; j <= shape[1] - 1 - min_; ++j)
for (auto j = shape[1] - 1 - max_; j <= shape[1] - 1 - min_; ++j)
array(i, j) = val;
}
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Avoid potential off-by-one in 2D borders.
These lines fill the 2D boundary loops (left, right, bottom, top). It appears correct for inclusive boundaries. If min_ or max_ become larger than expected, you risk overlaps or out-of-range indexing. Consider adding boundary checks.

Comment on lines +27 to +35
def checkout(branch, create=False, recreate=False):
if recreate:
delete_branch(branch)
create = True

if create and not branch_exists(branch):
run(f"git checkout -b {branch}")
else:
run(f"git checkout {branch}")
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add missing delete_branch function.

The function references an undefined delete_branch function.

Please implement the missing delete_branch function before using it in the checkout function. Example implementation:

def delete_branch(branch):
    """Delete a Git branch if it exists."""
    if branch_exists(branch):
        run(f"git branch -D {branch}")
🧰 Tools
🪛 Ruff (0.8.2)

29-29: Undefined name delete_branch

(F821)

Comment on lines +73 to +74
# -DhighResourceTests=ON
option(highResourceTests "Enable heavy tests for CI (3d/etc" OFF)
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add missing print statement for highResourceTests option.

The new option is not included in the print_phare_options function output. Add it for consistency with other options.

Add this line in the print_phare_options function:

     message("Exclude heavier tests                       : " ${lowResourceTests})
+    message("Include heavy tests (3D/etc)                : " ${highResourceTests})
     message("PHARE_MPI_PROCS                             : " ${PHARE_MPI_PROCS})

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +319 to +346
def debug_tracer():
"""
print live stack trace during execution
"""

import sys, os

def tracefunc(frame, event, arg, indent=[0]):
filename = os.path.basename(frame.f_code.co_filename)
line_number = frame.f_lineno
if event == "call":
indent[0] += 2
print(
"-" * indent[0] + "> enter function",
frame.f_code.co_name,
f"{filename} {line_number}",
)
elif event == "return":
print(
"<" + "-" * indent[0],
"exit function",
frame.f_code.co_name,
f"{filename} {line_number}",
)
indent[0] -= 2
return tracefunc

sys.setprofile(tracefunc)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix mutable default argument in debug_tracer function.

The function uses a mutable list as a default argument (indent=[0]), which is a Python anti-pattern that can lead to unexpected behavior when the function is reused.

Apply this diff to fix the issue:

-def tracefunc(frame, event, arg, indent=[0]):
+def tracefunc(frame, event, arg, indent=None):
+    if indent is None:
+        indent = [0]
📝 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
def debug_tracer():
"""
print live stack trace during execution
"""
import sys, os
def tracefunc(frame, event, arg, indent=[0]):
filename = os.path.basename(frame.f_code.co_filename)
line_number = frame.f_lineno
if event == "call":
indent[0] += 2
print(
"-" * indent[0] + "> enter function",
frame.f_code.co_name,
f"{filename} {line_number}",
)
elif event == "return":
print(
"<" + "-" * indent[0],
"exit function",
frame.f_code.co_name,
f"{filename} {line_number}",
)
indent[0] -= 2
return tracefunc
sys.setprofile(tracefunc)
def debug_tracer():
"""
print live stack trace during execution
"""
import sys, os
def tracefunc(frame, event, arg, indent=None):
if indent is None:
indent = [0]
filename = os.path.basename(frame.f_code.co_filename)
line_number = frame.f_lineno
if event == "call":
indent[0] += 2
print(
"-" * indent[0] + "> enter function",
frame.f_code.co_name,
f"{filename} {line_number}",
)
elif event == "return":
print(
"<" + "-" * indent[0],
"exit function",
frame.f_code.co_name,
f"{filename} {line_number}",
)
indent[0] -= 2
return tracefunc
sys.setprofile(tracefunc)
🧰 Tools
🪛 Ruff (0.8.2)

326-326: Do not use mutable data structures for argument defaults

Replace with None; initialize within function

(B006)

@@ -67,6 +79,7 @@
using FineParticle = decltype(particles[0]); // may be a reference

core::apply(patterns, [&](auto const& pattern) {
auto weight = static_cast<Weight_t>(pattern.weight_);

Check notice

Code scanning / CodeQL

Unused local variable

Variable weight is not used.
@@ -392,6 +392,60 @@
+ Mask{0u}.nCells(array));
}

TEST(MaskedView3d, maskOps3)
{
constexpr std::size_t dim = 3;

Check notice

Code scanning / CodeQL

Unused local variable

Variable dim is not used.
TYPED_TEST_SUITE_P(ACollectionOfParticles_3d);


TYPED_TEST_P(ACollectionOfParticles_3d, DepositCorrectlyTheirWeight_3d)

Check notice

Code scanning / CodeQL

Unused local variable Note test

Variable (unnamed local variable) is not used.
Comment on lines +776 to +780
// auto const& [vx, vy, vz] = this->v();
// EXPECT_DOUBLE_EQ(this->rho(7, 7, 7), 1.0);
// EXPECT_DOUBLE_EQ(vx(7, 7, 7), 2.0);
// EXPECT_DOUBLE_EQ(vy(7, 7, 7), -1.0);
// EXPECT_DOUBLE_EQ(vz(7, 7, 7), 1.0);

Check notice

Code scanning / CodeQL

Unused local variable Note test

Variable (unnamed local variable) is not used.


using My3dTypes = ::testing::Types<Interpolator<3, 1>, Interpolator<3, 2>, Interpolator<3, 3>>;
INSTANTIATE_TYPED_TEST_SUITE_P(testInterpolator, ACollectionOfParticles_3d, My3dTypes);

Check notice

Code scanning / CodeQL

Unused local variable Note test

Variable (unnamed local variable) is not used.

#include "core/def/phare_mpi.hpp"

#include "test_diagnostics.ipp"

Check notice

Code scanning / CodeQL

Unused local variable Note test

Variable (unnamed local variable) is not used.
static std::string const job_file = "job_3d";
static std::string const out_dir = "phare_outputs/diags_3d/";

TYPED_TEST(Simulator3dTest, fluid)

Check warning

Code scanning / CodeQL

Poorly documented large function Warning test

Poorly documented function: fewer than 2% comments for a function of 198 lines.
fluid_test(TypeParam{job_file}, out_dir);
}

TYPED_TEST(Simulator3dTest, particles)

Check warning

Code scanning / CodeQL

Poorly documented large function Warning test

Poorly documented function: fewer than 2% comments for a function of 133 lines.
particles_test(TypeParam{job_file}, out_dir);
}

TYPED_TEST(Simulator3dTest, electromag)

Check notice

Code scanning / CodeQL

Unused local variable Note test

Variable Eznew is not used.
electromag_test(TypeParam{job_file}, out_dir);
}

TYPED_TEST(Simulator3dTest, allFromPython)

Check notice

Code scanning / CodeQL

Unused local variable Note test

Variable Eynew is not used.
@@ -392,6 +392,60 @@
+ Mask{0u}.nCells(array));
}

TEST(MaskedView3d, maskOps3)
{
constexpr std::size_t dim = 3;

Check notice

Code scanning / CodeQL

Unused local variable Note test

Variable dim is not used.

TYPED_TEST_P(ACollectionOfParticles_3d, DepositCorrectlyTheirWeight_3d)
{
// auto const& [vx, vy, vz] = this->v();

Check notice

Code scanning / CodeQL

Unused static variable Note test

Static variable gtest_DepositCorrectlyTheirWeight_3d_defined_ is never read.
Comment on lines +778 to +782
// EXPECT_DOUBLE_EQ(vx(7, 7, 7), 2.0);
// EXPECT_DOUBLE_EQ(vy(7, 7, 7), -1.0);
// EXPECT_DOUBLE_EQ(vz(7, 7, 7), 1.0);
}
REGISTER_TYPED_TEST_SUITE_P(ACollectionOfParticles_3d, DepositCorrectlyTheirWeight_3d);

Check notice

Code scanning / CodeQL

Commented-out code Note test

This comment appears to contain commented-out code.
using My3dTypes = ::testing::Types<Interpolator<3, 1>, Interpolator<3, 2>, Interpolator<3, 3>>;
INSTANTIATE_TYPED_TEST_SUITE_P(testInterpolator, ACollectionOfParticles_3d, My3dTypes);
/*********************************************************************************************/

Check notice

Code scanning / CodeQL

Unused static variable Note test

Static variable gtest_testInterpolator_ACollectionOfParticles_3d is never read.

#include "core/def/phare_mpi.hpp"

#include "test_diagnostics.ipp"

Check notice

Code scanning / CodeQL

Include header files only Note test

The #include pre-processor directive should only be used to include header files.
static std::string const job_file = "job_3d";
static std::string const out_dir = "phare_outputs/diags_3d/";

TYPED_TEST(Simulator3dTest, fluid)

Check notice

Code scanning / CodeQL

Unused static variable Note test

Static variable gtest_Simulator3dTest_fluid_registered_ is never read.
fluid_test(TypeParam{job_file}, out_dir);
}

TYPED_TEST(Simulator3dTest, particles)

Check notice

Code scanning / CodeQL

Unused static variable Note test

Static variable gtest_Simulator3dTest_particles_registered_ is never read.
particles_test(TypeParam{job_file}, out_dir);
}

TYPED_TEST(Simulator3dTest, electromag)

Check notice

Code scanning / CodeQL

Unused static variable Note test

Static variable gtest_Simulator3dTest_electromag_registered_ is never read.
electromag_test(TypeParam{job_file}, out_dir);
}

TYPED_TEST(Simulator3dTest, allFromPython)

Check notice

Code scanning / CodeQL

Unused static variable Note test

Static variable gtest_Simulator3dTest_allFromPython_registered_ is never read.
Copy link

@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

🧹 Nitpick comments (1)
tests/simulator/test_advance.py (1)

290-290: Rename unused loop variable.

The loop control variable ilvl is not used within the loop body.

-        for ilvl, overlaps in hierarchy_overlaps(datahier, coarsest_time).items():
+        for _ilvl, overlaps in hierarchy_overlaps(datahier, coarsest_time).items():
🧰 Tools
🪛 Ruff (0.8.2)

290-290: Loop control variable ilvl not used within loop body

Rename unused ilvl to _ilvl

(B007)

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b69e607 and 0e85fa8.

📒 Files selected for processing (1)
  • tests/simulator/test_advance.py (15 hunks)
🧰 Additional context used
🪛 Ruff (0.8.2)
tests/simulator/test_advance.py

290-290: Loop control variable ilvl not used within loop body

Rename unused ilvl to _ilvl

(B007)

⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: build (macos-latest)
  • GitHub Check: build (macos-14)
  • GitHub Check: Analyze (cpp)
  • GitHub Check: build (ubuntu-latest)
  • GitHub Check: Analyze (python)
  • GitHub Check: build (macos-13)
🔇 Additional comments (3)
tests/simulator/test_advance.py (3)

9-9: LGTM! Documentation improvements enhance code clarity.

The updated docstring clearly explains the class's role in dimensionality-agnostic testing and its relationship with dimension-specific test files.

Also applies to: 32-42


285-340: LGTM! Improved error handling and debugging capabilities.

The changes enhance the test's maintainability and debuggability through:

  1. More descriptive variable naming (success_test_nbr)
  2. Detailed error information for debugging
  3. Well-documented empirical tolerance value (5.5e-15)
🧰 Tools
🪛 Ruff (0.8.2)

290-290: Loop control variable ilvl not used within loop body

Rename unused ilvl to _ilvl

(B007)


415-417: LGTM! Enhanced method signature with default value.

The addition of a default value for the cells parameter improves the method's usability while maintaining backward compatibility.

Copy link

@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

🧹 Nitpick comments (1)
tests/simulator/test_advance.py (1)

286-340: Improve variable naming and error handling in overlap test.

The changes improve error reporting and test logic, but there's an unused loop variable that should be addressed.

Apply this diff to fix the unused variable:

-        for ilvl, overlaps in hierarchy_overlaps(datahier, coarsest_time).items():
+        for _ilvl, overlaps in hierarchy_overlaps(datahier, coarsest_time).items():
🧰 Tools
🪛 Ruff (0.8.2)

290-290: Loop control variable ilvl not used within loop body

Rename unused ilvl to _ilvl

(B007)

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0e85fa8 and a8973ca.

📒 Files selected for processing (1)
  • tests/simulator/test_advance.py (16 hunks)
🧰 Additional context used
🪛 Ruff (0.8.2)
tests/simulator/test_advance.py

290-290: Loop control variable ilvl not used within loop body

Rename unused ilvl to _ilvl

(B007)

⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: build (macos-latest)
  • GitHub Check: Analyze (cpp)
  • GitHub Check: build (macos-14)
  • GitHub Check: build (ubuntu-latest)
  • GitHub Check: Analyze (python)
  • GitHub Check: build (macos-13)
🔇 Additional comments (4)
tests/simulator/test_advance.py (4)

32-42: LGTM! Documentation improvements enhance code clarity.

The added docstring clearly explains the class purpose and its relationship with dimension-dependent tests.


79-82: LGTM! Improved code organization with clear section markers.

The added section markers and docstrings improve code readability by clearly separating test setup from test definitions.

Also applies to: 98-100, 210-213, 277-283


415-417: LGTM! Improved function signature with default value.

The addition of a default value for cells parameter improves function usability.


444-444: LGTM! Enhanced error reporting in field coarsening test.

The changes improve error reporting by:

  1. Adding detailed comments explaining index calculations
  2. Including more context in error messages
  3. Using empirically determined tolerance values

Also applies to: 458-459, 464-465, 526-535, 555-587

@nicolasaunai nicolasaunai force-pushed the 3d_base branch 2 times, most recently from a0e526d to 3735984 Compare February 21, 2025 11:49
Copy link

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

♻️ Duplicate comments (3)
tools/python3/git.py (1)

29-29: ⚠️ Potential issue

Define or import the missing delete_branch function.
This function call is undefined, causing a name error. This issue was also flagged in a previous review.

+def delete_branch(branch):
+    """Delete a Git branch if it exists."""
+    if branch_exists(branch):
+        run(f"git branch -D {branch}")

 def checkout(branch, create=False, recreate=False):
     if recreate:
         delete_branch(branch)
🧰 Tools
🪛 Ruff (0.8.2)

29-29: Undefined name delete_branch

(F821)

src/amr/data/particles/refine/splitter.hpp (1)

82-82: ⚠️ Potential issue

Remove unused variable.

The variable weight is declared but never used.

Apply this diff to remove the unused variable:

-            auto weight = static_cast<Weight_t>(pattern.weight_);
tests/simulator/__init__.py (1)

319-346: ⚠️ Potential issue

Fix mutable default argument in debug_tracer function.

The function uses a mutable list as a default argument (indent=[0]), which is a Python anti-pattern that can lead to unexpected behavior when the function is reused.

Apply this diff to fix the issue:

-def tracefunc(frame, event, arg, indent=[0]):
+def tracefunc(frame, event, arg, indent=None):
+    if indent is None:
+        indent = [0]
🧰 Tools
🪛 Ruff (0.8.2)

326-326: Do not use mutable data structures for argument defaults

Replace with None; initialize within function

(B006)

🧹 Nitpick comments (25)
src/core/data/ndarray/ndarray_vector.hpp (3)

383-424: Consider optimizing the 3D border fill implementation.

The current implementation has nested loops that could be optimized for better cache utilization and readability:

  1. Consider extracting common loop patterns into helper functions
  2. The border fill logic could benefit from SIMD optimizations for large arrays
 template<typename Array>
 void fill3D(Array& array, typename Array::type val) const
 {
     auto shape = array.shape();
+    auto fillPlane = [&](auto i_start, auto i_end, auto j_start, auto j_end, auto k_start, auto k_end) {
+        for (auto i = i_start; i <= i_end; ++i)
+            for (auto j = j_start; j <= j_end; ++j)
+                for (auto k = k_start; k <= k_end; ++k)
+                    array(i, j, k) = val;
+    };
 
-    // left border
-    for (auto i = min_; i <= shape[0] - 1 - max_; ++i)
-        for (auto j = min_; j <= shape[1] - 1 - max_; ++j)
-            for (auto k = min_; k <= max_; ++k)
-                array(i, j, k) = val;
+    // Fill left and right borders
+    fillPlane(min_, shape[0] - 1 - max_, min_, shape[1] - 1 - max_, min_, max_);
+    fillPlane(min_, shape[0] - 1 - max_, min_, shape[1] - 1 - max_, shape[2] - 1 - max_, shape[2] - 1 - min_);
     // ... similar refactoring for other borders

433-449: Improve readability of 3D cell count calculation.

The nested logic for calculating cells in 3D is complex and could benefit from better documentation and variable naming:

 if constexpr (Array::dimension == 3)
 {
     auto [x, y, z] = shape;
     x -= i * 2;
     y -= i * 2;
     z -= i * 2;
-    cells += (x * y * 2) + (y * (z - 2) * 2) + ((z - 2) * (x - 2) * 2);
+    // Calculate cells for each face pair (front/back, left/right, top/bottom)
+    const auto frontBackFaces = x * y * 2;
+    const auto leftRightFaces = y * (z - 2) * 2;
+    const auto topBottomFaces = (z - 2) * (x - 2) * 2;
+    cells += frontBackFaces + leftRightFaces + topBottomFaces;
 }

527-595: Consider extracting common patterns in 3D view operator.

The 3D implementation of operator>> has repeated patterns that could be extracted into helper functions for better maintainability:

 if constexpr (MaskedView_t::dimension == 3)
 {
     assert(inner.zstart() > outer.zstart() and inner.zend() < outer.zend());
 
+    auto fillFace = [&](auto i_range, auto j_range, auto k_range, auto getValue) {
+        for (auto i : i_range)
+            for (auto j : j_range)
+                for (auto k : k_range)
+                    outer(i, j, k) = getValue(i, j, k);
+    };
+
     // ... use fillFace for repeated patterns
 }

Additionally, the corner lambda could be moved outside the if constexpr block to improve code organization.

tests/core/numerics/interpolator/test_main.cpp (1)

750-753: Consider extracting the weight calculation into a named function.

The lambda function for weight calculation could be moved to a named function for better readability and reusability.

Apply this diff to extract the weight calculation:

-        double weight = [](auto const& meshSize) {
-            return std::accumulate(meshSize.begin(), meshSize.end(), 1.0,
-                                   std::multiplies<double>());
-        }(layout.meshSize());
+        double weight = computeMeshWeight(layout.meshSize());

Add this function above the test class:

template<typename MeshSize>
double computeMeshWeight(MeshSize const& meshSize) {
    return std::accumulate(meshSize.begin(), meshSize.end(), 1.0, std::multiplies<double>());
}
src/core/utilities/meta/meta_utilities.hpp (1)

94-96: Consider refactoring 3D simulator options for better maintainability.

The commented out value 27 and limited particle numbers for 3D suggest potential validation gaps.

Consider:

  1. Using a constexpr function to generate valid particle numbers for each dimension
  2. Adding validation for the commented out value 27
  3. Documenting the rationale for the chosen particle numbers

Example structure:

template<typename DimConstant>
constexpr auto getValidParticleNumbers() {
    if constexpr (std::is_same_v<DimConstant, DimConst<3>>) {
        return std::tuple<
            std::integral_constant<std::size_t, 6>,
            std::integral_constant<std::size_t, 12>,
            std::integral_constant<std::size_t, 27>  // Add after validation
        >{};
    }
    // ... handle other dimensions
}
res/cmake/funcs.cmake (4)

28-35: Consider making PHARE_MPI_PROCS configurable via an external variable or option.
Currently, PHARE_MPI_PROCS is set to 2 if testMPI is enabled. For more flexibility, you could allow users (or CI) to override this in different testing environments (e.g., large clusters vs. local machines).


44-57: Ensure that the test target is not re-run unnecessarily if tests are auto-executed during the build.
You are calling add_custom_command to run tests post-build. If you have incremental builds or repeated builds, this might cause repeated executions. You may want to conditionally skip re-executing if nothing changes or consider turning off this post-build test run for normal development.


139-156: Standardize MPI arguments for Python tests.
The function phare_mpi_python3_exec uses a hard-coded mpirun. Consider making the MPI launcher configurable (e.g., mpiexec, srun) if your code will run on various HPC systems with different MPI launchers.


184-191: Consider adding filtering options to phare_print_all_vars().
Printing all variables can be overwhelming. A filtering mechanism (e.g., by prefix) might help developers find relevant variables in large builds.

src/amr/data/particles/refine/split_3d.hpp (1)

223-235: Confirm or document the usage of magic numbers for delta and weight.
Hardcoded floating-point values like .888184 and .083333 might need referencing or comments explaining how they were derived.

tools/python3/git.py (1)

22-22: Remove unused variable or rename if necessary.
The variable e is assigned in the except block but never used, triggering a static analysis warning.

-    except subprocess.CalledProcessError as e:
+    except subprocess.CalledProcessError:
🧰 Tools
🪛 Ruff (0.8.2)

22-22: Local variable e is assigned to but never used

Remove assignment to unused variable e

(F841)

tools/python3/cmake.py (1)

4-5: Implement the version function.

The version() function is empty and needs to be implemented.

Would you like me to generate an implementation that returns the CMake version using cmake --version?

tests/functional/harris/harris_3d.py (2)

142-172: Document or remove commented-out plot calls.

Several plot calls are commented out without explanation. Consider either:

  1. Adding comments explaining why these plots are disabled and under what conditions they should be re-enabled.
  2. Removing the commented code if it's no longer needed.

177-178: Address the TODO comment for 3D plotting.

The TODO comment indicates that a solution for 3D plotting is needed after the removal of the hierarchy plot3d feature.

Would you like me to help implement a new 3D plotting solution using libraries like matplotlib or plotly?

tests/simulator/refined_particle_nbr.py (1)

47-48: Remove TODO comment and document 3D case.

The TODO comment can be removed as the 3D case is now implemented. Consider adding a comment explaining the calculation for the 3D case.

Apply this diff to improve documentation:

-        # TODO3D if dim==3?
+        # For 3D, multiply 2D result by the number of cells in z dimension * 2
+        # to account for particles leaving through z-faces
         return dim2 * (cellNbr[2] * 2)
pyphare/pyphare/core/box.py (3)

185-198: Improve list modification safety.

The function modifies the list while iterating, which could be error-prone. Consider using a more explicit approach.

Apply this diff to improve safety:

 def remove_all(box, to_remove):
     if len(to_remove) > 0:
         remaining = box - to_remove[0]
         for to_rm in to_remove[1:]:
-            tmp, remove = [], []
-            for i, rem in enumerate(remaining):
-                if rem * to_rm is not None:
-                    remove.append(i)
-                    tmp += rem - to_rm
-            for rm in reversed(remove):
-                del remaining[rm]
-            remaining += tmp
+            new_remaining = []
+            for rem in remaining:
+                if rem * to_rm is not None:
+                    new_remaining.extend(rem - to_rm)
+                else:
+                    new_remaining.append(rem)
+            remaining = new_remaining
         return remaining
     return box

206-207: Improve variable naming clarity.

The variable name 'l' is ambiguous and could be confused with the number '1'.

Apply this diff to improve readability:

-def select(data, box):
-    return data[tuple([slice(l, u + 1) for l,u in zip(box.lower, box.upper)])]
+def select(data, box):
+    return data[tuple([slice(lower, upper + 1) for lower, upper in zip(box.lower, box.upper)])]
🧰 Tools
🪛 Ruff (0.8.2)

207-207: Ambiguous variable name: l

(E741)


209-223: Enhance class documentation.

The class documentation could be more descriptive and include examples.

Apply this diff to improve documentation:

 class DataSelector:
     """
-        can't assign return to function unless []
-        usage
-        DataSelector(data)[box] = val
+    A utility class for selecting and modifying data using Box instances or slices.
+    
+    This class provides a convenient interface for data access and modification
+    using Box instances or standard Python slices.
+    
+    Examples:
+        >>> selector = DataSelector(data)
+        >>> # Using a Box instance
+        >>> box = Box([0, 0], [2, 2])
+        >>> selector[box] = new_values
+        >>> # Using a slice
+        >>> selector[1:3, 1:3] = other_values
     """
pyphare/pyphare/core/gridlayout.py (1)

369-382: Consider improving variable naming and error handling.

The implementation could benefit from:

  1. More descriptive variable names (e.g., X,Y,Zx_mesh, y_mesh, z_mesh)
  2. Error handling for invalid dimensions

Here's a suggested improvement:

def meshCoords(self, qty):
    ndim = self.ndim
-   assert ndim > 0 and ndim < 4
+   if not (0 < ndim < 4):
+       raise ValueError(f"Invalid number of dimensions: {ndim}. Expected 1, 2, or 3.")
    x = self.yeeCoordsFor(qty, "x")
    if ndim == 1:
        return x
    y = self.yeeCoordsFor(qty, "y")
    if ndim == 2:
-       X,Y = np.meshgrid(x,y,indexing="ij")
-       return np.array([X.flatten(),Y.flatten()]).T.reshape((len(x), len(y), ndim))
+       x_mesh, y_mesh = np.meshgrid(x, y, indexing="ij")
+       return np.array([x_mesh.flatten(), y_mesh.flatten()]).T.reshape((len(x), len(y), ndim))
    z = self.yeeCoordsFor(qty, "z")
-   X ,Y, Z = np.meshgrid(x,y,z,indexing="ij")
-   return np.array([X.flatten(), Y.flatten(), Z.flatten()]).T.reshape((len(x), len(y), len(z), ndim))
+   x_mesh, y_mesh, z_mesh = np.meshgrid(x, y, z, indexing="ij")
+   return np.array([x_mesh.flatten(), y_mesh.flatten(), z_mesh.flatten()]).T.reshape((len(x), len(y), len(z), ndim))
tests/amr/data/field/coarsening/coarsening_utils.py (2)

8-21: Enhance docstring with parameter descriptions.

The docstring should include descriptions of all parameters and their types for better clarity.

def coarsen(qty, coarseField, fineField, coarseBox, fineData, coarseData):
    """
+   Parameters
+   ----------
+   qty : str
+       The quantity to coarsen (e.g., "Bx", "By", "Bz", etc.)
+   coarseField : Field
+       The coarse field object
+   fineField : Field
+       The fine field object
+   coarseBox : Box
+       The box defining the coarse grid region
+   fineData : ndarray
+       The dataset from the fine field
+   coarseData : ndarray
+       The dataset to store coarsened values
+
+   Notes
+   -----
    this function takes data in fineData (the dataset from the fineFied FieldData object)
    and put it in the coarseData dataset

    The coarsening method is the equivalent (and union) of the C++ coarsening methods,
    namely:
    - DefaultFieldCoarsener, used for all fields (moments and E) but the magnetic field B
    - MagneticFieldCoarsener, used for the magnetic field B

    as in the C++ counterpart, we hard-code here that the layout of B follows the yee layout.
    That is Bx is PDD, By is DPD and Bz is DDP
    """

181-182: Simplify nested if statements.

Combine the nested if statements into a single condition for better readability.

-                   elif not is_primal[0] and is_primal[1] and not is_primal[2]:
-                       if qty == "By":
+                   elif not is_primal[0] and is_primal[1] and not is_primal[2] and qty == "By":
🧰 Tools
🪛 Ruff (0.8.2)

181-182: Use a single if statement instead of nested if statements

(SIM102)

pyphare/pyphare/pharesee/geometry.py (1)

161-165: Improve variable naming in list comprehensions.

Replace the ambiguous variable name l with a more descriptive name like shift_list to enhance code readability.

-            f"{k}front": [(v[0], v[1], shape_z) for v in l] for k, l in shifts.items()
+            f"{k}front": [(v[0], v[1], shape_z) for v in shift_list] for k, shift_list in shifts.items()
-            f"{k}back": [([v[0], v[1], -shape_z]) for v in l] for k, l in shifts.items()
+            f"{k}back": [([v[0], v[1], -shape_z]) for v in shift_list] for k, shift_list in shifts.items()
🧰 Tools
🪛 Ruff (0.8.2)

161-161: Ambiguous variable name: l

(E741)


164-164: Ambiguous variable name: l

(E741)

tests/simulator/test_initialization.py (1)

355-416: Clean up unused loop variable.

The loop variable pi in the patch iteration is not used within the loop body.

-        for pi, patch in enumerate(level.patches):
+        for _pi, patch in enumerate(level.patches):
🧰 Tools
🪛 Ruff (0.8.2)

394-394: Function definition does not bind loop variable select

(B023)


396-396: Function definition does not bind loop variable patch

(B023)


396-396: Function definition does not bind loop variable nbrGhosts

(B023)


397-397: Function definition does not bind loop variable select

(B023)

tests/simulator/test_advance.py (2)

285-340: Clean up unused loop variable and enhance error reporting.

  1. The loop variable ilvl in the overlaps iteration is not used within the loop body.
  2. The error reporting includes comprehensive debug information.
-        for ilvl, overlaps in hierarchy_overlaps(datahier, coarsest_time).items():
+        for _ilvl, overlaps in hierarchy_overlaps(datahier, coarsest_time).items():
🧰 Tools
🪛 Ruff (0.8.2)

290-290: Loop control variable ilvl not used within loop body

Rename unused ilvl to _ilvl

(B007)


443-587: Remove unused variable.

The variable out is assigned but never used in the function.

-        out = "domain_particles"
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d84b955 and 72ab7cc.

📒 Files selected for processing (69)
  • .github/workflows/cmake_macos.yml (1 hunks)
  • pyphare/pyphare/core/box.py (2 hunks)
  • pyphare/pyphare/core/gridlayout.py (1 hunks)
  • pyphare/pyphare/pharesee/geometry.py (7 hunks)
  • pyphare/pyphare/pharesee/hierarchy/hierarchy.py (1 hunks)
  • pyphare/pyphare/pharesee/run/utils.py (1 hunks)
  • res/amr/splitting.yml (1 hunks)
  • res/cmake/funcs.cmake (1 hunks)
  • res/cmake/options.cmake (2 hunks)
  • src/amr/data/field/coarsening/coarsen_weighter.hpp (1 hunks)
  • src/amr/data/field/coarsening/magnetic_field_coarsener.hpp (1 hunks)
  • src/amr/data/particles/refine/particles_data_split.hpp (2 hunks)
  • src/amr/data/particles/refine/split.hpp (1 hunks)
  • src/amr/data/particles/refine/split_1d.hpp (10 hunks)
  • src/amr/data/particles/refine/split_2d.hpp (17 hunks)
  • src/amr/data/particles/refine/split_3d.hpp (1 hunks)
  • src/amr/data/particles/refine/splitter.hpp (5 hunks)
  • src/core/data/grid/grid.hpp (1 hunks)
  • src/core/data/grid/gridlayoutdefs.hpp (2 hunks)
  • src/core/data/ndarray/ndarray_vector.hpp (8 hunks)
  • src/core/data/particles/particle.hpp (3 hunks)
  • src/core/utilities/box/box.hpp (1 hunks)
  • src/core/utilities/meta/meta_utilities.hpp (2 hunks)
  • tests/amr/data/field/coarsening/CMakeLists.txt (0 hunks)
  • tests/amr/data/field/coarsening/coarsening_utils.py (1 hunks)
  • tests/amr/data/field/coarsening/input/input_1d_ratio_10.txt (0 hunks)
  • tests/amr/data/field/coarsening/input/input_1d_ratio_2.txt (0 hunks)
  • tests/amr/data/field/coarsening/input/input_1d_ratio_3.txt (0 hunks)
  • tests/amr/data/field/coarsening/input/input_1d_ratio_4.txt (0 hunks)
  • tests/amr/data/field/coarsening/input/input_1d_ratio_6.txt (0 hunks)
  • tests/amr/data/field/coarsening/input/input_1d_ratio_7.txt (0 hunks)
  • tests/amr/data/field/coarsening/input/input_1d_ratio_8.txt (0 hunks)
  • tests/amr/data/field/coarsening/input/input_1d_ratio_9.txt (0 hunks)
  • tests/amr/data/field/coarsening/input_config.h.in (0 hunks)
  • tests/amr/data/field/coarsening/test_coarsening_main.cpp (0 hunks)
  • tests/amr/data/field/coarsening/test_linear_coarsen.hpp (0 hunks)
  • tests/amr/data/field/coarsening/test_weighter_coarsen.hpp (1 hunks)
  • tests/amr/data/field/refine/CMakeLists.txt (1 hunks)
  • tests/amr/data/field/refine/test_default_field_refinement.cpp (4 hunks)
  • tests/amr/data/field/refine/test_field_refinement_on_hierarchy.cpp (1 hunks)
  • tests/amr/data/particles/refine/input/input_3d_ratio_2.txt (2 hunks)
  • tests/amr/data/particles/refine/test_split.cpp (1 hunks)
  • tests/core/data/ndarray/test_main.cpp (1 hunks)
  • tests/core/numerics/interpolator/test_main.cpp (1 hunks)
  • tests/diagnostic/CMakeLists.txt (1 hunks)
  • tests/diagnostic/job_3d.py.in (1 hunks)
  • tests/diagnostic/test-diagnostics_3d.cpp (1 hunks)
  • tests/functional/harris/harris_3d.py (1 hunks)
  • tests/simulator/CMakeLists.txt (1 hunks)
  • tests/simulator/__init__.py (3 hunks)
  • tests/simulator/advance/CMakeLists.txt (1 hunks)
  • tests/simulator/advance/test_fields_advance_1d.py (1 hunks)
  • tests/simulator/advance/test_fields_advance_2d.py (1 hunks)
  • tests/simulator/advance/test_fields_advance_3d.py (1 hunks)
  • tests/simulator/advance/test_particles_advance_3d.py (1 hunks)
  • tests/simulator/initialize/CMakeLists.txt (1 hunks)
  • tests/simulator/initialize/test_fields_init_2d.py (1 hunks)
  • tests/simulator/initialize/test_fields_init_3d.py (1 hunks)
  • tests/simulator/initialize/test_particles_init_1d.py (0 hunks)
  • tests/simulator/initialize/test_particles_init_2d.py (1 hunks)
  • tests/simulator/initialize/test_particles_init_3d.py (1 hunks)
  • tests/simulator/per_test.hpp (1 hunks)
  • tests/simulator/refined_particle_nbr.py (2 hunks)
  • tests/simulator/test_advance.py (16 hunks)
  • tests/simulator/test_initialization.py (13 hunks)
  • tests/simulator/utilities/field_coarsening.py (0 hunks)
  • tests/simulator/utilities/test_refine_field.py (2 hunks)
  • tools/python3/cmake.py (1 hunks)
  • tools/python3/git.py (1 hunks)
💤 Files with no reviewable changes (14)
  • tests/amr/data/field/coarsening/input_config.h.in
  • tests/amr/data/field/coarsening/test_coarsening_main.cpp
  • tests/amr/data/field/coarsening/input/input_1d_ratio_8.txt
  • tests/amr/data/field/coarsening/input/input_1d_ratio_6.txt
  • tests/simulator/initialize/test_particles_init_1d.py
  • tests/amr/data/field/coarsening/input/input_1d_ratio_9.txt
  • tests/amr/data/field/coarsening/input/input_1d_ratio_7.txt
  • tests/amr/data/field/coarsening/input/input_1d_ratio_3.txt
  • tests/amr/data/field/coarsening/input/input_1d_ratio_4.txt
  • tests/amr/data/field/coarsening/input/input_1d_ratio_10.txt
  • tests/amr/data/field/coarsening/CMakeLists.txt
  • tests/simulator/utilities/field_coarsening.py
  • tests/amr/data/field/coarsening/input/input_1d_ratio_2.txt
  • tests/amr/data/field/coarsening/test_linear_coarsen.hpp
🚧 Files skipped from review as they are similar to previous changes (33)
  • src/amr/data/field/coarsening/coarsen_weighter.hpp
  • src/core/data/grid/grid.hpp
  • tests/simulator/CMakeLists.txt
  • src/amr/data/particles/refine/split.hpp
  • tests/amr/data/field/refine/CMakeLists.txt
  • src/core/utilities/box/box.hpp
  • .github/workflows/cmake_macos.yml
  • res/cmake/options.cmake
  • tests/simulator/advance/CMakeLists.txt
  • tests/simulator/initialize/test_fields_init_2d.py
  • tests/simulator/advance/test_fields_advance_2d.py
  • pyphare/pyphare/pharesee/run/utils.py
  • tests/diagnostic/job_3d.py.in
  • tests/diagnostic/CMakeLists.txt
  • src/amr/data/particles/refine/particles_data_split.hpp
  • tests/simulator/initialize/CMakeLists.txt
  • pyphare/pyphare/pharesee/hierarchy/hierarchy.py
  • tests/core/data/ndarray/test_main.cpp
  • src/core/data/grid/gridlayoutdefs.hpp
  • tests/amr/data/field/refine/test_default_field_refinement.cpp
  • tests/amr/data/particles/refine/test_split.cpp
  • tests/simulator/advance/test_particles_advance_3d.py
  • tests/simulator/initialize/test_particles_init_3d.py
  • tests/simulator/utilities/test_refine_field.py
  • tests/amr/data/field/coarsening/test_weighter_coarsen.hpp
  • tests/simulator/initialize/test_particles_init_2d.py
  • res/amr/splitting.yml
  • tests/simulator/initialize/test_fields_init_3d.py
  • src/core/data/particles/particle.hpp
  • tests/amr/data/field/refine/test_field_refinement_on_hierarchy.cpp
  • tests/simulator/advance/test_fields_advance_1d.py
  • tests/amr/data/particles/refine/input/input_3d_ratio_2.txt
  • src/amr/data/particles/refine/split_1d.hpp
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.hpp`: Review the C++ code, point out issues relative t...

**/*.hpp: Review the C++ code, point out issues relative to principles of clean code, expressiveness, and performance.

  • tests/simulator/per_test.hpp
  • src/core/utilities/meta/meta_utilities.hpp
  • src/amr/data/particles/refine/splitter.hpp
  • src/core/data/ndarray/ndarray_vector.hpp
  • src/amr/data/particles/refine/split_3d.hpp
  • src/amr/data/field/coarsening/magnetic_field_coarsener.hpp
  • src/amr/data/particles/refine/split_2d.hpp
🪛 Ruff (0.8.2)
tests/simulator/__init__.py

326-326: Do not use mutable data structures for argument defaults

Replace with None; initialize within function

(B006)

pyphare/pyphare/core/box.py

207-207: Ambiguous variable name: l

(E741)

tests/functional/harris/harris_3d.py

62-62: Ambiguous variable name: l

(E741)

tests/amr/data/field/coarsening/coarsening_utils.py

181-182: Use a single if statement instead of nested if statements

(SIM102)

pyphare/pyphare/pharesee/geometry.py

161-161: Ambiguous variable name: l

(E741)


164-164: Ambiguous variable name: l

(E741)


167-167: Ambiguous variable name: l

(E741)


188-188: Ambiguous variable name: l

(E741)

tests/simulator/test_initialization.py

394-394: Function definition does not bind loop variable select

(B023)


396-396: Function definition does not bind loop variable patch

(B023)


396-396: Function definition does not bind loop variable nbrGhosts

(B023)


397-397: Function definition does not bind loop variable select

(B023)


582-582: Loop control variable pi not used within loop body

Rename unused pi to _pi

(B007)


586-586: Yoda condition detected

Rewrite as ppc == H

(SIM300)


664-664: Local variable out is assigned to but never used

Remove assignment to unused variable out

(F841)

tools/python3/git.py

22-22: Local variable e is assigned to but never used

Remove assignment to unused variable e

(F841)


29-29: Undefined name delete_branch

(F821)

tests/simulator/test_advance.py

290-290: Loop control variable ilvl not used within loop body

Rename unused ilvl to _ilvl

(B007)

tools/python3/cmake.py

48-48: Undefined name cmd

(F821)

🪛 Cppcheck (2.10-2)
tests/diagnostic/test-diagnostics_3d.cpp

[performance] 24-24: Function parameter 'path' should be passed by const reference.

(passedByValue)

⏰ Context from checks skipped due to timeout of 90000ms (5)
  • GitHub Check: build (macos-latest)
  • GitHub Check: build (macos-14)
  • GitHub Check: Analyze (cpp)
  • GitHub Check: build (macos-13)
  • GitHub Check: build (ubuntu-latest)
🔇 Additional comments (27)
tests/diagnostic/test-diagnostics_3d.cpp (3)

4-4: Consider moving test implementation to a header file.

The .ipp extension typically indicates an implementation file. Consider moving the implementation to a proper header file (.hpp) to follow standard C++ practices.


9-27: LGTM! Well-structured test cases.

The test cases follow a consistent pattern and cover different aspects of the 3D simulator (fluid dynamics, particle behavior, electromagnetic fields, and Python integration).

🧰 Tools
🪛 Cppcheck (2.10-2)

[performance] 24-24: Function parameter 'path' should be passed by const reference.

(passedByValue)


30-35: LGTM! Standard test initialization.

The main function properly initializes both Google Test and PHARE lifecycle management.

src/core/data/ndarray/ndarray_vector.hpp (1)

123-124: LGTM! The new z-dimension accessor methods are consistent with existing patterns.

The implementation follows the same pattern as xstart/xend and ystart/yend methods, maintaining code consistency.

src/core/utilities/meta/meta_utilities.hpp (2)

9-11: LGTM! Well-structured preprocessor directive.

The directive follows C++ best practices by checking if the macro is already defined and providing a sensible default value that enables all simulator dimensions.


90-91: Address TODO comments before merging.

The TODO comments indicate incomplete implementation for 3D particle number permutations. This could affect the functionality of 3D simulations.

Please clarify:

  1. What are the missing permutations?
  2. Are these permutations critical for 3D functionality?
  3. What is the timeline for implementing these permutations?
res/cmake/funcs.cmake (1)

59-67: Validate forced serial tests when testMPI is enabled.
The logic here excludes tests from being built if forceSerialTests is not set. If the developer mistakenly forgets to set it, tests may get silently skipped. Consider adding a warning message to help troubleshoot.

src/amr/data/particles/refine/split_3d.hpp (2)

90-116: Make constructor of WhitePattern<DimConst<3>> publicly accessible or confirm it’s intended to be private.
Classes default to private members. If you actually instantiate WhitePattern<DimConst<3>> outside this file, a private constructor may cause compilation issues.


156-165: Validate the inclusion of BlackPattern<DimConst<3>>.
The BlackPattern appears in the dispatcher for 27-particle splits but isn’t defined here. Ensure it's included or defined in another header to avoid linkage or compilation errors.

tests/simulator/per_test.hpp (1)

111-117: LGTM! Well-structured 3D test setup.

The implementation follows the established pattern from 1D and 2D test structures, maintaining consistency across dimensions.

tests/simulator/advance/test_fields_advance_3d.py (1)

1-4: LGTM! Well-documented and comprehensive test suite.

The test file is well-structured with clear documentation, good test parameterization, and comprehensive coverage of 3D field advancement scenarios.

src/amr/data/particles/refine/splitter.hpp (1)

18-30: LGTM! Well-documented pattern structure.

The documentation clearly explains the purpose and behavior of the SplitPattern struct.

tests/functional/harris/harris_3d.py (1)

138-139: LGTM!

The function correctly generates plot filenames with appropriate parameters.

tests/simulator/refined_particle_nbr.py (3)

33-36: LGTM!

The tearDown method correctly handles simulator cleanup with appropriate null checks.


106-116: LGTM!

The test_3d method and its constants are well-documented with clear explanations of the expected ratios.


130-136: LGTM!

The test_values method provides comprehensive coverage across dimensions and interpolation orders.

tests/simulator/__init__.py (1)

98-108: LGTM!

The function correctly implements 3D density calculation following the established pattern from 1D and 2D implementations.

src/amr/data/particles/refine/split_2d.hpp (1)

21-327: LGTM! The renaming improves code clarity.

The changes consistently rename Dispatcher to Pattern across all structures and type aliases, which better reflects their purpose in the codebase. This standardization of naming conventions enhances code readability and maintainability.

src/amr/data/field/coarsening/magnetic_field_coarsener.hpp (2)

38-42: LGTM! The template parameter renaming improves consistency.

The change from dimension to dim makes the code more concise while maintaining clarity.


82-333: Consider unifying duplicate logic across dimension-specific coarsening methods.

The coarsening methods for 1D, 2D, and 3D contain similar patterns of checking primal vs. dual centering and applying weighted averages. Consider extracting these common patterns into helper functions to reduce code duplication and improve maintainability.

For example, create a helper function to handle the centering checks and weight calculations:

template<std::size_t dim>
struct CoarseningWeights {
    static constexpr float getPrimalWeight() { return 0.5f; }
    static constexpr float getDualWeight() { return 0.25f; }
    
    static std::vector<float> getWeights(bool isPrimal) {
        return isPrimal ? 
            std::vector<float>{0.25f, 0.5f, 0.25f} : 
            std::vector<float>{0.5f, 0.5f};
    }
};
tests/amr/data/field/coarsening/coarsening_utils.py (1)

144-290: Consider systematically generating coarsening weights.

The code contains repetitive patterns for computing weighted averages based on primal/dual states. Consider encapsulating these patterns in a helper function or dictionary.

Example:

def get_weights(is_primal):
    """Return shift and weight arrays based on primal/dual state."""
    if is_primal:
        return [-1, 0, 1], [0.25, 0.5, 0.25]
    return [0, 1], [0.5, 0.5]

# Usage
ishift, iweights = get_weights(is_primal[0])
jshift, jweights = get_weights(is_primal[1])
kshift, kweights = get_weights(is_primal[2])
🧰 Tools
🪛 Ruff (0.8.2)

181-182: Use a single if statement instead of nested if statements

(SIM102)

pyphare/pyphare/pharesee/geometry.py (2)

63-74: Well-structured 3D ghost box implementation!

The extension to 3D maintains consistency with existing 1D and 2D implementations while following standard naming conventions (front/back) for the new dimension.


519-520: LGTM!

The list comprehension effectively handles the box removal operation.

tests/simulator/test_initialization.py (3)

254-353: Well-structured 3D magnetic field testing with performance tracking!

The implementation effectively extends magnetic field testing to 3D while adding useful timing measurements for performance analysis.


417-480: Well-calibrated 3D density testing!

The implementation effectively extends density testing to 3D with appropriate empirical deviation values for each dimension.

🧰 Tools
🪛 Ruff (0.8.2)

452-452: Local variable x is assigned to but never used

Remove assignment to unused variable x

(F841)


415-417: LGTM!

The addition of the cells parameter with a sensible default value enhances the function's flexibility while maintaining backward compatibility.

tests/simulator/test_advance.py (1)

342-354: LGTM!

The implementation effectively tracks successful tests and includes appropriate assertions to validate test execution.

part.v[2] = +1.;
}

interpolator(particles, rho, v, layout);
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Fix the interpolator call signature.

The interpolator call signature differs from the 1D and 2D implementations, which use makeIndexRange.

Apply this diff to maintain consistency with other dimensions:

-        interpolator(particles, rho, v, layout);
+        interpolator(makeIndexRange(particles), rho, v, layout);
📝 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
interpolator(particles, rho, v, layout);
interpolator(makeIndexRange(particles), rho, v, layout);

using Grid_t = typename PHARE_TYPES::Grid_t;
using UsableVecFieldND = UsableVecField<dim>;

GridLayout_t layout{ConstArray<double, dim>(.1), {nx, ny}, ConstArray<double, dim>(0)};
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix the grid layout initialization.

The grid layout initialization is incorrect as it only specifies two dimensions ({nx, ny}) for a 3D grid.

Apply this diff to fix the grid layout initialization:

-    GridLayout_t layout{ConstArray<double, dim>(.1), {nx, ny}, ConstArray<double, dim>(0)};
+    GridLayout_t layout{ConstArray<double, dim>(.1), {nx, ny, nz}, ConstArray<double, dim>(0)};
📝 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
GridLayout_t layout{ConstArray<double, dim>(.1), {nx, ny}, ConstArray<double, dim>(0)};
GridLayout_t layout{ConstArray<double, dim>(.1), {nx, ny, nz}, ConstArray<double, dim>(0)};

Comment on lines +774 to +781
TYPED_TEST_P(ACollectionOfParticles_3d, DepositCorrectlyTheirWeight_3d)
{
// auto const& [vx, vy, vz] = this->v();
// EXPECT_DOUBLE_EQ(this->rho(7, 7, 7), 1.0);
// EXPECT_DOUBLE_EQ(vx(7, 7, 7), 2.0);
// EXPECT_DOUBLE_EQ(vy(7, 7, 7), -1.0);
// EXPECT_DOUBLE_EQ(vz(7, 7, 7), 1.0);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Uncomment and fix the test assertions.

The test case is currently commented out, which is flagged by static analysis. The test should verify the correct deposition of weights and velocities.

Apply this diff to restore and fix the test assertions:

-    // auto const& [vx, vy, vz] = this->v();
-    // EXPECT_DOUBLE_EQ(this->rho(7, 7, 7), 1.0);
-    // EXPECT_DOUBLE_EQ(vx(7, 7, 7), 2.0);
-    // EXPECT_DOUBLE_EQ(vy(7, 7, 7), -1.0);
-    // EXPECT_DOUBLE_EQ(vz(7, 7, 7), 1.0);
+    auto idx = 2 + this->layout.nbrGhosts(QtyCentering::dual);
+    auto const& [vx, vy, vz] = this->v();
+    EXPECT_DOUBLE_EQ(this->rho(idx, idx, idx), 1.0);
+    EXPECT_DOUBLE_EQ(vx(idx, idx, idx), 2.0);
+    EXPECT_DOUBLE_EQ(vy(idx, idx, idx), -1.0);
+    EXPECT_DOUBLE_EQ(vz(idx, idx, idx), 1.0);
📝 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
TYPED_TEST_P(ACollectionOfParticles_3d, DepositCorrectlyTheirWeight_3d)
{
// auto const& [vx, vy, vz] = this->v();
// EXPECT_DOUBLE_EQ(this->rho(7, 7, 7), 1.0);
// EXPECT_DOUBLE_EQ(vx(7, 7, 7), 2.0);
// EXPECT_DOUBLE_EQ(vy(7, 7, 7), -1.0);
// EXPECT_DOUBLE_EQ(vz(7, 7, 7), 1.0);
}
TYPED_TEST_P(ACollectionOfParticles_3d, DepositCorrectlyTheirWeight_3d)
{
auto idx = 2 + this->layout.nbrGhosts(QtyCentering::dual);
auto const& [vx, vy, vz] = this->v();
EXPECT_DOUBLE_EQ(this->rho(idx, idx, idx), 1.0);
EXPECT_DOUBLE_EQ(vx(idx, idx, idx), 2.0);
EXPECT_DOUBLE_EQ(vy(idx, idx, idx), -1.0);
EXPECT_DOUBLE_EQ(vz(idx, idx, idx), 1.0);
}

Comment on lines +47 to +48
def run_test(test, verbose=False, capture_output=False):
run(test_cmd(cmd, verbose=verbose), capture_output=capture_output)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix undefined variable in run_test function.

The function uses an undefined variable cmd instead of the test parameter.

Apply this diff to fix the variable name:

-    run(test_cmd(cmd, verbose=verbose), capture_output=capture_output)
+    run(test_cmd(test, verbose=verbose), capture_output=capture_output)
📝 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
def run_test(test, verbose=False, capture_output=False):
run(test_cmd(cmd, verbose=verbose), capture_output=capture_output)
def run_test(test, verbose=False, capture_output=False):
run(test_cmd(test, verbose=verbose), capture_output=capture_output)
🧰 Tools
🪛 Ruff (0.8.2)

48-48: Undefined name cmd

(F821)

Comment on lines +23 to +27
def config(
path, samrai_dir=None, cxx_flags=None, use_ninja=False, use_ccache=False, extra=""
):
cmd = make_config_str(path, samrai_dir, cxx_flags, use_ninja, extra)
run(cmd, capture_output=False)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix missing parameter in config function.

The use_ccache parameter is not being passed to make_config_str(), which could prevent ccache from being enabled.

Apply this diff to fix the parameter passing:

-    cmd = make_config_str(path, samrai_dir, cxx_flags, use_ninja, extra)
+    cmd = make_config_str(path, samrai_dir, cxx_flags, use_ninja, use_ccache, extra)
📝 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
def config(
path, samrai_dir=None, cxx_flags=None, use_ninja=False, use_ccache=False, extra=""
):
cmd = make_config_str(path, samrai_dir, cxx_flags, use_ninja, extra)
run(cmd, capture_output=False)
def config(
path, samrai_dir=None, cxx_flags=None, use_ninja=False, use_ccache=False, extra=""
):
cmd = make_config_str(path, samrai_dir, cxx_flags, use_ninja, use_ccache, extra)
run(cmd, capture_output=False)

Comment on lines +104 to +108
def T(x, y, z):
K = 1
temp = 1.0 / density(x, y, z) * (K - b2(x, y, z) * 0.5)
assert np.all(temp > 0)
return temp
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Prevent potential assertion errors in temperature calculation.

The temperature function could raise an assertion error if b2(x, y, z) * 0.5 exceeds K = 1. Consider adding a safety margin to K or implementing bounds checking for the magnetic field pressure.

Apply this diff to add bounds checking:

-    K = 1
+    K = 1.5  # Increased to provide safety margin
     temp = 1.0 / density(x, y, z) * (K - b2(x, y, z) * 0.5)
-    assert np.all(temp > 0)
+    if not np.all(temp > 0):
+        raise ValueError("Temperature calculation resulted in negative values. "
+                        "Consider adjusting K or magnetic field components.")
     return temp
📝 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
def T(x, y, z):
K = 1
temp = 1.0 / density(x, y, z) * (K - b2(x, y, z) * 0.5)
assert np.all(temp > 0)
return temp
def T(x, y, z):
- K = 1
+ K = 1.5 # Increased to provide safety margin
temp = 1.0 / density(x, y, z) * (K - b2(x, y, z) * 0.5)
- assert np.all(temp > 0)
+ if not np.all(temp > 0):
+ raise ValueError("Temperature calculation resulted in negative values. "
+ "Consider adjusting K or magnetic field components.")
return temp

Copy link

@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

🧹 Nitpick comments (1)
tests/simulator/test_advance.py (1)

290-290: Rename unused loop variable.

The loop control variable ilvl is not used within the loop body.

-for ilvl, overlaps in hierarchy_overlaps(datahier, coarsest_time).items():
+for _ilvl, overlaps in hierarchy_overlaps(datahier, coarsest_time).items():
🧰 Tools
🪛 Ruff (0.8.2)

290-290: Loop control variable ilvl not used within loop body

Rename unused ilvl to _ilvl

(B007)

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 72ab7cc and 5a4071c.

📒 Files selected for processing (1)
  • tests/simulator/test_advance.py (16 hunks)
🧰 Additional context used
🪛 Ruff (0.8.2)
tests/simulator/test_advance.py

290-290: Loop control variable ilvl not used within loop body

Rename unused ilvl to _ilvl

(B007)

⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: build (macos-latest)
  • GitHub Check: Analyze (cpp)
  • GitHub Check: build (macos-14)
  • GitHub Check: build (ubuntu-latest)
  • GitHub Check: Analyze (python)
  • GitHub Check: build (macos-13)
🔇 Additional comments (4)
tests/simulator/test_advance.py (4)

9-9: LGTM! Clear documentation and well-organized imports.

The added import and expanded class documentation effectively clarify the purpose of the class and its relationship with dimension-specific tests.

Also applies to: 32-42


285-340: Improved error handling and test success tracking.

The changes enhance debugging capabilities and make the test results more accurate by:

  1. Using a more descriptive variable name success_test_nbr
  2. Adding detailed error information for debugging
  3. Simplifying box transformation logic with amr_to_local
🧰 Tools
🪛 Ruff (0.8.2)

290-290: Loop control variable ilvl not used within loop body

Rename unused ilvl to _ilvl

(B007)


415-417: LGTM! Enhanced test parameterization.

The addition of the cells parameter to both test methods improves test flexibility by allowing customization of the grid size.

Also applies to: 443-445


555-587: Improved error reporting in coarsening tests.

The enhanced error reporting now includes detailed information about the failing indices and box configurations, making it easier to debug test failures.

Copy link

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

♻️ Duplicate comments (8)
tools/python3/git.py (1)

27-35: ⚠️ Potential issue

Missing delete_branch function referenced in checkout.

The checkout function references a delete_branch function that is not defined in this file, causing a runtime error when recreate=True. This issue was previously flagged in a past review comment.

Add the missing delete_branch function:

+def delete_branch(branch):
+    """Delete a Git branch if it exists."""
+    if branch_exists(branch):
+        run(f"git branch -D {branch}")
+

 def checkout(branch, create=False, recreate=False):
     if recreate:
         delete_branch(branch)
         create = True

     if create and not branch_exists(branch):
         run(f"git checkout -b {branch}")
     else:
         run(f"git checkout {branch}")
🧰 Tools
🪛 Ruff (0.8.2)

29-29: Undefined name delete_branch

(F821)

tests/amr/data/field/coarsening/coarsening_utils.py (1)

144-168: 🛠️ Refactor suggestion

Refactor repeated weight computation patterns into a reusable function.

This section and several similar blocks below use the same pattern of nested loops with shifts and weights to compute weighted averages. This pattern should be extracted into a helper function to reduce code duplication.

Create a helper function for the triple-nested loop pattern:

+def compute_weighted_value(fineData, fineIndexX, fineIndexY, fineIndexZ, 
+                        ishift, iweights, jshift, jweights, kshift, kweights):
+    value = 0.0
+    for i, iweight in zip(ishift, iweights):
+        yvalue = 0.0
+        for j, jweight in zip(jshift, jweights):
+            zvalue = 0.0
+            for k, kweight in zip(kshift, kweights):
+                zvalue += (
+                    fineData[fineIndexX + i][fineIndexY + j][
+                        fineIndexZ + k
+                    ]
+                    * kweight
+                )
+            yvalue += zvalue * jweight
+        value += yvalue * iweight
+    return value

 # all primal is for moments
 if all(is_primal):
-    value = 0.0
     ishift = (-1, 0, 1)
     iweights = (0.25, 0.5, 0.25)
     jshift = (-1, 0, 1)
     jweights = (0.25, 0.5, 0.25)
     kshift = (-1, 0, 1)
     kweights = (0.25, 0.5, 0.25)
-    for i, iweight in zip(ishift, iweights):
-        yvalue = 0.0
-        for j, jweight in zip(jshift, jweights):
-            zvalue = 0.0
-            for k, kweight in zip(kshift, kweights):
-                zvalue += (
-                    fineData[fineIndexX + i][fineIndexY + j][
-                        fineIndexZ + k
-                    ]
-                    * kweight
-                )
-            yvalue += zvalue * jweight
-        value += yvalue * iweight
+    value = compute_weighted_value(fineData, fineIndexX, fineIndexY, fineIndexZ,
+                               ishift, iweights, jshift, jweights, kshift, kweights)
     coarseData[
         coarseLocalIndexX, coarseLocalIndexY, coarseLocalIndexZ
     ] = value

This pattern can then be reused for the other similar blocks at lines 203-230, 232-261, and 263-292.

tests/core/numerics/interpolator/test_main.cpp (3)

739-739: ⚠️ Potential issue

Fix the grid layout initialization to include all three dimensions.

The grid layout initialization is incorrect as it only specifies two dimensions ({nx, ny}) for a 3D grid.

Apply this diff to fix the grid layout initialization:

-    GridLayout_t layout{ConstArray<double, dim>(.1), {nx, ny}, ConstArray<double, dim>(0)};
+    GridLayout_t layout{ConstArray<double, dim>(.1), {nx, ny, nz}, ConstArray<double, dim>(0)};

768-768: ⚠️ Potential issue

Fix the interpolator call signature to match 1D and 2D implementations.

The interpolator call signature is inconsistent with the 1D and 2D implementations, which use makeIndexRange.

Apply this diff to maintain consistency with other dimensions:

-        interpolator(particles, rho, v, layout);
+        interpolator(makeIndexRange(particles), rho, v, layout);

773-781: ⚠️ Potential issue

Uncomment and fix the test assertions to properly test 3D interpolation.

The test case is currently commented out, which means it's not performing any actual verification. This test should be uncommented and updated to test the correct deposition of weights and velocities at the appropriate index.

Apply this diff to restore and fix the test assertions:

-    // auto const& [vx, vy, vz] = this->v();
-    // EXPECT_DOUBLE_EQ(this->rho(7, 7, 7), 1.0);
-    // EXPECT_DOUBLE_EQ(vx(7, 7, 7), 2.0);
-    // EXPECT_DOUBLE_EQ(vy(7, 7, 7), -1.0);
-    // EXPECT_DOUBLE_EQ(vz(7, 7, 7), 1.0);
+    auto idx = 2 + this->layout.nbrGhosts(QtyCentering::dual);
+    auto const& [vx, vy, vz] = this->v();
+    EXPECT_DOUBLE_EQ(this->rho(idx, idx, idx), 1.0);
+    EXPECT_DOUBLE_EQ(vx(idx, idx, idx), 2.0);
+    EXPECT_DOUBLE_EQ(vy(idx, idx, idx), -1.0);
+    EXPECT_DOUBLE_EQ(vz(idx, idx, idx), 1.0);
tests/functional/harris/harris_3d.py (1)

104-108: ⚠️ Potential issue

Prevent potential assertion errors in temperature calculation.

The temperature function could raise an assertion error if b2(x, y, z) * 0.5 exceeds K = 1. This would happen if the magnetic field pressure becomes too high, leading to failed simulations.

Apply this diff to add bounds checking:

-    K = 1
+    K = 1.5  # Increased to provide safety margin
     temp = 1.0 / density(x, y, z) * (K - b2(x, y, z) * 0.5)
-    assert np.all(temp > 0)
+    if not np.all(temp > 0):
+        raise ValueError("Temperature calculation resulted in negative values. "
+                        "Consider adjusting K or magnetic field components.")
     return temp
tools/python3/cmake.py (2)

47-48: ⚠️ Potential issue

Fix undefined variable in run_test function.

The function uses an undefined variable cmd instead of the test parameter.

-    run(test_cmd(cmd, verbose=verbose), capture_output=capture_output)
+    run(test_cmd(test, verbose=verbose), capture_output=capture_output)
🧰 Tools
🪛 Ruff (0.8.2)

48-48: Undefined name cmd

(F821)


23-27: ⚠️ Potential issue

Fix missing parameter in config function.

The use_ccache parameter is not being passed to make_config_str(), which will prevent ccache from being enabled when specified.

-    cmd = make_config_str(path, samrai_dir, cxx_flags, use_ninja, extra)
+    cmd = make_config_str(path, samrai_dir, cxx_flags, use_ninja, use_ccache, extra)
🧹 Nitpick comments (37)
src/core/utilities/meta/meta_utilities.hpp (1)

90-97:

❓ Verification inconclusive

Consider optimizing 3D particle permutations.

The implementation of 3D simulator options is solid, but note the TODO comment about adding more 3D particle permutations. In line 94, there's a commented-out value 27 that might be needed in the future.

For 3D simulations, particle counts can grow substantially. Consider implementing more efficient data structures or algorithms to handle larger particle counts, especially when working with InterpConst<3> which likely requires more computational resources.


🏁 Script executed:

#!/bin/bash
# Description: Check if there are any performance optimizations or memory concerns addressed elsewhere
# in relation to 3D particle handling

echo "Searching for related performance optimizations for 3D particles..."
rg -A 3 "particle.*performance|memory.*optimization|3D.*optimization" --type cpp

Length of output: 227


Attention: Revisit 3D Simulator Options for Future Scalability

The implementation for 3D simulator options appears solid; however, the TODO comment highlights that additional 3D particle permutations (including the commented-out value 27) might be needed to manage larger particle counts. No further performance or memory optimization measures specific to 3D particles were found elsewhere in the code. Given the potential for scalability issues—especially with InterpConst<3>—please verify through manual benchmarks or further static analysis that the current design will not introduce performance bottlenecks when these additional permutations are later enabled.

src/core/data/ndarray/ndarray_vector.hpp (4)

123-124: Good implementation of zstart() and zend() methods.

The implementation correctly extends the existing pattern for 2D operations to support 3D operations. Consider whether having dimension-specific min/max values might be more flexible for future use cases instead of using the same min/max values for all dimensions.

- NO_DISCARD auto zstart() const { return mask_.min(); }
- NO_DISCARD auto zend() const { return shape_[2] - 1 - mask_.max(); }
+ NO_DISCARD auto zstart() const { return mask_.minZ(); }
+ NO_DISCARD auto zend() const { return shape_[2] - 1 - mask_.maxZ(); }

383-424: Implement boundary checks in 3D fill method to improve robustness.

The 3D fill implementation could benefit from boundary validations before execution to prevent potential out-of-bounds access. Also, there's a commented line // // right border on line 394 that has a double comment character, which should be fixed.

 template<typename Array>
 void fill3D(Array& array, typename Array::type val) const
 {
     auto shape = array.shape();
+    
+    // Validate that mask fits within the array dimensions
+    assert(max_ < shape[0]/2 && max_ < shape[1]/2 && max_ < shape[2]/2);

     // left border
     for (auto i = min_; i <= shape[0] - 1 - max_; ++i)
         for (auto j = min_; j <= shape[1] - 1 - max_; ++j)
             for (auto k = min_; k <= max_; ++k)
                 array(i, j, k) = val;

-    // // right border
+    // right border
     for (auto i = min_; i <= shape[0] - 1 - max_; ++i)
         for (auto j = min_; j <= shape[1] - 1 - max_; ++j)
             for (auto k = shape[2] - 1 - max_; k <= shape[2] - 1 - min_; ++k)
                 array(i, j, k) = val;

433-450: Add comments explaining the 3D cells calculation for better maintainability.

The 3D calculation in nCells is complex and would benefit from a comment explaining the formula and reasoning. This would make future maintenance easier.

            if constexpr (Array::dimension == 3)
            {
                auto [x, y, z] = shape;
                x -= i * 2;
                y -= i * 2;
                z -= i * 2;
+               // Calculate total number of cells in 3D ghost layer:
+               // (x*y*2) -> front and back face cells
+               // (y*(z-2)*2) -> left and right face cells (excluding corners already counted)
+               // ((z-2)*(x-2)*2) -> bottom and top face cells (excluding corners and edges already counted)
                cells += (x * y * 2) + (y * (z - 2) * 2) + ((z - 2) * (x - 2) * 2);
            }

527-595: Well-structured implementation of 3D operator>> with good use of a lambda function.

The implementation for 3D MaskedView operator>> is thorough and follows the pattern established for 2D. The use of a lambda function for corner handling is a good approach to reduce code duplication. Consider further refactoring opportunities to make the code more maintainable:

  1. The edge handling could be factored into helper functions similar to the corner handling.
  2. Consider adding debug assertions to verify that values are properly transferred.

You could refactor the edge handling similar to the corner handling:

    if constexpr (MaskedView_t::dimension == 3)
    {
        assert(inner.zstart() > outer.zstart() and inner.zend() < outer.zend());

+       // Helper function for handling edges along x axis
+       auto edgeX = [&](auto i) {
+           for (auto k = inner.zstart(); k <= inner.zend(); ++k)
+           {
+               outer(i, outer.ystart(), k) = inner(i, inner.ystart(), k);
+               outer(i, outer.yend(), k)   = inner(i, inner.yend(), k);
+           }
+           for (auto j = inner.ystart(); j <= inner.yend(); ++j)
+           {
+               outer(i, j, outer.zstart()) = inner(i, j, inner.zstart());
+               outer(i, j, outer.zend())   = inner(i, j, inner.zend());
+           }
+       };

        for (auto i = inner.xstart(); i <= inner.xend(); ++i)
        {
-           for (auto k = inner.zstart(); k <= inner.zend(); ++k)
-           {
-               outer(i, outer.ystart(), k) = inner(i, inner.ystart(), k);
-               outer(i, outer.yend(), k)   = inner(i, inner.yend(), k);
-           }
-           for (auto j = inner.ystart(); j <= inner.yend(); ++j)
-           {
-               outer(i, j, outer.zstart()) = inner(i, j, inner.zstart());
-               outer(i, j, outer.zend())   = inner(i, j, inner.zend());
-           }
+           edgeX(i);
        }
src/amr/data/particles/refine/split_3d.hpp (2)

15-18: Avoid using namespace in a header file.

Inclusion of using namespace PHARE::core; within a header file can cause unexpected namespace pollutions across the codebase. It’s usually best to qualify symbols or use namespace aliases in source files rather than in headers.


119-294: Consider refactoring repeated splitter definitions.

Many Splitter specializations differ only by small constants or pattern combinations. Centralizing these repetitive definitions via macros or more generic templates could reduce boilerplate and promote easier maintenance.

tests/simulator/__init__.py (1)

98-108: Optional docstring for 3D density function.

Although consistent with the 1D/2D variants, it may be helpful to provide a docstring explaining the 3D density calculation logic and any assumptions or domain constraints. This helps clarify its usage and maintainability.

tests/simulator/advance/test_particles_advance_3d.py (1)

25-43: Method naming inconsistency with actual implementation.

The method name test_overlapped_particledatas_have_identical_particles doesn't match the actual implementation method _test_overlapped_particledatas_have_identical_particles. This mismatch can cause confusion for maintainers.

Consider renaming for consistency:

-def test_overlapped_particledatas_have_identical_particles(
+def test_overlapped_particledatas_have_identical_particles(
     self, interp_order, refinement_boxes
 ):
     self._test_overlapped_particledatas_have_identical_particles(
         ndim,
         interp_order,
         refinement_boxes,
         ppc=ppc,
         cells=40,
         largest_patch_size=20,
     )
pyphare/pyphare/core/box.py (2)

206-207: Ambiguous variable names in select function.

The single-letter variable names l and u in the list comprehension make the code less readable and maintainable. This was also flagged by static analysis.

-def select(data, box):
-    return data[tuple([slice(l, u + 1) for l,u in zip(box.lower, box.upper)])]
+def select(data, box):
+    return data[tuple([slice(lower, upper + 1) for lower, upper in zip(box.lower, box.upper)])]
🧰 Tools
🪛 Ruff (0.8.2)

207-207: Ambiguous variable name: l

(E741)


222-223: Consider adding docstring to the setitem method.

While the implementation is concise, adding a docstring would improve clarity about what's happening - the method gets the array view via getitem and then assigns values to it using slice assignment.

 def __setitem__(self, box_or_slice, val):
+    """Assign values to the selected region of data.
+    
+    Gets a view of the data via __getitem__ and then uses slice assignment to modify it.
+    """
     self.__getitem__(box_or_slice)[:] = val
tools/python3/git.py (2)

5-16: Add docstrings to Git utility functions.

The current_branch, hashes, and log functions lack proper docstrings. Adding documentation would improve code maintainability and help users understand the purpose and expected behavior of these functions.

 def current_branch():
+    """Return the name of the current Git branch."""
     return decode_bytes(run("git branch --show-current").stdout)


 def hashes(N=1):
+    """Return a list of the last N commit hashes.
+    
+    Args:
+        N: Number of commit hashes to return
+    
+    Returns:
+        List of commit hash strings
+    """
     return decode_bytes(run(f"git log -{N} --pretty=format:%h").stdout).splitlines()


 def log(N=10, use_short=False):
+    """Return the Git log for the last N commits.
+    
+    Args:
+        N: Number of commits to include in the log
+        use_short: When True, uses a compact format for log output
+    
+    Returns:
+        String containing the git log output
+    """
     # https://git-scm.com/docs/pretty-formats
     short = '--pretty=format:"%h%x09%an%x09%ad%x09%s"' if use_short else ""
     return decode_bytes(run(f"git log -{N} {short}").stdout)

19-24: Unused exception variable.

The exception variable e is assigned but never used. This was flagged by static analysis.

 def branch_exists(branch):
     try:
         run(f"git show-branch {branch}", check=True)
-    except subprocess.CalledProcessError as e:
+    except subprocess.CalledProcessError:
         return False  # exit failure means branch does not exist
     return True
🧰 Tools
🪛 Ruff (0.8.2)

22-22: Local variable e is assigned to but never used

Remove assignment to unused variable e

(F841)

tests/amr/data/field/coarsening/coarsening_utils.py (4)

8-22: Implementation looks good but docstring could be improved.

The function's docstring explains the purpose clearly, but could be enhanced by adding parameter descriptions and return value information.

 def coarsen(qty, coarseField, fineField, coarseBox, fineData, coarseData):
     """
     this function takes data in fineData (the dataset from the fineFied FieldData object)
     and put it in the coarseData dataset

     The coarsening method is the equivalent (and union) of the C++ coarsening methods,
     namely:
     - DefaultFieldCoarsener, used for all fields (moments and E) but the magnetic field B
     - MagneticFieldCoarsener, used for the magnetic field B

     as in the C++ counterpart, we hard-code here that the layout of B follows the yee layout.
     That is Bx is PDD, By is DPD and Bz is DDP
+    
+    Parameters:
+    -----------
+    qty : str
+        The field quantity name ("Bx", "By", "Bz", etc.)
+    coarseField : Field
+        The coarse field object
+    fineField : Field
+        The fine field object
+    coarseBox : Box
+        The coarse box defining the region to fill
+    fineData : ndarray
+        The source data from the fine field
+    coarseData : ndarray
+        The target data array for the coarse field
     """

80-95: Boolean condition should be checked first before further processing.

The code performs field-specific calculations after checking the primality, but for clarity and efficiency, it should check if the quantity is "Bx" first before calculating temporary values that might not be used.

 if is_primal[0] and not is_primal[1]:
-    if qty == "Bx":
-        coarseData[coarseLocalIndexX, coarseLocalIndexY] = 0.5 * (
-            fineData[fineIndexX, fineIndexY]
-            + fineData[fineIndexX, fineIndexY + 1]
-        )
-    else:
+    if qty != "Bx":
         left += fineData[fineIndexX - 1][fineIndexY] * 0.5
         left += fineData[fineIndexX - 1][fineIndexY + 1] * 0.5
         middle += fineData[fineIndexX][fineIndexY] * 0.5
         middle += fineData[fineIndexX][fineIndexY + 1] * 0.5
         right += fineData[fineIndexX + 1][fineIndexY] * 0.5
         right += fineData[fineIndexX + 1][fineIndexY + 1] * 0.5
         coarseData[coarseLocalIndexX][coarseLocalIndexY] = (
             left * 0.25 + middle * 0.5 + right * 0.25
         )
+    else:
+        coarseData[coarseLocalIndexX, coarseLocalIndexY] = 0.5 * (
+            fineData[fineIndexX, fineIndexY]
+            + fineData[fineIndexX, fineIndexY + 1]
+        )

181-182: Use a single if statement instead of nested statements.

The code uses an elif statement after checking conditions in the previous if block, but these conditions are mutually exclusive. Converting this to a separate if statement improves readability.

-elif not is_primal[0] and is_primal[1] and not is_primal[2]:
+if not is_primal[0] and is_primal[1] and not is_primal[2]:
     if qty == "By":
         coarseData[
             coarseLocalIndexX, coarseLocalIndexY, coarseLocalIndexZ
         ] = 0.25 * (
🧰 Tools
🪛 Ruff (0.8.2)

181-182: Use a single if statement instead of nested if statements

(SIM102)


170-190: Improve consistency in conditional checks for magnetic field components.

The code structure varies between different conditional blocks. For consistent code structure, use the same pattern for all magnetic field components.

 if is_primal[0] and not is_primal[1] and not is_primal[2]:
-    if qty == "Bx":
-        coarseData[
-            coarseLocalIndexX, coarseLocalIndexY, coarseLocalIndexZ
-        ] = 0.25 * (
-            fineData[fineIndexX, fineIndexY, fineIndexZ]
-            + fineData[fineIndexX, fineIndexY + 1, fineIndexZ]
-            + fineData[fineIndexX, fineIndexY, fineIndexZ + 1]
-            + fineData[fineIndexX, fineIndexY + 1, fineIndexZ + 1]
-        )
+    # Check for Bx component with primality PDD
+    if qty == "Bx":
+        coarseData[
+            coarseLocalIndexX, coarseLocalIndexY, coarseLocalIndexZ
+        ] = 0.25 * (
+            fineData[fineIndexX, fineIndexY, fineIndexZ]
+            + fineData[fineIndexX, fineIndexY + 1, fineIndexZ]
+            + fineData[fineIndexX, fineIndexY, fineIndexZ + 1]
+            + fineData[fineIndexX, fineIndexY + 1, fineIndexZ + 1]
+        )
+    # For other fields, implement the appropriate coarsening algorithm
+    else:
+        # Add implementation for other fields with PDD primality
+        pass

Apply similar structure to the other magnetic field component blocks.

🧰 Tools
🪛 Ruff (0.8.2)

181-182: Use a single if statement instead of nested if statements

(SIM102)

tests/functional/harris/harris_3d.py (3)

62-62: Replace ambiguous variable name 'l' with more descriptive name.

The single-letter variable name 'l' is ambiguous and can be easily confused with the number '1' or capital letter 'I'. Use a more descriptive name like 'scale_length' for better readability.

-def S(y, y0, l):
-    return 0.5 * (1.0 + np.tanh((y - y0) / l))
+def S(y, y0, scale_length):
+    return 0.5 * (1.0 + np.tanh((y - y0) / scale_length))
🧰 Tools
🪛 Ruff (0.8.2)

62-62: Ambiguous variable name: l

(E741)


177-182: Address TODOs for 3D plotting functionality.

The TODOs indicate that 3D plotting functionality has been removed but no alternative has been implemented. This should be addressed to ensure proper visualization of 3D simulation results.

Would you like me to help implement a solution for 3D plotting? I could suggest approaches such as:

  1. Using libraries like Matplotlib's 3D plotting capabilities
  2. Creating slice-based 2D visualizations of 3D data
  3. Integrating with visualization tools like ParaView or VisIt

Let me know which approach you'd prefer and I can provide code implementation suggestions.


142-172: Clean up commented-out plotting code and improve active plotting functionality.

The plotting function contains several commented-out sections. Either remove these completely or implement them properly for 3D data. The current mixture of active and inactive code makes maintenance difficult.

Consider refactoring the plotting function to use a more modular approach:

 def plot(diag_dir):
     run = Run(diag_dir)
+    # Define available plot types and their configurations
+    plot_configs = {
+        "N": {"pop_name": "protons", "plot_patches": True},
+        "B": {"components": ["x", "y", "z"], "plot_patches": True}
+        # Add more plot types as needed
+    }
+    
     for time in timestamps:
-        # run.GetDivB(time).plot(
-        #     filename=plot_file_for_qty("divb", time),
-        #     plot_patches=True,
-        #     vmin=1e-11,
-        #     vmax=2e-10,
-        # )
-        # run.GetRanks(time).plot(
-        #     filename=plot_file_for_qty("Ranks", time),
-        #     plot_patches=True,
-        # )
-        run.GetN(time, pop_name="protons").plot(
-            filename=plot_file_for_qty("N", time),
-            plot_patches=True,
-        )
-        for c in ["x", "y", "z"]:
-            run.GetB(time).plot(
-                filename=plot_file_for_qty(f"b{c}", time),
-                qty=f"B{c}",
-                plot_patches=True,
-            )
-        # run.GetJ(time).plot(
-        #     filename=plot_file_for_qty("jz", time),
-        #     qty="Jz",
-        #     plot_patches=True,
-        #     vmin=-2,
-        #     vmax=2,
-        # )
+        # Plot density
+        if "N" in plot_configs:
+            cfg = plot_configs["N"]
+            run.GetN(time, pop_name=cfg["pop_name"]).plot(
+                filename=plot_file_for_qty("N", time),
+                plot_patches=cfg["plot_patches"],
+            )
+        
+        # Plot magnetic field components
+        if "B" in plot_configs:
+            cfg = plot_configs["B"]
+            for c in cfg["components"]:
+                run.GetB(time).plot(
+                    filename=plot_file_for_qty(f"b{c}", time),
+                    qty=f"B{c}",
+                    plot_patches=cfg["plot_patches"],
+                )
tests/simulator/refined_particle_nbr.py (1)

44-48: Remove the TODO comment and clarify the 3D calculation logic.

The TODO comment on line 47 asks about handling dim==3, but the code already implements this case on line 48. The logic for calculating the particle numbers in 3D needs better documentation to explain the formula.

-        dim2 = refined_particle_nbr * ((cellNbr[0] * 2 + (cellNbr[1] * 2)))
+        # Calculate particles on x-y surface boundaries
+        dim2 = refined_particle_nbr * ((cellNbr[0] * 2 + (cellNbr[1] * 2)))
         if dim == 2:
             return dim2
-        # TODO3D if dim==3?
+        # For 3D, also consider particles on z surface boundaries
         return dim2 * (cellNbr[2] * 2)
tools/python3/cmake.py (1)

4-5: Implement or remove the empty version function.

The version() function is defined but has no implementation (it just contains pass). This should either be implemented to return the CMake version or removed if not needed.

-def version():
-    pass
+def version():
+    """Return the CMake version."""
+    proc = run("cmake --version", capture_output=True)
+    output = decode_bytes(proc.stdout)
+    # Extract version from first line which is typically:
+    # cmake version X.Y.Z
+    return output.splitlines()[0].split(' ')[2]
pyphare/pyphare/pharesee/geometry.py (2)

159-182: Improve variable naming for better readability.

The code uses single-letter variable name l which is ambiguous and can be confused with the number 1 or capital I in some fonts. Replace it with a more descriptive name.

-        front = {
-            f"{k}front": [(v[0], v[1], shape_z) for v in l] for k, l in shifts.items()
-        }
-        back = {
-            f"{k}back": [([v[0], v[1], -shape_z]) for v in l] for k, l in shifts.items()
-        }
-
-        shifts = {k: [([v[0], v[1], 0]) for v in l] for k, l in shifts.items()}
+        front = {
+            f"{k}front": [(v[0], v[1], shape_z) for v in shift_list] for k, shift_list in shifts.items()
+        }
+        back = {
+            f"{k}back": [([v[0], v[1], -shape_z]) for v in shift_list] for k, shift_list in shifts.items()
+        }
+
+        shifts = {k: [([v[0], v[1], 0]) for v in shift_list] for k, shift_list in shifts.items()}
🧰 Tools
🪛 Ruff (0.8.2)

161-161: Ambiguous variable name: l

(E741)


164-164: Ambiguous variable name: l

(E741)


167-167: Ambiguous variable name: l

(E741)


188-188: Improve variable naming consistency.

Similar to the previous comment, the variable l is used here as well. Replace it with a more descriptive name for better readability.

-    shifts = {"".join(sorted(k)): l for k, l in shifts.items()}
+    shifts = {"".join(sorted(k)): shift_list for k, shift_list in shifts.items()}
🧰 Tools
🪛 Ruff (0.8.2)

188-188: Ambiguous variable name: l

(E741)

res/cmake/funcs.cmake (3)

44-57: Clarify usage of add_phare_test_ versus top-level test functions.

Currently, add_phare_test_ is invoked by both add_phare_test and add_no_mpi_phare_test. For new contributors, it may be unclear under which conditions each function should be used. Consider adding a small comment block or docstring to add_phare_test_ explaining its role (e.g., environment configuration vs. user-facing usage).


107-109: Remove or implement the placeholder function add_phare_build_test.

This function is currently empty, which might be confusing. If it is intentionally a no-op, document why. Otherwise, remove or implement it to avoid confusion.


185-191: Consider scoping or grouping the outputs of phare_print_all_vars.

While printing all CMake variables can help in debugging, it may generate noisy logs. Consider adding an optional parameter or filter (e.g., a prefix) to only print PHARE-specific variables, reducing excessive output during normal development.

src/amr/data/field/coarsening/magnetic_field_coarsener.hpp (1)

226-333: Consider merging 3D patterns using a dimension-agnostic approach.

Each 3D component block (Bx, By, Bz) follows the same multi-point averaging pattern. A dimension-agnostic loop or partial template specialization could reduce repetitive code and risk of bugs.

tests/simulator/test_advance.py (4)

289-290: Rename the unused loop variable ilvl.

In the loop “for ilvl, overlaps in hierarchy_overlaps(...):”, ilvl is not used within the body. Rename it to _ilvl or remove it to address potential linter warnings and clarify that it’s unused.

🧰 Tools
🪛 Ruff (0.8.2)

290-290: Loop control variable ilvl not used within loop body

Rename unused ilvl to _ilvl

(B007)


293-315: Check performance impact of repeated indexing in overlap slices.

Multiple transformations and slices (amr_to_local, boxm.shift, etc.) may introduce moderate overhead if the dataset is large and repeated frequently. If performance becomes critical, consider caching intermediate results to reduce repeated computations in tight loops.


545-556: Hardcoded sentinel value -144123.

Here, a sentinel is used to detect uninitialized or overridden array data. Document in code comments the meaning of -144123 or consider using a self-explanatory constant (e.g., INVALID_DATA_MARKER = -144123) to improve readability.


713-735: Enhance error reporting for sub-cycle interpolation tests.

In case of failures, the printed debug info is valuable but could be more structured (e.g., JSON or multiline formatting). Clear formatting often speeds up debugging.

tests/simulator/test_initialization.py (5)

417-420: Parameter naming inconsistency between methods

The parameter name has changed from dim to ndim in this method, while some other methods still use dim. Consider standardizing all parameter names to ndim for consistency.

- def _test_B_is_as_provided_by_user(self, dim, interp_order, ppc=100, **kwargs):
+ def _test_B_is_as_provided_by_user(self, ndim, interp_order, ppc=100, **kwargs):

Similar changes should be made throughout the file for consistency.

Also applies to: 425-425


582-582: Unused loop control variable

The loop control variable pi is not used within the loop body. Consider renaming it to _ to indicate it's intentionally unused.

- for pi, patch in enumerate(datahier.level(0).patches):
+ for _, patch in enumerate(datahier.level(0).patches):
🧰 Tools
🪛 Ruff (0.8.2)

582-582: Loop control variable pi not used within loop body

Rename unused pi to _pi

(B007)


585-586: Consider using NumPy's array testing methods for histogram verification

Instead of using a boolean assertion with .all(), consider using NumPy's testing utilities which provide better error messages on failure.

- self.assertTrue((H == ppc).all())
+ np.testing.assert_array_equal(H, ppc * np.ones_like(H), 
+                              err_msg="Not all cells have exactly ppc particles")
🧰 Tools
🪛 Ruff (0.8.2)

586-586: Yoda condition detected

Rewrite as ppc == H

(SIM300)


664-664: Unused variable out

The variable out is assigned but never used. Consider removing it if it's not needed.

- out = "phare_outputs"
🧰 Tools
🪛 Ruff (0.8.2)

664-664: Local variable out is assigned to but never used

Remove assignment to unused variable out

(F841)


394-398: Closure variable binding issue in nested function

The nested function domain() references variables from the outer scope (dim, nbrGhosts, patch) which could lead to unexpected behavior if these variables change. Consider passing these as arguments to the function instead.

- def domain(patch_data):
-     if dim == 1:
-         return patch_data.dataset[select]
-     return patch_data.dataset[:].reshape(
-         patch.box.shape + (nbrGhosts * 2) + 1
-     )[select]
+ def domain(patch_data, dim_value, nb_ghosts, patch_box):
+     if dim_value == 1:
+         return patch_data.dataset[select]
+     return patch_data.dataset[:].reshape(
+         patch_box.shape + (nb_ghosts * 2) + 1
+     )[select]

Then call it with:

domain(pdatas["rho"], dim, nbrGhosts, patch.box)
🧰 Tools
🪛 Ruff (0.8.2)

394-394: Function definition does not bind loop variable select

(B023)


396-396: Function definition does not bind loop variable patch

(B023)


396-396: Function definition does not bind loop variable nbrGhosts

(B023)


397-397: Function definition does not bind loop variable select

(B023)

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5a4071c and 55ce59f.

📒 Files selected for processing (56)
  • .github/workflows/cmake_macos.yml (1 hunks)
  • pyphare/pyphare/core/box.py (2 hunks)
  • pyphare/pyphare/core/gridlayout.py (1 hunks)
  • pyphare/pyphare/pharesee/geometry.py (7 hunks)
  • pyphare/pyphare/pharesee/hierarchy/hierarchy.py (1 hunks)
  • pyphare/pyphare/pharesee/run/utils.py (1 hunks)
  • res/amr/splitting.yml (1 hunks)
  • res/cmake/funcs.cmake (1 hunks)
  • res/cmake/options.cmake (2 hunks)
  • src/amr/data/field/coarsening/coarsen_weighter.hpp (1 hunks)
  • src/amr/data/field/coarsening/magnetic_field_coarsener.hpp (1 hunks)
  • src/amr/data/particles/refine/particles_data_split.hpp (2 hunks)
  • src/amr/data/particles/refine/split.hpp (1 hunks)
  • src/amr/data/particles/refine/split_1d.hpp (10 hunks)
  • src/amr/data/particles/refine/split_2d.hpp (17 hunks)
  • src/amr/data/particles/refine/split_3d.hpp (1 hunks)
  • src/amr/data/particles/refine/splitter.hpp (5 hunks)
  • src/core/data/grid/grid.hpp (1 hunks)
  • src/core/data/grid/gridlayoutdefs.hpp (2 hunks)
  • src/core/data/ndarray/ndarray_vector.hpp (8 hunks)
  • src/core/data/particles/particle.hpp (3 hunks)
  • src/core/utilities/box/box.hpp (1 hunks)
  • src/core/utilities/meta/meta_utilities.hpp (2 hunks)
  • tests/amr/data/field/coarsening/coarsening_utils.py (1 hunks)
  • tests/amr/data/field/coarsening/test_weighter_coarsen.hpp (1 hunks)
  • tests/amr/data/field/refine/CMakeLists.txt (1 hunks)
  • tests/amr/data/field/refine/test_default_field_refinement.cpp (4 hunks)
  • tests/amr/data/field/refine/test_field_refinement_on_hierarchy.cpp (1 hunks)
  • tests/amr/data/particles/refine/input/input_3d_ratio_2.txt (1 hunks)
  • tests/amr/data/particles/refine/test_split.cpp (1 hunks)
  • tests/core/data/ndarray/test_main.cpp (1 hunks)
  • tests/core/numerics/interpolator/test_main.cpp (1 hunks)
  • tests/diagnostic/CMakeLists.txt (1 hunks)
  • tests/diagnostic/job_3d.py.in (1 hunks)
  • tests/diagnostic/test-diagnostics_3d.cpp (1 hunks)
  • tests/functional/harris/harris_3d.py (1 hunks)
  • tests/simulator/__init__.py (3 hunks)
  • tests/simulator/advance/CMakeLists.txt (1 hunks)
  • tests/simulator/advance/test_fields_advance_1d.py (1 hunks)
  • tests/simulator/advance/test_fields_advance_2d.py (1 hunks)
  • tests/simulator/advance/test_fields_advance_3d.py (1 hunks)
  • tests/simulator/advance/test_particles_advance_3d.py (1 hunks)
  • tests/simulator/initialize/CMakeLists.txt (1 hunks)
  • tests/simulator/initialize/test_fields_init_2d.py (1 hunks)
  • tests/simulator/initialize/test_fields_init_3d.py (1 hunks)
  • tests/simulator/initialize/test_particles_init_1d.py (0 hunks)
  • tests/simulator/initialize/test_particles_init_2d.py (1 hunks)
  • tests/simulator/initialize/test_particles_init_3d.py (1 hunks)
  • tests/simulator/per_test.hpp (1 hunks)
  • tests/simulator/refined_particle_nbr.py (2 hunks)
  • tests/simulator/test_advance.py (16 hunks)
  • tests/simulator/test_initialization.py (13 hunks)
  • tests/simulator/utilities/field_coarsening.py (0 hunks)
  • tests/simulator/utilities/test_refine_field.py (2 hunks)
  • tools/python3/cmake.py (1 hunks)
  • tools/python3/git.py (1 hunks)
💤 Files with no reviewable changes (2)
  • tests/simulator/utilities/field_coarsening.py
  • tests/simulator/initialize/test_particles_init_1d.py
🚧 Files skipped from review as they are similar to previous changes (33)
  • src/amr/data/field/coarsening/coarsen_weighter.hpp
  • tests/simulator/advance/CMakeLists.txt
  • .github/workflows/cmake_macos.yml
  • src/core/utilities/box/box.hpp
  • tests/diagnostic/job_3d.py.in
  • src/amr/data/particles/refine/particles_data_split.hpp
  • src/amr/data/particles/refine/split.hpp
  • tests/simulator/initialize/test_fields_init_2d.py
  • src/core/data/grid/grid.hpp
  • tests/amr/data/field/refine/CMakeLists.txt
  • res/cmake/options.cmake
  • tests/simulator/initialize/CMakeLists.txt
  • pyphare/pyphare/pharesee/run/utils.py
  • tests/simulator/initialize/test_particles_init_2d.py
  • tests/core/data/ndarray/test_main.cpp
  • src/core/data/grid/gridlayoutdefs.hpp
  • pyphare/pyphare/core/gridlayout.py
  • tests/diagnostic/CMakeLists.txt
  • tests/simulator/initialize/test_particles_init_3d.py
  • tests/amr/data/field/refine/test_default_field_refinement.cpp
  • tests/simulator/per_test.hpp
  • tests/simulator/advance/test_fields_advance_1d.py
  • tests/simulator/advance/test_fields_advance_2d.py
  • pyphare/pyphare/pharesee/hierarchy/hierarchy.py
  • tests/amr/data/particles/refine/test_split.cpp
  • tests/simulator/utilities/test_refine_field.py
  • tests/amr/data/field/coarsening/test_weighter_coarsen.hpp
  • tests/amr/data/field/refine/test_field_refinement_on_hierarchy.cpp
  • src/core/data/particles/particle.hpp
  • src/amr/data/particles/refine/splitter.hpp
  • tests/simulator/initialize/test_fields_init_3d.py
  • src/amr/data/particles/refine/split_1d.hpp
  • src/amr/data/particles/refine/split_2d.hpp
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.hpp`: Review the C++ code, point out issues relative t...

**/*.hpp: Review the C++ code, point out issues relative to principles of clean code, expressiveness, and performance.

  • src/core/utilities/meta/meta_utilities.hpp
  • src/core/data/ndarray/ndarray_vector.hpp
  • src/amr/data/field/coarsening/magnetic_field_coarsener.hpp
  • src/amr/data/particles/refine/split_3d.hpp
🪛 Ruff (0.8.2)
tests/simulator/__init__.py

326-326: Do not use mutable data structures for argument defaults

Replace with None; initialize within function

(B006)

tools/python3/git.py

22-22: Local variable e is assigned to but never used

Remove assignment to unused variable e

(F841)


29-29: Undefined name delete_branch

(F821)

pyphare/pyphare/core/box.py

207-207: Ambiguous variable name: l

(E741)

tools/python3/cmake.py

48-48: Undefined name cmd

(F821)

tests/functional/harris/harris_3d.py

62-62: Ambiguous variable name: l

(E741)

pyphare/pyphare/pharesee/geometry.py

161-161: Ambiguous variable name: l

(E741)


164-164: Ambiguous variable name: l

(E741)


167-167: Ambiguous variable name: l

(E741)


188-188: Ambiguous variable name: l

(E741)

tests/amr/data/field/coarsening/coarsening_utils.py

181-182: Use a single if statement instead of nested if statements

(SIM102)

tests/simulator/test_advance.py

290-290: Loop control variable ilvl not used within loop body

Rename unused ilvl to _ilvl

(B007)

tests/simulator/test_initialization.py

394-394: Function definition does not bind loop variable select

(B023)


396-396: Function definition does not bind loop variable patch

(B023)


396-396: Function definition does not bind loop variable nbrGhosts

(B023)


397-397: Function definition does not bind loop variable select

(B023)


582-582: Loop control variable pi not used within loop body

Rename unused pi to _pi

(B007)


586-586: Yoda condition detected

Rewrite as ppc == H

(SIM300)


664-664: Local variable out is assigned to but never used

Remove assignment to unused variable out

(F841)

🪛 Cppcheck (2.10-2)
tests/diagnostic/test-diagnostics_3d.cpp

[performance] 24-24: Function parameter 'path' should be passed by const reference.

(passedByValue)

⏰ Context from checks skipped due to timeout of 90000ms (5)
  • GitHub Check: build (ubuntu-latest)
  • GitHub Check: Analyze (python)
  • GitHub Check: Analyze (cpp)
  • GitHub Check: build (macos-13)
  • GitHub Check: build (macos-14)
🔇 Additional comments (52)
src/core/utilities/meta/meta_utilities.hpp (3)

9-11: Good approach for configurable simulator dimensionality.

The preprocessor directive with a default value for PHARE_SIMULATORS provides flexibility, allowing users to override the maximum dimensions supported at compile time. This approach helps control build size and compilation time while enabling 3D simulations by default.


81-88: Clean transition to conditional dimension support.

The conditional compilation approach for 2D simulators is well-implemented, maintaining backward compatibility while enabling the transition to 3D. The code structure keeps the tuple format consistent.


98-98: Ensure consistent tuple formatting.

The closing bracket with semicolon >{}; is correctly placed, maintaining the formatting consistency of the function. The conditional compilation blocks are properly structured to ensure valid C++ syntax regardless of which preprocessor branches are taken.

src/core/data/ndarray/ndarray_vector.hpp (5)

6-6: Good addition of the <algorithm> include.

The addition of the algorithm header is appropriate as the code uses std::fill, std::copy, and other algorithm functions.


348-352: Good use of auto for loop variables.

Changing from explicit std::size_t to auto is a good practice in modern C++ for loop variables, making the code more flexible and maintainable.


361-381: Consider boundary checks to prevent off-by-one issues in 2D borders.

While the code appears correct for inclusive boundaries, if min_ or max_ become larger than expected, you risk overlaps or out-of-range indexing. Consider adding boundary checks or assertions to ensure the indices remain valid.


469-470: Good update to include x dimension assertions.

The assertion is correctly updated to ensure proper nesting of the masked views in the x dimension.


477-480: Enhanced dimension checking for multi-dimensional arrays.

The check properly ensures that dimensions greater than 1 have the appropriate y-dimension assertions.

src/amr/data/particles/refine/split_3d.hpp (1)

90-116: Verify the use of the templated constructor in WhitePattern.

The constructor is templated on Delta, but it is not immediately visible how or where this is instantiated. If unused, consider removing or confirming its necessity.

tests/simulator/__init__.py (1)

326-345: Address mutable default argument in debug_tracer.

This was previously flagged. Python recommends against using mutable objects ([0]) as default parameter values, as they can lead to unexpected behavior when reusing the function.

Apply this diff to fix:

-def tracefunc(frame, event, arg, indent=[0]):
+def tracefunc(frame, event, arg, indent=None):
+    if indent is None:
+        indent = [0]
🧰 Tools
🪛 Ruff (0.8.2)

326-326: Do not use mutable data structures for argument defaults

Replace with None; initialize within function

(B006)

tests/diagnostic/test-diagnostics_3d.cpp (1)

1-36: Looks good!

The diagnostic tests for 3D scenarios appear well-structured and consistent with the workflow for the fluid, particles, electromag, and Python-driven tests. The main function is straightforward and correctly invokes the PHARE lifecycle before running Google Tests.

🧰 Tools
🪛 Cppcheck (2.10-2)

[performance] 24-24: Function parameter 'path' should be passed by const reference.

(passedByValue)


[performance] 36-36: Function parameter 'path' should be passed by const reference.

(passedByValue)

tests/simulator/advance/test_particles_advance_3d.py (8)

1-4: Clear docstring explaining the purpose of this module.

Good documentation that clearly explains why this file exists separately from test_advance.py - to isolate dimension-specific test cases.


6-14: Import organization is appropriate.

The imports are well-organized, with standard library imports first, followed by third-party libraries, and then project-specific imports.


16-19: Good use of constants for dimensionality and test parameters.

Defining constants like ndim, interp_orders, and ppc at the module level makes the code more maintainable and easier to modify for different testing scenarios.


21-22: Helper function enhances test readability.

The per_interp function nicely abstracts the creation of test cases for different interpolation orders, making the test data definition cleaner.


33-43: Good use of parameterized testing with refinement boxes.

The test effectively uses data-driven testing with multiple box configurations to ensure the 3D functionality works across different scenarios.


45-47: Test method properly leverages base class functionality.

Good reuse of the base test logic while providing 3D-specific parameters.


50-51: Standard unittest entrypoint in place.

Properly implemented main block for standalone test execution.


1-51:

❌ Incorrect review comment

Verify coverage of 3D test scenarios.

Based on the AI summary, this file should include tests for field coarsening and ghost level handling, but these methods appear to be missing:

  • test_field_coarsening_via_subcycles
  • test_field_level_ghosts_via_subcycles_and_coarser_interpolation

Either the summary is incorrect or the implementation is incomplete.


🏁 Script executed:

#!/bin/bash
# Check if these test methods exist in the PR branch
grep -r "test_field_coarsening_via_subcycles\|test_field_level_ghosts_via_subcycles_and_coarser_interpolation" tests/

Length of output: 2707


Clarification on 3D Particle Test Coverage
The tests for field coarsening and ghost level handling are not missing—instead, they are implemented in a separate file (e.g., tests/simulator/advance/test_fields_advance_3d.py). The intent behind having this file (tests/simulator/advance/test_particles_advance_3d.py) is to isolate 3D particle test cases from field-specific tests, which aligns with the established test organization.

Likely an incorrect or invalid review comment.

res/amr/splitting.yml (2)

103-141: Well-structured 3D configuration maintained consistency with existing patterns.

The dimension_3 section follows the same logical pattern as the existing dimension_1 and dimension_2 sections, with appropriate parameters for 3D operations:

  1. Each interpolation order (1-3) is properly defined
  2. Particle types (N_particles_6, N_particles_12, N_particles_27) mirror the design pattern of lower dimensions
  3. Delta and weight values are carefully specified with appropriate precision

This consistency makes maintenance easier and ensures a cohesive configuration system across dimensions.


103-115:

❓ Verification inconclusive

Verify 3D delta and weight values with physics equations.

The delta and weight values for 3D operations appear to be carefully calculated, but it would be beneficial to verify they are derived from the correct physical equations for 3D particle interactions.


🌐 Web query:

What are the typical delta and weight values for 3D particle interpolation in particle-in-cell (PIC) simulations?

💡 Result:

In 3D particle-in-cell (PIC) simulations, delta functions (shape functions) and particle weights are critical for balancing numerical accuracy and computational efficiency. Here's a breakdown of typical implementations:


1. Delta Functions (Shape Functions)

These determine how particle charge/current densities are interpolated to grid points and vice versa. Common choices include:

  • Nearest Grid Point (NGP):

    • Zero-order shape function (Dirac delta approximation).
    • Rarely used due to high noise but computationally simple.
  • Cloud-in-Cell (CIC):

    • First-order linear interpolation (triangular-shaped cloud in 1D, trilinear in 3D).
    • Most common choice for its balance of accuracy and simplicity [6][8].
    • Mathematically:
      [
      S(x - X) = \prod_{d=1}^3 \begin{cases}
      1 - \frac{|x_d - X_d|}{\Delta x_d}, & |x_d - X_d| \leq \Delta x_d \
      0, & \text{otherwise}
      \end{cases}
      ]
  • Triangular-Shaped Cloud (TSC):

    • Second-order quadratic interpolation for smoother fields [3][8].
    • Reduces numerical noise compared to CIC but increases computational cost.
  • Higher-Order B-Splines:

    • Used in advanced codes (e.g., WarpX) to suppress numerical instabilities [2][5].
    • Example: Third-order B-splines for improved field solver accuracy [3][5].

2. Particle Weights

Weights represent the contribution of each macro-particle to physical quantities like charge density. Key considerations:

  • Standard PIC (Full-(f)):

    • Weight (w_j = q_j / q_0), where (q_j) is the charge of the macro-particle and (q_0) is the charge of a physical particle.
    • Typical values depend on the plasma density and computational constraints (e.g., (10^6)–(10^{12}) physical particles per macro-particle) [10][8].
  • (\delta f)-PIC Method:

    • Weights track perturbations from a background distribution (f_0):
      [
      w_j = \frac{\delta f}{g(Z_j)} \propto \frac{f - f_0}{f_0}
      ]
      where (g) is the marker distribution (often Maxwellian) [1][7].
    • Normalized to ensure (\sum w_j \approx 0) for small perturbations [1][7].
  • Adaptive Weighting:

    • Weights adjust dynamically in phase-space remapping schemes to minimize noise [3][7].

3. Trade-offs and Best Practices

  • Noise vs. Resolution: Lower-order methods (CIC) introduce more noise but are faster; higher-order methods (TSC, B-splines) improve accuracy at higher cost [2][6].
  • Energy Conservation: Codes like WarpX use energy-conserving schemes (e.g., Esirkepov current deposition) to mitigate numerical heating [5][9].
  • Velocity-Space Smoothing: For (\delta f) methods, weights are often initialized with a Maxwellian (g) to reduce statistical variance [1][7].

Example Workflow in 3D

  1. Charge Deposition:
    Particles deposit charge on the grid using CIC/TSC delta functions:
    [
    \rho(\mathbf{x}_g) = \sum_j q_j S(\mathbf{x}_g - \mathbf{x}_j)
    ]
  2. Field Solve:
    Compute (\mathbf{E}) and (\mathbf{B}) on the grid via Maxwell’s equations [4][5].
  3. Force Interpolation:
    Fields are interpolated back to particles using the same shape function:
    [
    \mathbf{F}_j = q_j \sum_g \mathbf{E}(\mathbf{x}_g) S(\mathbf{x}_g - \mathbf{x}_j)
    ]
  4. Weight Evolution:
    In (\delta f)-PIC, weights are updated via:
    [
    \dot{w}j = -\frac{d}{dt} \ln f_0 \big|{\mathbf{Z}_j} \cdot \mathbf{\dot{Z}}_j
    ] [1][7].

Key References

  • CIC/TSC: [8][3][6]
  • (\delta f)-PIC weights: [1][7]
  • High-order shape functions: [2][5][3]
  • Energy conservation: [5][9]

This framework ensures self-consistent, noise-controlled simulations while maintaining scalability for large 3D domains.

Citations:


Action: Verify 3D Interpolation Constants and Weight Format

  • The delta and weight values for N_particles_6 and N_particles_12 (i.e. 0.966431/0.166666 and 0.74823/0.083333) look reminiscent of fractional distributions (e.g. 1/6 and 1/12) that one might expect from a linear interpolation (CIC); however, please ensure that these numbers are indeed derived from the correct 3D PIC physics equations.
  • For N_particles_27, the snippet shows a delta of 1 accompanied by two weight values (.125 and .0625). Please verify whether these are intended to be combined (e.g. as an array or summed components) or if one of them is erroneous. Confirm that their derivation is consistent with the targeted interpolation method (e.g. if using a higher-order scheme like TSC or B-splines).

Ensure that the underlying equations used in your model are reflected accurately in these parameters.

pyphare/pyphare/core/box.py (3)

65-68: Good enhancement to Box subtraction operator to handle multiple boxes.

The update to the __sub__ method extends functionality to handle lists or tuples of boxes, which improves the flexibility of the Box class. The type checking and assertion that all items are Box instances is a good safety measure.


185-198: New remove_all function handles multiple box removals efficiently.

The implementation correctly manages the removal of multiple boxes from a target box. The algorithm:

  1. Processes boxes sequentially
  2. Keeps track of which boxes need to be removed
  3. Handles overlapping cases appropriately

This implementation is robust and addresses a common use case in AMR operations.


209-224: DataSelector class provides a clean interface for box-based data access.

The DataSelector class is well designed with clear documentation and implements both __getitem__ and __setitem__ for intuitive data access and modification. The implementation is concise while maintaining clarity.

tests/core/numerics/interpolator/test_main.cpp (2)

774-774: Remove unused static variable warning.

The static variable gtest_DepositCorrectlyTheirWeight_3d_defined_ is generated by the test registration macro but is not read. This is a common pattern in GTest and doesn't represent an actual issue.


786-786: Remove unused static variable warning.

The static variable gtest_testInterpolator_ACollectionOfParticles_3d is generated by the test registration macro but is not read. This is a common pattern in GTest and doesn't represent an actual issue.

tests/amr/data/particles/refine/input/input_3d_ratio_2.txt (1)

46-51:

✅ Verification successful

Consider adding required configuration for TileClustering and GriddingAlgorithm sections.

The TileClustering and GriddingAlgorithm sections are currently empty. If these sections require configuration parameters for a valid simulation, they should be added.


🏁 Script executed:

#!/bin/bash
# Search for other 3D configuration files to check if empty sections are typical

# Find other 3D input files and check their TileClustering and GriddingAlgorithm sections
echo "Checking other 3D configuration files for reference..."
fd "input.*3d.*\.txt" tests/ -x grep -A 10 "TileClustering" {}

Length of output: 265


Confirmation: Empty Config Sections Are Intentional

Based on the verification, the empty configuration blocks for both the TileClustering and GriddingAlgorithm sections are consistent with other 3D configuration files. It appears these sections are intentionally left blank as part of the established pattern. No additional configuration is required at this time.

tests/simulator/refined_particle_nbr.py (4)

33-36: Good addition of a tearDown method.

Adding a tearDown method ensures that the simulator is properly reset after each test, preventing state from one test affecting others. This is especially important if exceptions are raised during tests.


106-116: LGTM! Good 3D test case implementation.

The 3D test case is well-defined with appropriate constants for minimum and maximum differences. The code correctly extends the testing framework to handle 3D scenarios.


118-136: Well-structured testing helper method.

The _check_deltas_and_weights method is well organized, breaking down the validation process into clear steps. This method ensures the correct calculation of delta and weight values for particles across different dimensions.


140-145: Good test execution ordering.

The explicit ordering of tests ensures that delta/weights are verified first, followed by dimension-specific tests. This ordered test execution provides a more systematic validation approach.

tests/simulator/advance/test_fields_advance_3d.py (3)

1-15: LGTM! Well-structured 3D test file header.

The file is well-organized with clear imports and setup for 3D testing. The docstring explains the purpose of separating dimension-specific test cases, which is good practice for maintainability.


21-22: LGTM! Effective helper function.

The per_interp function elegantly generates test cases for different interpolation orders, promoting code reuse throughout the test file.


102-117: Good test implementation for level ghost fields.

The implementation of the test_field_level_ghosts_via_subcycles_and_coarser_interpolation method is well-done. I notice there's a commented-out @unittest.skip line which suggests this test was previously skipped but is now enabled, which is good for expanding test coverage to 3D.

pyphare/pyphare/pharesee/geometry.py (4)

63-74: LGTM! Good implementation of 3D ghost boxes.

The function now correctly handles 3D ghost boxes by defining them for all six faces of the 3D volume (left, right, bottom, top, front, back). This is a necessary extension for 3D functionality.


392-400: LGTM! Well-defined 3D sides dictionary.

The addition of 3D sides (front and back) in the dictionary is well-implemented, correctly handling the domain boundaries in all three dimensions.


407-415: LGTM! Effective periodic patch handling.

The implementation of periodic boundary conditions for patches is well-done, creating copies of patches with appropriate shifts based on their positioning relative to domain boundaries.


519-519: LGTM! Efficient box subtraction operation.

The operation to subtract patch boxes from ghost area boxes is efficient and correctly handles the computation of level ghost boxes.

src/amr/data/field/coarsening/magnetic_field_coarsener.hpp (2)

65-79: Validate indexing logic in fine_start_index.

The function calculates the fine-level starting index by multiplying the coarse index by the ratio. Ensure that boundary conditions are checked upstream to avoid unexpected out-of-bounds access when coarseIndex lies near domain extremes, especially for higher dimensions or custom ratio values.


126-224: Consolidate repeated averaging formulas for 2D components.

The logic for Bx, By, and the dual-dual combination repeats in structure. Consider extracting the averaging step into a helper utility, passing the relevant fine indices. This can simplify future maintenance or expansions (e.g., if new weighting factors are introduced).

tests/simulator/test_advance.py (3)

415-417: Ensure the new cells=120 parameter is tested for all relevant scenarios.

With _test_L0_particle_number_conservation now supporting a cells=120 default, confirm other test paths also pass. Large cell counts may expose boundary corner cases or performance constraints.


444-465: Parameterizing cells=60 in _test_field_coarsening_via_subcycles.

This refactoring adds flexibility for different domain sizes. Double-check that the newly introduced parameter interacts properly with any user overrides and with largest_patch_size=30 to avoid mismatch or off-by-one indexing errors.


802-809: Validate the threshold in base_test_field_level_ghosts_via_subcycles_and_coarser_interpolation.

The assertion self.assertGreater(successful_test_nbr, ...) indicates a minimal threshold for success. Ensure it covers all refined boxes or corner cases where a refinement might carry zero overlap.

tests/simulator/test_initialization.py (9)

254-254: Added flexible particle count configuration - good improvement!

Adding the ppc parameter with a default value of 100 improves configurability of the test, allowing for different particle densities to be tested.

Also applies to: 266-267


260-261: Excellent addition of timing diagnostics

Adding timing measurements is valuable for tracking test performance, especially when dealing with computationally intensive 3D simulation tests.

Also applies to: 270-274


329-351: 3D support correctly implemented for magnetic field testing

The implementation properly handles z-dimension coordinates and correctly applies the 3D meshgrid for testing magnetic field components. Good use of dimension-specific code blocks.


384-414: Well-structured domain selection and mesh handling for vector field testing

The refactored code provides cleaner dimension-agnostic handling of domain selection and mesh management. The nested function approach improves code organization.

🧰 Tools
🪛 Ruff (0.8.2)

394-394: Function definition does not bind loop variable select

(B023)


396-396: Function definition does not bind loop variable patch

(B023)


396-396: Function definition does not bind loop variable nbrGhosts

(B023)


397-397: Function definition does not bind loop variable select

(B023)


427-428: Adjustment of particle numbers for 3D testing

Good adaptation of particle counts for 3D testing (20 particles per cell), which balances computational cost with testing accuracy. The empirical deviation tolerance of 2e-1 for 3D seems reasonable.


692-693: Consistent dimensionality checking

Good adaptation for extracting dimensionality from the patch box's ndim attribute and verifying that simulation dimensions match. This improves robustness when dealing with multi-dimensional simulations.

Also applies to: 697-698


528-536: Conditional plotting code properly updated for 3D support

The plotting code is correctly updated to handle 3D data by checking the dimension and adjusting plot parameters accordingly. The filename now includes the dimensionality parameter, which is useful for result tracking.


458-467: Clean dimension-agnostic mesh grid and data selection

The code now properly handles data selection and mesh grid generation for arbitrary dimensions, making it more maintainable and ready for 3D testing.


621-623: Early return for non-primary MPI rank

Good addition of MPI rank check to avoid redundant test execution in parallel environments. This prevents unnecessary test duplication and potential issues with concurrent access.

}
Main
{
dim = 1
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix dimensionality in Main section for 3D simulation.

The dim parameter is set to 1 despite this being a 3D configuration file. This appears to be inconsistent with the three-dimensional domain defined in the CartesianGridGeometry section.

-    dim = 1
+    dim = 3
📝 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
dim = 1
dim = 3

Comment on lines +54 to +73
def test_overlaped_fields_are_equal_with_min_max_patch_size_of_max_ghosts(
self, interp_order, refinement_boxes
):
print(f"{self._testMethodName}_{ndim}d")
time_step_nbr = 3
time_step = 0.001
from pyphare.pharein.simulation import check_patch_size

largest_patch_size, smallest_patch_size = check_patch_size(
ndim, interp_order=interp_order, cells=[cells] * ndim
)
datahier = self.getHierarchy(
ndim,
interp_order,
refinement_boxes,
"eb",
cells=cells,
smallest_patch_size=smallest_patch_size,
largest_patch_size=smallest_patch_size,
time_step=time_step,
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Fix parameter inconsistency in patch size test.

There's an inconsistency in the patch size parameters. The function retrieves both largest_patch_size and smallest_patch_size, but then uses smallest_patch_size for both parameters in the getHierarchy call.

        datahier = self.getHierarchy(
            ndim,
            interp_order,
            refinement_boxes,
            "eb",
            cells=cells,
            smallest_patch_size=smallest_patch_size,
-            largest_patch_size=smallest_patch_size,
+            largest_patch_size=largest_patch_size,
            time_step=time_step,
            time_step_nbr=time_step_nbr,
            nbr_part_per_cell=ppc,
        )
📝 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
def test_overlaped_fields_are_equal_with_min_max_patch_size_of_max_ghosts(
self, interp_order, refinement_boxes
):
print(f"{self._testMethodName}_{ndim}d")
time_step_nbr = 3
time_step = 0.001
from pyphare.pharein.simulation import check_patch_size
largest_patch_size, smallest_patch_size = check_patch_size(
ndim, interp_order=interp_order, cells=[cells] * ndim
)
datahier = self.getHierarchy(
ndim,
interp_order,
refinement_boxes,
"eb",
cells=cells,
smallest_patch_size=smallest_patch_size,
largest_patch_size=smallest_patch_size,
time_step=time_step,
def test_overlaped_fields_are_equal_with_min_max_patch_size_of_max_ghosts(
self, interp_order, refinement_boxes
):
print(f"{self._testMethodName}_{ndim}d")
time_step_nbr = 3
time_step = 0.001
from pyphare.pharein.simulation import check_patch_size
largest_patch_size, smallest_patch_size = check_patch_size(
ndim, interp_order=interp_order, cells=[cells] * ndim
)
datahier = self.getHierarchy(
ndim,
interp_order,
refinement_boxes,
"eb",
cells=cells,
smallest_patch_size=smallest_patch_size,
largest_patch_size=largest_patch_size,
time_step=time_step,
time_step_nbr=time_step_nbr,
nbr_part_per_cell=ppc,
)

Comment on lines +82 to +124
template<std::size_t D, typename FieldT>
typename std::enable_if<D == 1, void>::type
coarsen(Point_t const fineStartIndex, FieldT const& fineField, FieldT& coarseField,
Point_t const coarseIndex);

if constexpr (dimension == 1)
{
// in 1D div(B) is automatically satisfied so using this coarsening
// opertor is probably not better than the default one, but we do that
// for a kind of consistency...
// coarse flux is equal to fine flux and we're 1D so there is flux partitioned
// only for By and Bz, Bx is equal to the fine value
template<std::size_t D, typename FieldT>
typename std::enable_if<D == 2, void>::type
coarsen(Point_t const fineStartIndex, FieldT const& fineField, FieldT& coarseField,
Point_t const coarseIndex);

if (centering_[dirX] == core::QtyCentering::primal) // bx
{
coarseField(coarseIndex[dirX]) = fineField(fineStartIndex[dirX]);
}
else if (centering_[dirX] == core::QtyCentering::dual) // by and bz
{
coarseField(coarseIndex[dirX])
= 0.5 * (fineField(fineStartIndex[dirX] + 1) + fineField(fineStartIndex[dirX]));
}
}
template<std::size_t D, typename FieldT>
typename std::enable_if<D == 3, void>::type
coarsen(Point_t const fineStartIndex, FieldT const& fineField, FieldT& coarseField,
Point_t const coarseIndex);

if constexpr (dimension == 2)
{
if (centering_[dirX] == core::QtyCentering::primal
and centering_[dirY] == core::QtyCentering::dual)
{
coarseField(coarseIndex[dirX], coarseIndex[dirY])
= 0.5
* (fineField(fineStartIndex[dirX], fineStartIndex[dirY])
+ fineField(fineStartIndex[dirX], fineStartIndex[dirY] + 1));
}
else if (centering_[dirX] == core::QtyCentering::dual
and centering_[dirY] == core::QtyCentering::primal)
{
coarseField(coarseIndex[dirX], coarseIndex[dirY])
= 0.5
* (fineField(fineStartIndex[dirX], fineStartIndex[dirY])
+ fineField(fineStartIndex[dirX] + 1, fineStartIndex[dirY]));
}
else if (centering_[dirX] == core::QtyCentering::dual
and centering_[dirY] == core::QtyCentering::dual)
{
coarseField(coarseIndex[dirX], coarseIndex[dirY])
= 0.25
* (fineField(fineStartIndex[dirX], fineStartIndex[dirY])
+ fineField(fineStartIndex[dirX] + 1, fineStartIndex[dirY])
+ fineField(fineStartIndex[dirX], fineStartIndex[dirY] + 1)
+ fineField(fineStartIndex[dirX] + 1, fineStartIndex[dirY] + 1));
}
else
{
throw std::runtime_error("no magnetic field should end up here");
}
}
else if constexpr (dimension == 3)
{
throw std::runtime_error("Not Implemented yet");
}
}

private:
std::array<core::QtyCentering, dimension> const centering_;
std::array<core::QtyCentering, dim> const centering_;
SAMRAI::hier::Box const sourceBox_;
SAMRAI::hier::Box const destinationBox_;
static int constexpr ratio_ = 2;
};

template<std::size_t dim>
template<std::size_t D, typename FieldT>
typename std::enable_if<D == 1, void>::type
MagneticFieldCoarsener<dim>::coarsen(Point_t const fineStartIndex, FieldT const& fineField,
FieldT& coarseField, Point_t const coarseIndex)
{
// in 1D div(B) is automatically satisfied so using this coarsening
// opertor is probably not better than the default one, but we do that
// for some constistency
// coarse flux is equal to fine flux and we're 1D so tehre is flux partitionned
// only for By and Bz, Bx is equal to the fine value
if (centering_[dirX] == core::QtyCentering::primal) // bx
{
coarseField(coarseIndex[dirX]) = fineField(fineStartIndex[dirX]);
}
else if (centering_[dirX] == core::QtyCentering::dual) // by and bz
{
coarseField(coarseIndex[dirX])
= 0.5 * (fineField(fineStartIndex[dirX] + 1) + fineField(fineStartIndex[dirX]));
}
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Unclear fallback behavior for 1D coarsening logic.

While 1D automatically satisfies div(B), the code lumps By/Bz under the dual centering case without additional checks. Ensure that the function cannot receive an unexpected combination of centerings. If there's a possibility, consider throwing an error or handling that case explicitly.

Copy link

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

♻️ Duplicate comments (5)
tools/python3/git.py (1)

27-35: ⚠️ Potential issue

Add missing delete_branch function.

The function references an undefined delete_branch function.

Please implement the missing delete_branch function before using it in the checkout function. Example implementation:

def delete_branch(branch):
    """Delete a Git branch if it exists."""
    if branch_exists(branch):
        run(f"git branch -D {branch}")
🧰 Tools
🪛 Ruff (0.8.2)

29-29: Undefined name delete_branch

(F821)

tests/simulator/__init__.py (1)

319-346: ⚠️ Potential issue

Fix mutable default argument in debug_tracer function.

The function uses a mutable list as a default argument (indent=[0]), which is a Python anti-pattern that can lead to unexpected behavior when the function is reused.

Apply this diff to fix the issue:

-def tracefunc(frame, event, arg, indent=[0]):
+def tracefunc(frame, event, arg, indent=None):
+    if indent is None:
+        indent = [0]
🧰 Tools
🪛 Ruff (0.8.2)

326-326: Do not use mutable data structures for argument defaults

Replace with None; initialize within function

(B006)

tests/core/numerics/interpolator/test_main.cpp (3)

739-739: ⚠️ Potential issue

Fix the grid layout initialization.

The grid layout initialization is incorrect as it only specifies two dimensions ({nx, ny}) for a 3D grid.

Apply this diff to fix the grid layout initialization:

-    GridLayout_t layout{ConstArray<double, dim>(.1), {nx, ny}, ConstArray<double, dim>(0)};
+    GridLayout_t layout{ConstArray<double, dim>(.1), {nx, ny, nz}, ConstArray<double, dim>(0)};

768-768: ⚠️ Potential issue

Fix the interpolator call signature.

The interpolator call signature differs from the 1D and 2D implementations, which use makeIndexRange.

Apply this diff to maintain consistency with other dimensions:

-        interpolator(particles, rho, v, layout);
+        interpolator(makeIndexRange(particles), rho, v, layout);

774-781: ⚠️ Potential issue

Uncomment and fix the test assertions.

The test case is currently commented out, which is flagged by static analysis. The test should verify the correct deposition of weights and velocities.

Apply this diff to restore and fix the test assertions:

-    // auto const& [vx, vy, vz] = this->v();
-    // EXPECT_DOUBLE_EQ(this->rho(7, 7, 7), 1.0);
-    // EXPECT_DOUBLE_EQ(vx(7, 7, 7), 2.0);
-    // EXPECT_DOUBLE_EQ(vy(7, 7, 7), -1.0);
-    // EXPECT_DOUBLE_EQ(vz(7, 7, 7), 1.0);
+    auto idx = 2 + this->layout.nbrGhosts(QtyCentering::dual);
+    auto const& [vx, vy, vz] = this->v();
+    EXPECT_DOUBLE_EQ(this->rho(idx, idx, idx), 1.0);
+    EXPECT_DOUBLE_EQ(vx(idx, idx, idx), 2.0);
+    EXPECT_DOUBLE_EQ(vy(idx, idx, idx), -1.0);
+    EXPECT_DOUBLE_EQ(vz(idx, idx, idx), 1.0);
🧹 Nitpick comments (21)
tools/python3/cmake.py (3)

4-5: Complete the implementation of the version() function.

The version() function is currently a placeholder with no implementation. Either implement the function to return the CMake version or add a TODO comment explaining why it's empty.

 def version():
-    pass
+    """Returns the CMake version."""
+    proc = run("cmake --version", capture_output=True)
+    out = decode_bytes(proc.stdout)
+    # First line contains version like "cmake version 3.x.x"
+    version_str = out.splitlines()[0].split(" ")[2]
+    return version_str

30-31: Add error handling for build command failures.

The build function doesn't handle potential failures in the build process. Consider adding error handling to provide more informative messages when builds fail.

 def build(use_ninja=False, threads=1):
-    run("ninja" if use_ninja else f"make -j{threads}", capture_output=False)
+    try:
+        proc = run("ninja" if use_ninja else f"make -j{threads}", capture_output=False)
+        if proc.returncode != 0:
+            print(f"Build failed with return code {proc.returncode}")
+            return False
+        return True
+    except Exception as e:
+        print(f"Build failed with exception: {e}")
+        return False

34-37: Add docstrings to remaining functions for better code documentation.

The list_tests(), test_cmd(), and run_test() functions lack docstrings explaining their purpose and usage, unlike make_config_str().

Example for list_tests():

 def list_tests():
+    """
+    Lists all available tests from CTest.
+    
+    Returns:
+        list: Names of all available tests.
+    """
     proc = run("ctest -N", capture_output=True)
     out = decode_bytes(proc.stdout)
     return [line.split(" ")[-1] for line in out.splitlines()[1:-2]]
tests/simulator/refined_particle_nbr.py (1)

44-48: Remove the TODO comment as it's already implemented

The code already handles the 3D case correctly right after the TODO comment, so the TODO comment should be removed.

        dim2 = refined_particle_nbr * ((cellNbr[0] * 2 + (cellNbr[1] * 2)))
        if dim == 2:
            return dim2
-        # TODO3D if dim==3?
        return dim2 * (cellNbr[2] * 2)
tools/python3/git.py (1)

19-24: Simplify exception handling.

The exception variable 'e' is captured but never used.

def branch_exists(branch):
    try:
        run(f"git show-branch {branch}", check=True)
-    except subprocess.CalledProcessError as e:
+    except subprocess.CalledProcessError:
        return False  # exit failure means branch does not exist
    return True
🧰 Tools
🪛 Ruff (0.8.2)

22-22: Local variable e is assigned to but never used

Remove assignment to unused variable e

(F841)

tests/simulator/test_initialization.py (3)

392-397: Improve scoping in the domain function.

The inner function domain uses variables from the outer scope, creating unclear dependencies.

Consider passing these variables as parameters to make the function more self-contained:

-def domain(patch_data):
+def domain(patch_data, dim, nbrGhosts, patch):
    if dim == 1:
        return patch_data.dataset[select]
    return patch_data.dataset[:].reshape(
        patch.box.shape + (nbrGhosts * 2) + 1
    )[select]
🧰 Tools
🪛 Ruff (0.8.2)

394-394: Function definition does not bind loop variable select

(B023)


396-396: Function definition does not bind loop variable patch

(B023)


396-396: Function definition does not bind loop variable nbrGhosts

(B023)


397-397: Function definition does not bind loop variable select

(B023)


582-587: Fix unused loop control variable.

The variable pi is not used within the loop body.

-for pi, patch in enumerate(datahier.level(0).patches):
+for _, patch in enumerate(datahier.level(0).patches):
🧰 Tools
🪛 Ruff (0.8.2)

582-582: Loop control variable pi not used within loop body

Rename unused pi to _pi

(B007)


586-586: Yoda condition detected

Rewrite as ppc == H

(SIM300)


664-664: Remove unused variable.

The variable out is assigned but never used.

-out = "phare_outputs"
🧰 Tools
🪛 Ruff (0.8.2)

664-664: Local variable out is assigned to but never used

Remove assignment to unused variable out

(F841)

pyphare/pyphare/core/box.py (1)

206-207: Use descriptive variable names to avoid ambiguity.

The variable name l is ambiguous and could be mistaken for the number '1' or capital 'I'. Consider using a more descriptive name like lower or lower_bound.

-def select(data, box):
-    return data[tuple([slice(l, u + 1) for l,u in zip(box.lower, box.upper)])]
+def select(data, box):
+    return data[tuple([slice(lower, upper + 1) for lower, upper in zip(box.lower, box.upper)])]
🧰 Tools
🪛 Ruff (0.8.2)

207-207: Ambiguous variable name: l

(E741)

tests/amr/data/field/coarsening/coarsening_utils.py (2)

131-292: Simplify 3D case with helper functions.

The 3D case contains extensive repetition in weight calculation logic. Consider creating helper functions to generate weights based on primality patterns and component types.

You could create a function that generates the appropriate shift and weight arrays based on primality, and another function to perform the nested summation. This would make the code more maintainable and less error-prone.

def get_shifts_and_weights(is_primal, direction):
    """Generate shifts and weights based on primality for a direction."""
    if is_primal:
        return [-1, 0, 1], [0.25, 0.5, 0.25]
    else:
        return [0, 1], [0.5, 0.5]

def apply_weights(fine_data, fine_indices, shifts_weights_by_dim):
    """Apply weights using nested loops for any number of dimensions."""
    # Implementation that handles the nested loops dynamically
    # ...
🧰 Tools
🪛 Ruff (0.8.2)

181-182: Use a single if statement instead of nested if statements

(SIM102)


181-182: Use a single if statement instead of nested if statements.

Replace elif not is_primal[0] and is_primal[1] and not is_primal[2]: with a cleaner condition to improve readability.

-                    elif not is_primal[0] and is_primal[1] and not is_primal[2]:
-                        if qty == "By":
+                    elif not is_primal[0] and is_primal[1] and not is_primal[2] and qty == "By":
🧰 Tools
🪛 Ruff (0.8.2)

181-182: Use a single if statement instead of nested if statements

(SIM102)

tests/functional/harris/harris_3d.py (1)

62-64: Rename the ambiguous variable l.
Use more descriptive naming to avoid confusion with digit “1.”

-def S(y, y0, l):
-    return 0.5 * (1.0 + np.tanh((y - y0) / l))
+def S(y, y0, scale_length):
+    return 0.5 * (1.0 + np.tanh((y - y0) / scale_length))
🧰 Tools
🪛 Ruff (0.8.2)

62-62: Ambiguous variable name: l

(E741)

pyphare/pyphare/pharesee/geometry.py (2)

63-74: Check consistency with max ghost cells for 3D ghost boxes.

For the 3D ghost boxes, ensure ghost_box_width correctly reflects the maximum needed cells across all dimensions. An off-by-one error could omit or over-include some boundary cells.


161-167: Rename ambiguous variable.

Static analysis flags the variable l as ambiguous. Please rename it to something clearer (e.g., shift_list) to improve readability.

-for k, l in shifts.items():
+for k, shift_list in shifts.items():
🧰 Tools
🪛 Ruff (0.8.2)

161-161: Ambiguous variable name: l

(E741)


164-164: Ambiguous variable name: l

(E741)


167-167: Ambiguous variable name: l

(E741)

res/cmake/funcs.cmake (1)

1-193: Overall good structure with flexible MPI and Python testing.

The file properly introduces test functions with MPI/no-MPI variants, environment variables, and an ASAN leak detection flag. A couple of considerations for further improvements:
• Revisit ASAN_OPTIONS=detect_leaks=0 if debugging memory leaks is desired in the future.
• Check whether setting PHARE_MPI_PROCS=2 by default is acceptable on all environments or if a dynamic approach is needed.

src/amr/data/field/coarsening/magnetic_field_coarsener.hpp (2)

33-36: Clarify assumptions about Yee layout.

The comment warns that this coarsener logic assumes certain Yee centering for magnetic fields. Consider explicitly validating or throwing an error if unexpected centerings appear, ensuring no silent miscoarsening occurs.


221-223: Add specific error context for unhandled centerings.

The runtime_error messages could be more descriptive (e.g., specifying which centering setup was encountered) to ease debugging if users encounter this exception.

-throw std::runtime_error("no magnetic field should end up here");
+throw std::runtime_error("Unhandled magnetic field centering combination in coarsening");

Also applies to: 331-332

tests/simulator/test_advance.py (3)

286-340: Spelling and partial success approach
The function name and docstring contain a spelling issue: "overlaped" should be "overlapped." Additionally, the function short-circuits upon the first failed assertion, returning a partial success count. Ensure this behavior is intentional, as it can hide subsequent issues.

- def base_test_overlaped_fields_are_equal(self, datahier, coarsest_time):
+ def base_test_overlapped_fields_are_equal(self, datahier, coarsest_time):
🧰 Tools
🪛 Ruff (0.8.2)

290-290: Loop control variable ilvl not used within loop body

Rename unused ilvl to _ilvl

(B007)


290-290: Rename unused loop variable
The loop variable ilvl is not used. Rename it to _ilvl to clearly indicate it's unused.

- for ilvl, overlaps in hierarchy_overlaps(datahier, coarsest_time).items():
+ for _ilvl, overlaps in hierarchy_overlaps(datahier, coarsest_time).items():
🧰 Tools
🪛 Ruff (0.8.2)

290-290: Loop control variable ilvl not used within loop body

Rename unused ilvl to _ilvl

(B007)


346-354: Spelling correction
The function name _test_overlaped_fields_are_equal also has "overlaped" instead of "overlapped."

- def _test_overlaped_fields_are_equal(self, datahier, time_step_nbr, time_step):
+ def _test_overlapped_fields_are_equal(self, datahier, time_step_nbr, time_step):
src/amr/data/particles/refine/split_3d.hpp (1)

17-17: Avoid using namespace in header
Including a namespace directive in a header file can inadvertently pollute the global scope. Consider removing this directive or restricting its scope.

- using namespace PHARE::core;
+ // Remove or limit this directive in header scope
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 55ce59f and c9c3437.

📒 Files selected for processing (58)
  • .github/workflows/cmake_macos.yml (1 hunks)
  • pyphare/pyphare/core/box.py (2 hunks)
  • pyphare/pyphare/core/gridlayout.py (1 hunks)
  • pyphare/pyphare/pharesee/geometry.py (7 hunks)
  • pyphare/pyphare/pharesee/hierarchy/hierarchy.py (1 hunks)
  • pyphare/pyphare/pharesee/run/utils.py (1 hunks)
  • res/amr/splitting.yml (1 hunks)
  • res/cmake/funcs.cmake (1 hunks)
  • res/cmake/options.cmake (2 hunks)
  • src/amr/data/field/coarsening/coarsen_weighter.hpp (1 hunks)
  • src/amr/data/field/coarsening/magnetic_field_coarsener.hpp (1 hunks)
  • src/amr/data/particles/refine/particles_data_split.hpp (2 hunks)
  • src/amr/data/particles/refine/split.hpp (1 hunks)
  • src/amr/data/particles/refine/split_1d.hpp (10 hunks)
  • src/amr/data/particles/refine/split_2d.hpp (17 hunks)
  • src/amr/data/particles/refine/split_3d.hpp (1 hunks)
  • src/amr/data/particles/refine/splitter.hpp (5 hunks)
  • src/core/data/grid/grid.hpp (1 hunks)
  • src/core/data/grid/gridlayoutdefs.hpp (2 hunks)
  • src/core/data/ndarray/ndarray_vector.hpp (8 hunks)
  • src/core/data/particles/particle.hpp (3 hunks)
  • src/core/utilities/box/box.hpp (1 hunks)
  • src/core/utilities/meta/meta_utilities.hpp (2 hunks)
  • tests/amr/data/field/coarsening/coarsening_utils.py (1 hunks)
  • tests/amr/data/field/coarsening/test_weighter_coarsen.hpp (1 hunks)
  • tests/amr/data/field/refine/CMakeLists.txt (1 hunks)
  • tests/amr/data/field/refine/test_default_field_refinement.cpp (4 hunks)
  • tests/amr/data/field/refine/test_field_refinement_on_hierarchy.cpp (1 hunks)
  • tests/amr/data/particles/refine/input/input_3d_ratio_2.txt (1 hunks)
  • tests/amr/data/particles/refine/test_split.cpp (1 hunks)
  • tests/core/data/ndarray/test_main.cpp (1 hunks)
  • tests/core/numerics/interpolator/test_main.cpp (1 hunks)
  • tests/diagnostic/CMakeLists.txt (1 hunks)
  • tests/diagnostic/job_3d.py.in (1 hunks)
  • tests/diagnostic/test-diagnostics_3d.cpp (1 hunks)
  • tests/functional/conservation/CMakeLists.txt (1 hunks)
  • tests/functional/conservation/conserv_3d.py (1 hunks)
  • tests/functional/harris/harris_3d.py (1 hunks)
  • tests/simulator/__init__.py (3 hunks)
  • tests/simulator/advance/CMakeLists.txt (1 hunks)
  • tests/simulator/advance/test_fields_advance_1d.py (1 hunks)
  • tests/simulator/advance/test_fields_advance_2d.py (1 hunks)
  • tests/simulator/advance/test_fields_advance_3d.py (1 hunks)
  • tests/simulator/advance/test_particles_advance_3d.py (1 hunks)
  • tests/simulator/initialize/CMakeLists.txt (1 hunks)
  • tests/simulator/initialize/test_fields_init_2d.py (1 hunks)
  • tests/simulator/initialize/test_fields_init_3d.py (1 hunks)
  • tests/simulator/initialize/test_particles_init_1d.py (0 hunks)
  • tests/simulator/initialize/test_particles_init_2d.py (1 hunks)
  • tests/simulator/initialize/test_particles_init_3d.py (1 hunks)
  • tests/simulator/per_test.hpp (1 hunks)
  • tests/simulator/refined_particle_nbr.py (2 hunks)
  • tests/simulator/test_advance.py (16 hunks)
  • tests/simulator/test_initialization.py (13 hunks)
  • tests/simulator/utilities/field_coarsening.py (0 hunks)
  • tests/simulator/utilities/test_refine_field.py (2 hunks)
  • tools/python3/cmake.py (1 hunks)
  • tools/python3/git.py (1 hunks)
💤 Files with no reviewable changes (2)
  • tests/simulator/utilities/field_coarsening.py
  • tests/simulator/initialize/test_particles_init_1d.py
🚧 Files skipped from review as they are similar to previous changes (34)
  • src/amr/data/particles/refine/split.hpp
  • src/core/utilities/box/box.hpp
  • src/core/data/grid/grid.hpp
  • tests/amr/data/field/refine/CMakeLists.txt
  • tests/simulator/initialize/test_fields_init_2d.py
  • src/amr/data/particles/refine/particles_data_split.hpp
  • .github/workflows/cmake_macos.yml
  • src/core/data/grid/gridlayoutdefs.hpp
  • tests/diagnostic/CMakeLists.txt
  • tests/core/data/ndarray/test_main.cpp
  • pyphare/pyphare/pharesee/run/utils.py
  • tests/simulator/advance/test_fields_advance_2d.py
  • tests/diagnostic/job_3d.py.in
  • src/amr/data/field/coarsening/coarsen_weighter.hpp
  • tests/simulator/advance/CMakeLists.txt
  • tests/simulator/initialize/test_particles_init_2d.py
  • tests/simulator/advance/test_particles_advance_3d.py
  • pyphare/pyphare/pharesee/hierarchy/hierarchy.py
  • tests/amr/data/particles/refine/test_split.cpp
  • res/cmake/options.cmake
  • tests/simulator/utilities/test_refine_field.py
  • tests/simulator/advance/test_fields_advance_1d.py
  • tests/amr/data/field/refine/test_default_field_refinement.cpp
  • tests/simulator/initialize/test_particles_init_3d.py
  • tests/simulator/initialize/test_fields_init_3d.py
  • res/amr/splitting.yml
  • tests/amr/data/field/coarsening/test_weighter_coarsen.hpp
  • src/core/data/particles/particle.hpp
  • tests/amr/data/field/refine/test_field_refinement_on_hierarchy.cpp
  • tests/simulator/advance/test_fields_advance_3d.py
  • tests/simulator/initialize/CMakeLists.txt
  • src/amr/data/particles/refine/split_1d.hpp
  • src/amr/data/particles/refine/split_2d.hpp
  • src/amr/data/particles/refine/splitter.hpp
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.hpp`: Review the C++ code, point out issues relative t...

**/*.hpp: Review the C++ code, point out issues relative to principles of clean code, expressiveness, and performance.

  • src/core/utilities/meta/meta_utilities.hpp
  • src/core/data/ndarray/ndarray_vector.hpp
  • tests/simulator/per_test.hpp
  • src/amr/data/field/coarsening/magnetic_field_coarsener.hpp
  • src/amr/data/particles/refine/split_3d.hpp
🪛 Ruff (0.8.2)
tools/python3/git.py

22-22: Local variable e is assigned to but never used

Remove assignment to unused variable e

(F841)


29-29: Undefined name delete_branch

(F821)

tests/functional/harris/harris_3d.py

62-62: Ambiguous variable name: l

(E741)

tests/amr/data/field/coarsening/coarsening_utils.py

181-182: Use a single if statement instead of nested if statements

(SIM102)

pyphare/pyphare/core/box.py

207-207: Ambiguous variable name: l

(E741)

tests/functional/conservation/conserv_3d.py

188-188: SyntaxError: Expected ',', found name


188-188: SyntaxError: Expected ',', found '='


189-189: SyntaxError: Expected ',', found name


189-189: SyntaxError: Expected ',', found '='


189-190: SyntaxError: Expected ']', found newline

pyphare/pyphare/pharesee/geometry.py

161-161: Ambiguous variable name: l

(E741)


164-164: Ambiguous variable name: l

(E741)


167-167: Ambiguous variable name: l

(E741)


188-188: Ambiguous variable name: l

(E741)

tools/python3/cmake.py

48-48: Undefined name cmd

(F821)

tests/simulator/test_initialization.py

394-394: Function definition does not bind loop variable select

(B023)


396-396: Function definition does not bind loop variable patch

(B023)


396-396: Function definition does not bind loop variable nbrGhosts

(B023)


397-397: Function definition does not bind loop variable select

(B023)


582-582: Loop control variable pi not used within loop body

Rename unused pi to _pi

(B007)


586-586: Yoda condition detected

Rewrite as ppc == H

(SIM300)


664-664: Local variable out is assigned to but never used

Remove assignment to unused variable out

(F841)

tests/simulator/__init__.py

326-326: Do not use mutable data structures for argument defaults

Replace with None; initialize within function

(B006)

tests/simulator/test_advance.py

290-290: Loop control variable ilvl not used within loop body

Rename unused ilvl to _ilvl

(B007)

🪛 Cppcheck (2.10-2)
tests/diagnostic/test-diagnostics_3d.cpp

[performance] 24-24: Function parameter 'path' should be passed by const reference.

(passedByValue)

⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: build (macos-13)
  • GitHub Check: build (macos-latest)
  • GitHub Check: build (macos-14)
  • GitHub Check: build (ubuntu-latest, gcc)
  • GitHub Check: build (ubuntu-latest, clang)
  • GitHub Check: Analyze (cpp)
🔇 Additional comments (53)
tools/python3/cmake.py (2)

23-27: Fix missing parameter in config function.

The use_ccache parameter is not being passed to make_config_str(), which could prevent ccache from being enabled.

Apply this diff to fix the parameter passing:

-    cmd = make_config_str(path, samrai_dir, cxx_flags, use_ninja, extra)
+    cmd = make_config_str(path, samrai_dir, cxx_flags, use_ninja, use_ccache, extra)

47-48: Fix undefined variable in run_test function.

The function uses an undefined variable cmd instead of the test parameter.

Apply this diff to fix the variable name:

-    run(test_cmd(cmd, verbose=verbose), capture_output=capture_output)
+    run(test_cmd(test, verbose=verbose), capture_output=capture_output)
🧰 Tools
🪛 Ruff (0.8.2)

48-48: Undefined name cmd

(F821)

tests/simulator/refined_particle_nbr.py (4)

33-37: Good addition of the tearDown method!

This ensures proper cleanup after tests, especially if an exception occurs during a test. This is good practice for maintaining test isolation and preventing resource leaks.


106-116: Appropriate 3D constants and test implementation

The 3D constants and test method are properly implemented following the same pattern as the 1D and 2D tests. The calculations and rationale for the chosen values are well-documented in the comments.


118-136: Well-structured test for checking deltas and weights

The _check_deltas_and_weights method and associated test_values function systematically test the splitter functionality across all supported dimensions, interpolation orders, and refined particle numbers. This provides good coverage for validating the correctness of the 3D implementation.


140-145: Good test organization

The test suite is now explicitly ordered to ensure deterministic test execution. This is a good practice as it ensures that the test_values method runs first to validate the correctness of deltas and weights before the dimension-specific tests.

src/core/utilities/meta/meta_utilities.hpp (3)

9-11: Good implementation of conditional compilation control.

This preprocessor directive provides a clean way to configure the dimensionality of simulations at compile time. Default value of 3 allows for maximum flexibility while enabling conditional code inclusion.


83-88: Well-structured conditional code inclusion for 2D configurations.

The code properly handles comma placement for the tuple syntax and provides a clean separation between dimensionality options.


90-97: Clean implementation of 3D simulator options.

The 3D options are properly guarded by the preprocessor condition and include helpful TODOs for future development. The commented out value (27) in line 94 suggests future work or optimization opportunity.

src/core/data/ndarray/ndarray_vector.hpp (5)

6-6: Good addition of necessary include.

Adding the algorithm header is appropriate since it's needed for the std::copy function used in the implementation.


123-124: Clean implementation of z-dimension access methods.

These methods follow the same pattern as the existing x and y accessors, maintaining consistency in the API design.


348-448: Verify boundary safety in 3D array calculations.

The implementation for 3D cell counting is complex with nested calculations. While the approach is valid, ensure all boundary cases are properly handled to prevent potential array access issues or overlaps.

Consider adding range checking to prevent potential out-of-bounds access if min_ or max_ values exceed array dimensions.


384-424: Comprehensive implementation of 3D border filling.

The fill3D implementation covers all the faces, edges, and corners of the 3D structure. The code organization with clear comments helps in understanding the complex implementation.


529-595: Well-structured 3D masked view operator implementation.

The implementation thoroughly handles all aspects of 3D masked views with clear structure and logic. The use of a lambda for corner handling is a nice touch that reduces code duplication.

tools/python3/git.py (1)

1-17: Well-implemented Git utility functions.

These functions provide a clean wrapper around Git commands with proper error handling and output formatting.

tests/simulator/test_initialization.py (3)

254-273: Good performance tracking with timing measurements.

Adding timing measurements helps track performance across different dimensions and provides valuable data for optimization efforts.


329-351: Comprehensive 3D testing implementation.

The 3D case properly extracts z-components and uses appropriate meshgrid generation for 3D data. The testing approach is consistent with the 1D and 2D implementations.


417-426: Well-calibrated empirical deviation expectations for 3D.

The empirical dimension deviations account for the increased uncertainty in 3D simulations, with appropriate scaling from 1D to 3D.

tests/functional/conservation/CMakeLists.txt (1)

13-13: Script name updated to reflect dimensional specificity.

The change from conserv.py to conserv_2d.py aligns with the PR's goal of updating to 3D support. This modification appropriately separates the 2D test cases in preparation for adding 3D tests.

tests/simulator/__init__.py (2)

6-6: New import for Box supports 3D functionality.

The import of Box from pyphare.core.box is necessary for the implementation of the 3D density function.


98-108: Implementation of 3D density function supports the 3D expansion.

The density_3d_periodic function is well-implemented, extending the existing 1D and 2D pattern. It correctly handles the 3D case with exponential decay in all three dimensions plus a background particle density.

pyphare/pyphare/core/gridlayout.py (1)

369-381: Well-structured mesh coordinates generator for multi-dimensional simulations.

The new meshCoords method nicely expands the GridLayout functionality by generating appropriate mesh coordinates for 1D, 2D, and 3D simulations. It correctly handles each dimension case and properly structures the output arrays.

This implementation is crucial for the 3D update, providing a clean interface for obtaining structured coordinate meshes at the correct positions for field quantities.

tests/simulator/per_test.hpp (1)

111-117: Test infrastructure extended to support 3D simulations.

The addition of the Simulator3dTest template structure and associated type definitions properly extends the testing framework to 3D simulations, following the established pattern from 1D and 2D tests. This is an essential component for validating the 3D functionality being added in this PR.

tests/diagnostic/test-diagnostics_3d.cpp (6)

4-4: Fix include directive to use a header file.

The include directive is using an implementation file (.ipp) instead of a header file (.hpp). While this might be intentional for template implementations, it's flagged by static analysis.


9-12: LGTM: Clean and concise test implementation.

The test case is well-structured and follows the pattern of other test files in the codebase.


14-17: LGTM: Clean and concise test implementation.

The test case is well-structured and follows the pattern of other test files in the codebase.


19-22: LGTM: Clean and concise test implementation.

The test case is well-structured and follows the pattern of other test files in the codebase.


24-27: LGTM: Clean and concise test implementation.

The test case is well-structured and follows the pattern of other test files in the codebase.

🧰 Tools
🪛 Cppcheck (2.10-2)

[performance] 24-24: Function parameter 'path' should be passed by const reference.

(passedByValue)


30-35: LGTM: Standard Google Test initialization.

The main function correctly initializes Google Test and the PHARE lifecycle.

pyphare/pyphare/core/box.py (3)

65-69: Enhanced Box subtraction method handles collections.

This is a good enhancement to support subtracting multiple boxes at once, which simplifies client code.


185-198: Well implemented remove_all function.

The implementation handles the case of removing multiple boxes efficiently by processing each box to be removed iteratively and managing the list of remaining boxes.


209-223: Great addition of the DataSelector class.

The DataSelector class provides a clean interface for data access and modification using Box instances. The docstring effectively communicates its purpose and usage.

tests/amr/data/field/coarsening/coarsening_utils.py (3)

8-20: Well-documented function with clear purpose.

The function documentation clearly explains its purpose and relationship to C++ counterparts, which is valuable for maintainers.


38-56: LGTM: 1D coarsening implementation.

The 1D coarsening logic correctly handles both primal and dual cases, with special handling for the "Bx" quantity.


57-130: Consider systematically generating coarsening weights.

The 2D case contains several code blocks with repetitive patterns of weighting calculations for different cases. Consider refactoring this into a more systematic approach using functions or data structures.

tests/core/numerics/interpolator/test_main.cpp (3)

724-743: LGTM: Well-structured 3D test fixture setup.

The test fixture for 3D interpolation follows the same pattern as the 1D and 2D implementations, which maintains code consistency.


745-769: LGTM: Constructor implementation for 3D particles.

The constructor appropriately initializes the test particles with 3D properties.


783-787: LGTM: Test suite registration and instantiation.

The test suite is properly registered and instantiated for different 3D interpolator types.

tests/amr/data/particles/refine/input/input_3d_ratio_2.txt (1)

10-10: Fix dimensionality in Main section for 3D simulation.
This is a repeat of a prior comment indicating that dim is set to 1 in a 3D configuration, which is inconsistent.

-    dim = 1
+    dim = 3
tests/functional/harris/harris_3d.py (1)

105-108: Prevent potential assertion errors in temperature calculation.
Similar to a previously raised concern, if b2(x, y, z) * 0.5 exceeds K=1, you risk negative temperature results. Consider adjusting K or adding bounds checks.

-    K = 1
+    K = 1.5  # Provide a safety margin
     temp = 1.0 / density(x, y, z) * (K - b2(x, y, z) * 0.5)
-    assert np.all(temp > 0)
+    if not np.all(temp > 0):
+        raise ValueError(
+            "Temperature calculation resulted in negative values. "
+            "Consider adjusting K or magnetic field components."
+        )
pyphare/pyphare/pharesee/geometry.py (2)

59-60: Consider verifying ghost box coverage for edge cases.

The bottom/top ghost boxes use ghost_box_width = max_ghosts - 1, which might cause an off-by-one for certain patch configurations. Please confirm that ghost_box_width consistently covers all intended cells at the domain boundaries.


188-188: Validate the key normalization logic.

You're reassigning shifts by creating sorted keys with "".join(sorted(k)). Please confirm that this rekeying will not mask or combine different shift sets with identical sorted string representations.

🧰 Tools
🪛 Ruff (0.8.2)

188-188: Ambiguous variable name: l

(E741)

src/amr/data/field/coarsening/magnetic_field_coarsener.hpp (2)

82-124: [Duplicate from previous review] Unclear fallback behavior for 1D logic.

As previously noted, the code lumps By/Bz under the dual centering without additional checks. Ensure that unexpected centerings in 1D are handled or trigger a clear exception.


66-79: Verify the indexing for fine-level data.

In fine_start_index, ensure that multiplying coarseIndex by ratio_ correctly maps to all relevant fine cells, especially when corner or boundary cells are involved.

tests/simulator/test_advance.py (9)

9-9: No issues with new import


30-42: Docstring updates
No concerns here.


98-101: No concerns


415-417: New default parameter
Looks good.


444-445: New default parameter
No issues noted.


458-458: No concerns


464-465: Refined parameters
Implementation is consistent.


797-809: Added tested quantities
No issues.

🧰 Tools
🪛 Ruff (0.8.2)

798-798: f-string without any placeholders

Remove extraneous f prefix

(F541)


799-799: f-string without any placeholders

Remove extraneous f prefix

(F541)


816-817: Particle domain checks
No issues found.

Also applies to: 823-824

Comment on lines +187 to +190
dls = [0.2,0.1
nbrcells = [20, 50]
nbrdts = [25000, 100000]

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix syntax error caused by missing bracket.
The list is missing a closing bracket, causing parse failures in subsequent lines.

-    dls = [0.2,0.1
+    dls = [0.2, 0.1]
    nbrcells = [20, 50]
    nbrdts = [25000, 100000]
📝 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
dls = [0.2,0.1
nbrcells = [20, 50]
nbrdts = [25000, 100000]
dls = [0.2, 0.1]
nbrcells = [20, 50]
nbrdts = [25000, 100000]
🧰 Tools
🪛 Ruff (0.8.2)

188-188: SyntaxError: Expected ',', found name


188-188: SyntaxError: Expected ',', found '='


189-189: SyntaxError: Expected ',', found name


189-189: SyntaxError: Expected ',', found '='


189-190: SyntaxError: Expected ']', found newline

Comment on lines +213 to +216
paths = sorted(
paths,
key=lambda path: float(paths[0].split("/")[-1].strip("vth").split("dx")[1]),
)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Correct sorting logic to parse each path properly.
Currently, the lambda always references paths[0], resulting in no meaningful sort.

-    paths = sorted(
-        paths,
-        key=lambda path: float(paths[0].split("/")[-1].strip("vth").split("dx")[1]),
-    )
+    paths = sorted(
+        paths,
+        key=lambda path: float(path.split("/")[-1].strip("vth").split("dx")[1]),
+    )
📝 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
paths = sorted(
paths,
key=lambda path: float(paths[0].split("/")[-1].strip("vth").split("dx")[1]),
)
paths = sorted(
paths,
key=lambda path: float(path.split("/")[-1].strip("vth").split("dx")[1]),
)

Copy link

@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

♻️ Duplicate comments (1)
tests/functional/harris/harris_3d.py (1)

104-108: ⚠️ Potential issue

Prevent potential assertion errors in temperature calculation.

The temperature function could raise an assertion error if b2(x, y, z) * 0.5 exceeds K = 1. Consider adding a safety margin to K or implementing bounds checking for the magnetic field pressure.

-    K = 1
+    K = 1.5  # Increased to provide safety margin
     temp = 1.0 / density(x, y, z) * (K - b2(x, y, z) * 0.5)
-    assert np.all(temp > 0)
+    if not np.all(temp > 0):
+        raise ValueError("Temperature calculation resulted in negative values. "
+                        "Consider adjusting K or magnetic field components.")
     return temp
🧹 Nitpick comments (7)
tests/functional/harris/harris_3d.py (7)

62-63: Rename ambiguous variable l to improve readability.

The parameter name l (lowercase 'L') is ambiguous and can easily be mistaken for the number '1' in some fonts.

-def S(y, y0, l):
-    return 0.5 * (1.0 + np.tanh((y - y0) / l))
+def S(y, y0, width):
+    return 0.5 * (1.0 + np.tanh((y - y0) / width))
🧰 Tools
🪛 Ruff (0.8.2)

62-62: Ambiguous variable name: l

(E741)


37-52: Consider making simulation parameters configurable.

The simulation parameters are currently hardcoded in the config function. Consider making them configurable via function parameters to increase reusability and testability.

-def config():
+def config(time_step=time_step, time_step_nbr=time_step_nbr, dl=dl, cells=cells,
+          max_nbr_levels=2, resistivity=0.001, hyper_resistivity=0.001,
+          output_dir=diag_outputs):
     sim = ph.Simulation(
-        time_step=time_step,
-        time_step_nbr=time_step_nbr,
-        dl=dl,
-        cells=cells,
+        time_step=time_step,
+        time_step_nbr=time_step_nbr,
+        dl=dl,
+        cells=cells,
         refinement="tagging",
-        max_nbr_levels=2,
-        hyper_resistivity=0.001,
-        resistivity=0.001,
+        max_nbr_levels=max_nbr_levels,
+        hyper_resistivity=hyper_resistivity,
+        resistivity=resistivity,
         diag_options={
             "format": "phareh5",
-            "options": {"dir": diag_outputs, "mode": "overwrite"},
+            "options": {"dir": output_dir, "mode": "overwrite"},
         },
         # strict=True,
     )

177-179: TODO comments should include more specific action items.

The TODO comments about 3D plotting should include more specific information about what needs to be done or potential approaches to solve the problem.

-    # TODO3D nico : I removed the plot because the hierarchy plot3d has been removed
-    # TODO3D we need to find a way to plot the data
+    # TODO: Implement new 3D visualization approach to replace the removed hierarchy plot3d.
+    # Options to consider:
+    # 1. Investigate using matplotlib's 3D plotting capabilities with slices
+    # 2. Explore integration with specialized 3D visualization tools such as ParaView
+    # 3. Implement custom slice-based visualization along principal axes

143-172: Consider cleaning up commented-out code in the plot function.

The plot function contains a significant amount of commented-out code, which can be confusing. Consider either removing this code, clearly marking it as temporarily disabled, or implementing placeholder functionality.

def plot(diag_dir):
    run = Run(diag_dir)
    for time in timestamps:
-        # run.GetDivB(time).plot(
-        #     filename=plot_file_for_qty("divb", time),
-        #     plot_patches=True,
-        #     vmin=1e-11,
-        #     vmax=2e-10,
-        # )
-        # run.GetRanks(time).plot(
-        #     filename=plot_file_for_qty("Ranks", time),
-        #     plot_patches=True,
-        # )
        run.GetN(time, pop_name="protons").plot(
            filename=plot_file_for_qty("N", time),
            plot_patches=True,
        )
        for c in ["x", "y", "z"]:
            run.GetB(time).plot(
                filename=plot_file_for_qty(f"b{c}", time),
                qty=f"B{c}",
                plot_patches=True,
            )
-        # run.GetJ(time).plot(
-        #     filename=plot_file_for_qty("jz", time),
-        #     qty="Jz",
-        #     plot_patches=True,
-        #     vmin=-2,
-        #     vmax=2,
-        # )

Consider adding a comment at the top of the function to explain the current limitations:

def plot(diag_dir):
    """
    Plot available 3D simulation results.
    Note: Some visualization capabilities are currently limited in 3D.
    Only density and magnetic field components are currently plotted.
    """
    run = Run(diag_dir)
    # Rest of the function...

122-122: Consider making the random seed configurable.

The random seed for particle initialization is hardcoded, which ensures reproducibility but limits flexibility. Consider making it a configurable parameter.

-    protons = {"charge": 1, "density": density, **vvv, "init": {"seed": 12334}}
+    # Use a fixed seed for reproducibility, but allow overriding
+    particle_seed = os.environ.get("PHARE_PARTICLE_SEED", "12334")
+    protons = {"charge": 1, "density": density, **vvv, "init": {"seed": int(particle_seed)}}

19-20: Consider adding comments explaining the physical meaning of simulation parameters.

The grid parameters are defined without context explaining their physical significance. Adding comments would improve maintainability.

-cells = (200, 400, 50)
-dl = (0.2, 0.2, 0.2)
+# Number of cells in each dimension (x, y, z)
+cells = (200, 400, 50)
+# Cell size in each dimension (x, y, z) in physical units
+dl = (0.2, 0.2, 0.2)

188-188: Consider adding error handling around MPI barrier.

The MPI barrier call could potentially fail in case of communication issues. Consider adding error handling.

-    cpp.mpi_barrier()
+    try:
+        cpp.mpi_barrier()
+    except Exception as e:
+        print(f"MPI barrier failed: {e}")
+        # Consider appropriate cleanup or fallback behavior here
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c9c3437 and f8910e4.

📒 Files selected for processing (1)
  • tests/functional/harris/harris_3d.py (1 hunks)
🧰 Additional context used
🪛 Ruff (0.8.2)
tests/functional/harris/harris_3d.py

62-62: Ambiguous variable name: l

(E741)

⏰ Context from checks skipped due to timeout of 90000ms (7)
  • GitHub Check: Analyze (python)
  • GitHub Check: build (ubuntu-latest, clang)
  • GitHub Check: build (macos-14)
  • GitHub Check: Analyze (cpp)
  • GitHub Check: build (ubuntu-latest, gcc)
  • GitHub Check: build (macos-latest)
  • GitHub Check: build (macos-13)

(np.asarray(actual["offset"]) == np.asarray(exp["offset"])).all()
)
for actual in tested_overlaps[ilvl]:
self.assertTrue(actual in expected[ilvl])

Check notice

Code scanning / CodeQL

Imprecise assert

assertTrue(a in b) cannot provide an informative message. Using assertIn(a, b) instead will give more informative messages.
overlap_boxes += [actual["box"]]
self.assertEqual(len(expected), len(tested_overlaps[ilvl]))
for actual in tested_overlaps[ilvl]:
self.assertTrue(actual in expected)

Check notice

Code scanning / CodeQL

Imprecise assert

assertTrue(a in b) cannot provide an informative message. Using assertIn(a, b) instead will give more informative messages.
(np.asarray(actual["offset"]) == np.asarray(exp["offset"])).all()
)
for actual in tested_overlaps[ilvl]:
self.assertTrue(actual in expected[ilvl])

Check notice

Code scanning / CodeQL

Imprecise assert Note test

assertTrue(a in b) cannot provide an informative message. Using assertIn(a, b) instead will give more informative messages.
overlap_boxes += [actual["box"]]
self.assertEqual(len(expected), len(tested_overlaps[ilvl]))
for actual in tested_overlaps[ilvl]:
self.assertTrue(actual in expected)

Check notice

Code scanning / CodeQL

Imprecise assert Note test

assertTrue(a in b) cannot provide an informative message. Using assertIn(a, b) instead will give more informative messages.
Copy link

@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

🧹 Nitpick comments (4)
pyphare/pyphare/pharesee/geometry.py (1)

87-189: Improved 3D periodicity shifts handling with some optimization opportunities

The function has been successfully extended to handle 3D shifts with comprehensive mapping for all possible combinations, including appropriate handling of front/back dimensions. The addition of key uniqueness validation (lines 184-186) is a good defensive programming practice.

However, there are several instances of ambiguous variable names that could be improved.

-        front = {
-            f"{k}front": [(v[0], v[1], shape_z) for v in l] for k, l in shifts.items()
-        }
-        back = {
-            f"{k}back": [([v[0], v[1], -shape_z]) for v in l] for k, l in shifts.items()
-        }
-
-        shifts = {k: [([v[0], v[1], 0]) for v in l] for k, l in shifts.items()}
+        front = {
+            f"{k}front": [(v[0], v[1], shape_z) for v in shift_list] for k, shift_list in shifts.items()
+        }
+        back = {
+            f"{k}back": [([v[0], v[1], -shape_z]) for v in shift_list] for k, shift_list in shifts.items()
+        }
+
+        shifts = {k: [([v[0], v[1], 0]) for v in shift_list] for k, shift_list in shifts.items()}

And similarly on line 188:

-    shifts = {"".join(sorted(k)): l for k, l in shifts.items()}
+    shifts = {"".join(sorted(k)): shift_list for k, shift_list in shifts.items()}
🧰 Tools
🪛 Ruff (0.8.2)

161-161: Ambiguous variable name: l

(E741)


164-164: Ambiguous variable name: l

(E741)


167-167: Ambiguous variable name: l

(E741)


188-188: Ambiguous variable name: l

(E741)

pyphare/pyphare_tests/test_pharesee/test_geometry.py (1)

81-94: Improved test overlap verification with dictionary comprehension

The test has been enhanced with a more concise dictionary comprehension to collect overlaps for each level, and the comparison logic has been simplified. This update properly tests the changes made to the geometry module.

However, there's an unused loop variable that could be renamed.

-        for ilvl, lvl in enumerate(hierarchy.patch_levels):
+        for ilvl, _lvl in enumerate(hierarchy.patch_levels):
🧰 Tools
🪛 Ruff (0.8.2)

89-89: Loop control variable lvl not used within loop body

Rename unused lvl to _lvl

(B007)

pyphare/pyphare_tests/test_pharesee/test_geometry_2d.py (2)

117-123: Streamlined overlap testing with dictionary comprehension

The test has been updated to use a dictionary comprehension for more concise collection of overlaps, consistent with changes in other tests. This approach properly validates the updated geometry functionality.

However, there are two minor improvements that could be made:

-            for ilvl in expected.keys()
+            for ilvl in expected

This removes the unnecessary .keys() call as dictionaries are directly iterable by keys.

🧰 Tools
🪛 Ruff (0.8.2)

122-122: Use key in dict instead of key in dict.keys()

Remove .keys()

(SIM118)


124-129: Simplified assert logic with unused variable

The assertion logic has been simplified to check if each actual overlap exists within the expected overlaps using the in operator, which enhances readability.

However, there's an unused loop variable that could be renamed.

-        for ilvl, lvl in enumerate(hierarchy.levels().items()):
+        for ilvl, _lvl in enumerate(hierarchy.levels().items()):
🧰 Tools
🪛 Ruff (0.8.2)

124-124: Loop control variable lvl not used within loop body

Rename unused lvl to _lvl

(B007)

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f8910e4 and 8f589e6.

📒 Files selected for processing (3)
  • pyphare/pyphare/pharesee/geometry.py (7 hunks)
  • pyphare/pyphare_tests/test_pharesee/test_geometry.py (2 hunks)
  • pyphare/pyphare_tests/test_pharesee/test_geometry_2d.py (2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
pyphare/pyphare_tests/test_pharesee/test_geometry_2d.py (1)
pyphare/pyphare/pharesee/hierarchy/hierarchy.py (1)
  • levels (214-217)
pyphare/pyphare/pharesee/geometry.py (6)
src/core/utilities/box/box.hpp (11)
  • Box (35-35)
  • Box (37-41)
  • Box (37-37)
  • Box (44-48)
  • Box (198-198)
  • upper (89-89)
  • box (51-51)
  • dim (100-116)
  • dim (118-133)
  • grow (260-265)
  • grow (260-260)
pyphare/pyphare/core/box.py (5)
  • Box (5-72)
  • shape (32-34)
  • grow (114-119)
  • copy (71-72)
  • copy (141-145)
pyphare/pyphare/simulator/simulator.py (1)
  • domain_box (279-281)
pyphare/pyphare/core/gridlayout.py (1)
  • ndim (221-222)
pyphare/pyphare/pharesee/hierarchy/hierarchy.py (1)
  • update (79-98)
pyphare/pyphare/pharesee/hierarchy/patch.py (1)
  • copy (43-47)
🪛 Ruff (0.8.2)
pyphare/pyphare_tests/test_pharesee/test_geometry.py

89-89: Loop control variable lvl not used within loop body

Rename unused lvl to _lvl

(B007)

pyphare/pyphare_tests/test_pharesee/test_geometry_2d.py

122-122: Use key in dict instead of key in dict.keys()

Remove .keys()

(SIM118)


124-124: Loop control variable lvl not used within loop body

Rename unused lvl to _lvl

(B007)

pyphare/pyphare/pharesee/geometry.py

161-161: Ambiguous variable name: l

(E741)


164-164: Ambiguous variable name: l

(E741)


167-167: Ambiguous variable name: l

(E741)


188-188: Ambiguous variable name: l

(E741)

⏰ Context from checks skipped due to timeout of 90000ms (7)
  • GitHub Check: build (macos-14)
  • GitHub Check: build (macos-latest)
  • GitHub Check: build (macos-13)
  • GitHub Check: build (ubuntu-latest, clang)
  • GitHub Check: build (ubuntu-latest, gcc)
  • GitHub Check: Analyze (python)
  • GitHub Check: Analyze (cpp)
🔇 Additional comments (6)
pyphare/pyphare/pharesee/geometry.py (5)

63-74: Good extension to 3D dimension handling

The implementation correctly extends the domain border ghost boxes to handle 3D cases, following the same pattern established for 1D and 2D dimensions. The addition of "front" and "back" ghost boxes is consistent with the overall design.


193-226: Well-structured utility for periodic shift calculation

This new function is a well-designed utility that systematically computes all possible periodic shifts for a box within a domain box across all supported dimensions (1D, 2D, and 3D). The implementation follows a consistent pattern for each dimension, which enhances readability and maintainability.


228-267: Improved overlap computation using the new shift utility

The compute_overlaps function has been rewritten to leverage the new possible_periodic_shifts utility, resulting in cleaner and more maintainable code. The implementation correctly handles overlaps across all dimensions and follows good practices for offset handling.


457-492: Robust extension of periodic list handling for 3D

The get_periodic_list function has been properly extended to handle 3D cases by defining appropriate sides for all three dimensions. The implementation correctly handles sorting of side labels before using them as keys for shifts, ensuring consistent lookup regardless of detection order.


596-596: Improved boxm.remove usage with list comprehension

The code now uses a list comprehension for handling the removal of patch boxes, which is more concise and pythonic.

pyphare/pyphare_tests/test_pharesee/test_geometry_2d.py (1)

170-179: Enhanced large patch overlap testing with dictionary comprehension

The test has been updated to verify overlaps for large patches using a cleaner dictionary comprehension approach. The assertions properly validate that the expected and actual overlaps match, which ensures the geometric calculations correctly handle larger patches.

Copy link

@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

🔭 Outside diff range comments (1)
pyphare/pyphare/pharesee/geometry.py (1)

270-409: 🛠️ Refactor suggestion

Consider merging or differentiating the two overlap functions

There are now two similar functions: compute_overlaps and compute_overlaps_. The latter seems to be the original implementation with detailed comments. Consider either:

  1. Merging them if both are needed and one is meant to replace the other
  2. Better differentiating their purposes with clear documentation
  3. Marking one as deprecated if it's being phased out
♻️ Duplicate comments (2)
pyphare/pyphare_tests/test_pharesee/test_geometry_2d.py (2)

124-129: 🛠️ Refactor suggestion

Use assertIn for better error messages

Replace assertTrue(a in b) with assertIn(a, b) to get more informative error messages when tests fail.

-for ilvl, lvl in enumerate(hierarchy.levels().items()):
+for ilvl, _lvl in enumerate(hierarchy.levels().items()):
    if ilvl not in expected:
        continue
    self.assertEqual(len(expected[ilvl]), len(level_overlaps[ilvl]))
-   for actual in tested_overlaps[ilvl]:
-       self.assertTrue(actual in expected[ilvl])
+   for actual in tested_overlaps[ilvl]:
+       self.assertIn(actual, expected[ilvl])
🧰 Tools
🪛 Ruff (0.8.2)

124-124: Loop control variable lvl not used within loop body

Rename unused lvl to _lvl

(B007)


170-179: 🛠️ Refactor suggestion

Improve test assertions

Same improvements as above:

-for actual in tested_overlaps[ilvl]:
-    self.assertTrue(actual in expected)
+for actual in tested_overlaps[ilvl]:
+    self.assertIn(actual, expected)
🧹 Nitpick comments (3)
pyphare/pyphare/pharesee/geometry.py (1)

85-189: Refactor the periodicity shifts function to improve variable naming

The function has been extended to support 3D periodicity shifts, which is great. However, there are several uses of ambiguous variable names l which can be confusing.

-        front = {
-            f"{k}front": [(v[0], v[1], shape_z) for v in l] for k, l in shifts.items()
-        }
-        back = {
-            f"{k}back": [([v[0], v[1], -shape_z]) for v in l] for k, l in shifts.items()
-        }
-
-        shifts = {k: [([v[0], v[1], 0]) for v in l] for k, l in shifts.items()}
+        front = {
+            f"{k}front": [(v[0], v[1], shape_z) for v in shift_list] for k, shift_list in shifts.items()
+        }
+        back = {
+            f"{k}back": [([v[0], v[1], -shape_z]) for v in shift_list] for k, shift_list in shifts.items()
+        }
+
+        shifts = {k: [([v[0], v[1], 0]) for v in shift_list] for k, shift_list in shifts.items()}

Also, on line 188:

-    shifts = {"".join(sorted(k)): l for k, l in shifts.items()}
+    shifts = {"".join(sorted(k)): shift_list for k, shift_list in shifts.items()}
🧰 Tools
🪛 Ruff (0.8.2)

161-161: Ambiguous variable name: l

(E741)


164-164: Ambiguous variable name: l

(E741)


167-167: Ambiguous variable name: l

(E741)


188-188: Ambiguous variable name: l

(E741)

pyphare/pyphare_tests/test_pharesee/test_geometry.py (1)

81-95: Improved overlap testing with structured dictionary

The introduction of the tested_overlaps dictionary improves code readability and maintainability. The testing logic is now more straightforward.

There's an unused loop variable that should be renamed:

-for ilvl, lvl in enumerate(hierarchy.patch_levels):
+for ilvl, _lvl in enumerate(hierarchy.patch_levels):

or simply:

-for ilvl, lvl in enumerate(hierarchy.patch_levels):
+for ilvl in range(len(hierarchy.patch_levels)):
🧰 Tools
🪛 Ruff (0.8.2)

89-89: Loop control variable lvl not used within loop body

Rename unused lvl to _lvl

(B007)

pyphare/pyphare_tests/test_pharesee/test_geometry_2d.py (1)

117-123: Improved structure for overlap testing

The introduction of the tested_overlaps dictionary improves code organization. There are a couple of minor improvements to make:

  1. Use key in dict instead of key in dict.keys() for better performance
-for ilvl in expected.keys()
+for ilvl in expected
🧰 Tools
🪛 Ruff (0.8.2)

122-122: Use key in dict instead of key in dict.keys()

Remove .keys()

(SIM118)

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8f589e6 and be40b4e.

📒 Files selected for processing (3)
  • pyphare/pyphare/pharesee/geometry.py (7 hunks)
  • pyphare/pyphare_tests/test_pharesee/test_geometry.py (2 hunks)
  • pyphare/pyphare_tests/test_pharesee/test_geometry_2d.py (2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
pyphare/pyphare_tests/test_pharesee/test_geometry_2d.py (1)
pyphare/pyphare/pharesee/hierarchy/hierarchy.py (1)
  • levels (214-217)
pyphare/pyphare/pharesee/geometry.py (3)
pyphare/pyphare/core/box.py (4)
  • Box (5-72)
  • shape (32-34)
  • copy (71-72)
  • copy (141-145)
pyphare/pyphare/simulator/simulator.py (1)
  • domain_box (279-281)
pyphare/pyphare/core/gridlayout.py (1)
  • ndim (221-222)
🪛 Ruff (0.8.2)
pyphare/pyphare_tests/test_pharesee/test_geometry_2d.py

122-122: Use key in dict instead of key in dict.keys()

Remove .keys()

(SIM118)


124-124: Loop control variable lvl not used within loop body

Rename unused lvl to _lvl

(B007)

pyphare/pyphare/pharesee/geometry.py

161-161: Ambiguous variable name: l

(E741)


164-164: Ambiguous variable name: l

(E741)


167-167: Ambiguous variable name: l

(E741)


188-188: Ambiguous variable name: l

(E741)

pyphare/pyphare_tests/test_pharesee/test_geometry.py

89-89: Loop control variable lvl not used within loop body

Rename unused lvl to _lvl

(B007)

⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: build (macos-latest)
  • GitHub Check: build (ubuntu-latest, clang)
  • GitHub Check: build (ubuntu-latest, gcc)
  • GitHub Check: build (macos-13)
  • GitHub Check: Analyze (python)
  • GitHub Check: Analyze (cpp)
🔇 Additional comments (4)
pyphare/pyphare/pharesee/geometry.py (4)

63-74: Added 3D support for domain border ghost boxes

The implementation for 3D ghost boxes is complete and follows the same pattern as the 2D implementation. This is a key enhancement for the 3D update.


193-226: New function for calculating possible periodic shifts

This utility function elegantly handles all possible shifts for 1D, 2D, and 3D cases. It's a good abstraction that simplifies the overlap calculation logic.


228-269: Improved overlap calculation using the new periodic shifts function

The refactored compute_overlaps function leverages the new possible_periodic_shifts function to handle all dimensionalities in a more consistent way.


460-478: Added 3D support for domain side definition

The implementation properly extends the 2D side definitions to include "front" and "back" for the z-dimension in 3D cases.

Copy link

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

🧹 Nitpick comments (2)
pyphare/pyphare/pharesee/geometry.py (2)

184-189: Improve readability of dictionary key transformation.

The key checking and transformation logic is clever but could be more readable. Also, the variable l is ambiguous.

-    assert len(list(shifts.keys())) == len(
-        set(["".join(sorted(k)) for k in list(shifts.keys())])
-    )
-
-    shifts = {"".join(sorted(k)): l for k, l in shifts.items()}
+    # Ensure all keys are unique when normalized (sorted characters)
+    assert len(list(shifts.keys())) == len(
+        set(["".join(sorted(k)) for k in list(shifts.keys())])
+    )
+
+    # Normalize keys by sorting their characters
+    shifts = {"".join(sorted(k)): shift_list for k, shift_list in shifts.items()}
🧰 Tools
🪛 Ruff (0.8.2)

188-188: Ambiguous variable name: l

(E741)


275-413: Consider removing deprecated function.

The compute_overlaps_ function appears to be kept for backward compatibility or as a reference while the new compute_overlaps function is introduced. Once you've verified the new implementation works correctly across all test cases, consider removing this deprecated version to avoid confusion and reduce code duplication.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between be40b4e and 1e9af03.

📒 Files selected for processing (1)
  • pyphare/pyphare/pharesee/geometry.py (7 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
pyphare/pyphare/pharesee/geometry.py (2)
pyphare/pyphare/core/box.py (4)
  • Box (5-72)
  • shape (32-34)
  • copy (71-72)
  • copy (141-145)
pyphare/pyphare/core/phare_utilities.py (1)
  • np_array_ify (62-67)
🪛 Ruff (0.8.2)
pyphare/pyphare/pharesee/geometry.py

161-161: Ambiguous variable name: l

(E741)


164-164: Ambiguous variable name: l

(E741)


167-167: Ambiguous variable name: l

(E741)


188-188: Ambiguous variable name: l

(E741)

🔇 Additional comments (5)
pyphare/pyphare/pharesee/geometry.py (5)

63-74: Extended for 3D support!

The domain_border_ghost_boxes function has been properly extended to handle 3D cases, adding "front" and "back" ghost boxes for the z-dimension, following the same pattern as the 2D implementation.


193-226: Well-implemented 3D periodic shift generation!

This new function efficiently generates all possible periodic shifts for boxes in 1D, 2D, and 3D configurations. The nested loops approach is clear and properly handles all combinations of shifts.


228-272: Good refactoring of overlap computation.

The compute_overlaps function has been well refactored to use the new possible_periodic_shifts function, making the code more concise and easier to understand. The logic handles all dimensions consistently.


474-482: Good extension to 3D for side boxes.

The get_periodic_list function has been properly extended to handle 3D cases, adding "front" and "back" sides, consistent with the approach in other parts of the code.


601-601: Using Box.remove_all method correctly.

The code now uses the Box.remove_all method to subtract multiple boxes at once, which is more efficient than repeatedly applying the subtraction operator.

Comment on lines +161 to +171
f"{k}front": [(v[0], v[1], shape_z) for v in l] for k, l in shifts.items()
}
back = {
f"{k}back": [([v[0], v[1], -shape_z]) for v in l] for k, l in shifts.items()
}

shifts = {k: [([v[0], v[1], 0]) for v in l] for k, l in shifts.items()}

shifts.update(front)
shifts.update(back)
shifts.update(
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Address ambiguous variable names.

The variables l in the dictionary comprehensions make the code harder to read and maintain.

-        front = {
-            f"{k}front": [(v[0], v[1], shape_z) for v in l] for k, l in shifts.items()
-        }
-        back = {
-            f"{k}back": [([v[0], v[1], -shape_z]) for v in l] for k, l in shifts.items()
-        }
-
-        shifts = {k: [([v[0], v[1], 0]) for v in l] for k, l in shifts.items()}
+        front = {
+            f"{k}front": [(v[0], v[1], shape_z) for v in shift_list] for k, shift_list in shifts.items()
+        }
+        back = {
+            f"{k}back": [([v[0], v[1], -shape_z]) for v in shift_list] for k, shift_list in shifts.items()
+        }
+
+        shifts = {k: [([v[0], v[1], 0]) for v in shift_list] for k, shift_list in shifts.items()}
📝 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
f"{k}front": [(v[0], v[1], shape_z) for v in l] for k, l in shifts.items()
}
back = {
f"{k}back": [([v[0], v[1], -shape_z]) for v in l] for k, l in shifts.items()
}
shifts = {k: [([v[0], v[1], 0]) for v in l] for k, l in shifts.items()}
shifts.update(front)
shifts.update(back)
shifts.update(
front = {
f"{k}front": [(v[0], v[1], shape_z) for v in shift_list] for k, shift_list in shifts.items()
}
back = {
f"{k}back": [([v[0], v[1], -shape_z]) for v in shift_list] for k, shift_list in shifts.items()
}
shifts = {k: [([v[0], v[1], 0]) for v in shift_list] for k, shift_list in shifts.items()}
shifts.update(front)
shifts.update(back)
shifts.update(
🧰 Tools
🪛 Ruff (0.8.2)

161-161: Ambiguous variable name: l

(E741)


164-164: Ambiguous variable name: l

(E741)


167-167: Ambiguous variable name: l

(E741)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants