Skip to content

Conversation

@PhilipDeegan
Copy link
Member

@PhilipDeegan PhilipDeegan commented Dec 8, 2025

output from new harris diff test

diff 0.0
B max:  [np.float64(5.10702591327572e-15), np.float64(0.0), np.float64(0.0)]
ion charge rho max:  [np.float64(0.0), np.float64(0.0), np.float64(0.0)]
ion mass rho max:  [np.float64(0.0), np.float64(0.0), np.float64(0.0)]
E max:  [np.float64(1.8561541192951836e-16), np.float64(0.0), np.float64(0.0)]

diff 0.005
B max:  [np.float64(6.505213034913027e-19), np.float64(0.0), np.float64(0.0)]
ion charge rho max:  [np.float64(0.0), np.float64(0.0), np.float64(0.0)]
ion mass rho max:  [np.float64(0.0), np.float64(0.0), np.float64(0.0)]
E max:  [np.float64(1.3877787807814457e-17), np.float64(0.0), np.float64(0.0)]

diff 0.01
B max:  [np.float64(6.505213034913027e-19), np.float64(0.0), np.float64(0.0)]
ion charge rho max:  [np.float64(0.0), np.float64(0.0), np.float64(0.0)]
ion mass rho max:  [np.float64(0.0), np.float64(0.0), np.float64(0.0)]
E max:  [np.float64(3.469446951953614e-18), np.float64(0.0), np.float64(0.0)]

@coderabbitai
Copy link

coderabbitai bot commented Dec 8, 2025

📝 Walkthrough

Walkthrough

Adds Box-based data selection utilities and hierarchy diffing in Python; generalizes border aggregation to templated operations (e.g., max) in C++; introduces ion-specific border-filling across messenger/initializer/solver layers; adds diagnostic tooling and assorted const-correctness, validation, and formatting tweaks.

Changes

Cohort / File(s) Summary
Python: box & grid utilities
pyphare/pyphare/core/box.py, pyphare/pyphare/core/gridlayout.py
Added select(data, box) and DataSelector for Box-based slicing; added "value": "primal" keys to yee_centering direction entries.
Python: hierarchy, patchdata & diff tooling
pyphare/pyphare/pharesee/hierarchy/hierarchy.py, pyphare/pyphare/pharesee/hierarchy/patchdata.py, pyphare/pyphare/pharesee/hierarchy/hierarchy_utils.py
Added zeros_like(), has_non_zero(), max() on PatchHierarchy; FieldData.zeros_like(); new top-level diff_hierarchy(hier) to compute overlap diffs into a diff-hierarchy.
Python: geometry overlap metadata
pyphare/pyphare/pharesee/geometry.py
Overlap dicts now include "name" (reference patch-data name); append helper updated to accept/pass name.
Python: runtime, simulator & diagnostics
pyphare/pyphare/pharesee/run/run.py, pyphare/pyphare/simulator/simulator.py, pyphare/pyphare/pharein/simulation.py, tools/diff_diags.py
Guarded dict assignment to avoid KeyError; changed Simulator.print_one_line default to False; adjusted smallest patch size calc; added tools/diff_diags.py diagnostic script and helpers.
Python: tests
tests/simulator/__init__.py
Adjusted refinement vector parameter from [8] * dim[12] * dim.
C++: templated field/tensor operations & const-correctness
src/amr/data/field/field_data.hpp, src/amr/data/tensorfield/tensor_field_data.hpp
Replaced sum-specific paths with templated operate<Operation>() / unpackStreamAnd<Operation>(); made many PatchData/BoxOverlap params const-correct.
C++: transaction refactor (border ops)
src/amr/messengers/field_operate_transaction.hpp, src/diagnostic/diagnostic_model_view.hpp
Renamed FieldBorderSumTransactionFieldBorderOpTransaction<FieldData, Operation>; factories and callers updated to accept an Operation policy (e.g., PlusEqualsOp).
C++: new max operation & types
src/core/utilities/types.hpp
Added PHARE::core::SetMax<D> functor to support max reductions.
C++: refiner & schedule updates for max ops
src/amr/messengers/refiner.hpp
Added PatchFieldBorderMax / PatchVecFieldBorderMax refiner types and schedule branches using SetMaxOp/PlusEqualsOp; adjusted branching and error handling.
C++: messenger/strategy ion-border API
src/amr/messengers/hybrid_messenger_strategy.hpp, src/amr/messengers/hybrid_messenger.hpp, src/amr/messengers/mhd_hybrid_messenger_strategy.hpp, src/amr/messengers/hybrid_hybrid_messenger_strategy.hpp, src/amr/messengers/hybrid_messenger_info.hpp
Added fillIonBorders(...) to strategy and messenger; introduced ion-specific max-refiner pools, registration, and population from maxBorderFields/maxBorderVecFields.
C++: integration points for ion border fills
src/amr/level_initializer/hybrid_level_initializer.hpp, src/amr/solvers/solver_ppc.hpp, src/amr/physical_models/hybrid_model.hpp
Inserted calls to fillIonBorders(...) during level init and ion-move; extended fillMessengerInfo() to add ion-related maxBorderFields/maxBorderVecFields.
C++: field operate transaction factory & callers
src/amr/messengers/field_operate_transaction.hpp, src/diagnostic/diagnostic_model_view.hpp
Transaction/factory templates updated across call sites (e.g., diagnostic view now uses FieldBorderOpTransactionFactory<..., PlusEqualsOp>).
C++: misc formatting & MPI edge-case
src/core/data/field/field_box.hpp, src/core/data/tensorfield/tensorfield.hpp, src/core/utilities/mpi_utils.hpp
Minor formatting edits; collectVector now appends empty vectors when an MPI segment size is zero.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant LevelInit as HybridLevelInitializer
    participant Solver as SolverPPC
    participant Messenger as HybridMessenger
    participant Strategy as HybridHybridMessengerStrategy
    participant RefinerPool as MaxRefinerPool
    participant PatchData as PatchData/FieldData

    Note over LevelInit,Solver: two insertion points call messenger to fill ion borders
    LevelInit->>Messenger: fillIonBorders(ions, level, time)
    Solver->>Messenger: fromCoarser.fillIonBorders(ions, level, newTime)
    Messenger->>Strategy: strat_->fillIonBorders(ions, level, time)
    Strategy->>RefinerPool: for each ionDensity/ionFlux BorderMaxRefiner: execute()
    RefinerPool->>PatchData: unpackStreamAnd<SetMaxOp>(stream, overlap) / operate<SetMaxOp>(src, overlap)
    PatchData->>PatchData: apply SetMax (d = max(d, d0)) over overlap slice
    Strategy->>Strategy: continue for remaining refiners
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

refactoring, posterity

Suggested reviewers

  • nicolasaunai
  • UCaromel

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.39% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Difference hierarchies / max border schedules' directly corresponds to the primary changes across the PR: addition of diff_hierarchy utility, max-based border refiners, and generic operation scheduling.
Description check ✅ Passed The description provides output from a new Harris difference test demonstrating the feature works, showing numerical maxima for B, ion densities, and E fields at multiple time steps.
✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c6cbda7 and 45e0f76.

📒 Files selected for processing (1)
  • pyphare/pyphare/pharesee/geometry.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • pyphare/pyphare/pharesee/geometry.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: build (ubuntu-latest, gcc)
  • GitHub Check: build (ubuntu-latest, clang)
  • GitHub Check: build (macos-14)
  • GitHub Check: Analyze (python)
  • GitHub Check: Analyze (cpp)

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

❤️ Share

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

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

🧹 Nitpick comments (10)
src/amr/messengers/field_sum_transaction.hpp (1)

6-11: Minor include organization note.

The includes could be reorganized to follow standard convention: local includes (quoted) grouped first, then system includes (angle brackets) grouped after. Currently they're interleaved with blank lines. This is a minor style point and not critical, but worth noting for consistency across the codebase.

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

186-187: Consider clearer variable naming.

Static analysis flags the variable name l as ambiguous (could be confused with 1). Consider using lower and upper for clarity.

Apply this diff:

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

Note: Static analysis also suggests adding strict=True to zip() (Python 3.10+), but since Box.__init__ enforces equal-length arrays, this is optional.


189-203: LGTM! DataSelector enables Box-based slicing.

The implementation correctly enables syntax like DataSelector(data)[box] = val by returning views that support slice assignment. The dual support for Box and regular slicing provides good flexibility.

The docstring could be more descriptive:

 class DataSelector:
     """
-        can't assign return to function unless []
-        usage
-        DataSelector(data)[box] = val
+    Wrapper enabling Box-based slicing and assignment on NumPy arrays.
+    
+    Usage:
+        DataSelector(data)[box] = val  # Assign to Box region
+        view = DataSelector(data)[box]  # Get Box region
     """
src/core/data/field/field_box.hpp (1)

71-82: Consider reusing operate_on_fields with SetMax operator to avoid duplication.

This function duplicates the iteration logic from operate_on_fields above. Since SetMax already exists in core/utilities/types.hpp and is used elsewhere (e.g., field_data.hpp), consider:

-void max_of_fields(auto& dst, auto const& src)
-{
-    assert(dst.lcl_box.size() == src.lcl_box.size());
-    auto src_it = src.lcl_box.begin();
-    auto dst_it = dst.lcl_box.begin();
-    for (; dst_it != dst.lcl_box.end(); ++src_it, ++dst_it)
-    {
-        auto& dst_val = dst.field(*dst_it);
-        auto& src_val = src.field(*src_it);
-        dst_val       = std::max(dst_val, src_val);
-    }
-}
+void max_of_fields(auto& dst, auto const& src)
+{
+    using value_type = std::decay_t<typename std::decay_t<decltype(dst.field)>::value_type>;
+    operate_on_fields<SetMax<value_type>>(dst, src);
+}

Also, if keeping the current implementation, line 79 should use auto const& src_val since src is const-qualified.

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

68-69: Rename ambiguous variable l for clarity.

The variable l is easily confused with 1 or I. Consider using a more descriptive name like length or scale.

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

183-190: Remove extraneous f prefix from strings without placeholders.

Several f-strings have no interpolation, as flagged by static analysis:

         ranks.plot(
-            filename=plot_file_for_qty(plot_dir, f"ranks", new_time, f"L{ilvl}"),
+            filename=plot_file_for_qty(plot_dir, "ranks", new_time, f"L{ilvl}"),

Similar changes needed at lines 215 (f"ionCharge""ionCharge") and 230 (f"ionMass""ionMass").


276-282: Use not in instead of not ... in for membership test.

More idiomatic Python style:

-            if not "Unable to synchronously open object" in err:  # no diag for time
+            if "Unable to synchronously open object" not in err:  # no diag for time
src/amr/messengers/refiner.hpp (1)

116-137: Minor: Clarify comment wording.

The comments state "schedule used to == max of density and flux" - the == notation is slightly confusing. Consider rewording to "schedule used to compute max of..." for consistency with the sum variant comment style ("schedule used to += density and flux").

-            // schedule used to == max of density and flux for populations
+            // schedule used to compute max of density and flux for populations
             // on complete overlaped ghost box nodes
             else if constexpr (Type == RefinerType::PatchFieldBorderMax)
             {
@@ -128,7 +128,7 @@
             }


-            // schedule used to == max of density and flux for populations
+            // schedule used to compute max of density and flux for populations
             // on complete overlaped ghost box nodes
             else if constexpr (Type == RefinerType::PatchVecFieldBorderMax)
             {
src/amr/messengers/hybrid_hybrid_messenger_strategy.hpp (1)

490-502: Hardcoded size assertions may be fragile.

The assertions ionFluxBorderMaxRefiners_.size() == 1 and ionDensityBorderMaxRefiners_.size() == 2 are hardcoded magic numbers. If the configuration in registerInitComms changes (e.g., maxBorderFields or maxBorderVecFields sizes change), these assertions will fail without clear context.

Consider either:

  1. Deriving the expected size from a shared constant or configuration
  2. Adding a clarifying comment explaining why these specific sizes are expected
src/amr/messengers/field_max_transaction.hpp (1)

17-18: Incomplete documentation.

The class documentation contains a TODO placeholder. Consider completing the description to explain the purpose of FieldBorderMaxTransaction (e.g., "handles max-reduction operations for field border synchronization across patches").

-/** * @brief FieldBorderMaxTransaction is TODO
+/** * @brief FieldBorderMaxTransaction handles max-reduction operations for field border
+ *         synchronization during refinement across SAMRAI patches.
  *
  */
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8cd180f and 0f7bb60.

📒 Files selected for processing (26)
  • pyphare/pyphare/core/box.py (1 hunks)
  • pyphare/pyphare/core/gridlayout.py (3 hunks)
  • pyphare/pyphare/pharesee/geometry.py (3 hunks)
  • pyphare/pyphare/pharesee/hierarchy/hierarchy.py (1 hunks)
  • pyphare/pyphare/pharesee/hierarchy/hierarchy_utils.py (1 hunks)
  • pyphare/pyphare/pharesee/hierarchy/patchdata.py (1 hunks)
  • pyphare/pyphare/pharesee/run/run.py (1 hunks)
  • pyphare/pyphare/simulator/simulator.py (2 hunks)
  • src/amr/data/field/field_data.hpp (8 hunks)
  • src/amr/data/tensorfield/tensor_field_data.hpp (2 hunks)
  • src/amr/level_initializer/hybrid_level_initializer.hpp (1 hunks)
  • src/amr/messengers/field_max_transaction.hpp (1 hunks)
  • src/amr/messengers/field_sum_transaction.hpp (1 hunks)
  • src/amr/messengers/hybrid_hybrid_messenger_strategy.hpp (7 hunks)
  • src/amr/messengers/hybrid_messenger.hpp (1 hunks)
  • src/amr/messengers/hybrid_messenger_info.hpp (1 hunks)
  • src/amr/messengers/hybrid_messenger_strategy.hpp (1 hunks)
  • src/amr/messengers/mhd_hybrid_messenger_strategy.hpp (1 hunks)
  • src/amr/messengers/refiner.hpp (6 hunks)
  • src/amr/physical_models/hybrid_model.hpp (1 hunks)
  • src/amr/solvers/solver_ppc.hpp (1 hunks)
  • src/core/data/field/field_box.hpp (1 hunks)
  • src/core/data/tensorfield/tensorfield.hpp (1 hunks)
  • src/core/utilities/mpi_utils.hpp (1 hunks)
  • src/core/utilities/types.hpp (1 hunks)
  • tests/functional/harris/harris_2d_diff.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.hpp

⚙️ CodeRabbit configuration file

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

Files:

  • src/core/utilities/types.hpp
  • src/amr/messengers/hybrid_messenger_strategy.hpp
  • src/amr/level_initializer/hybrid_level_initializer.hpp
  • src/amr/messengers/mhd_hybrid_messenger_strategy.hpp
  • src/amr/messengers/hybrid_messenger_info.hpp
  • src/amr/messengers/hybrid_messenger.hpp
  • src/core/data/field/field_box.hpp
  • src/amr/messengers/refiner.hpp
  • src/amr/physical_models/hybrid_model.hpp
  • src/core/data/tensorfield/tensorfield.hpp
  • src/amr/messengers/field_sum_transaction.hpp
  • src/amr/data/tensorfield/tensor_field_data.hpp
  • src/amr/solvers/solver_ppc.hpp
  • src/amr/messengers/hybrid_hybrid_messenger_strategy.hpp
  • src/core/utilities/mpi_utils.hpp
  • src/amr/data/field/field_data.hpp
  • src/amr/messengers/field_max_transaction.hpp
🧠 Learnings (5)
📓 Common learnings
Learnt from: PhilipDeegan
Repo: PHAREHUB/PHARE PR: 784
File: tests/simulator/test_restarts.py:333-339
Timestamp: 2024-10-09T08:32:15.667Z
Learning: PhilipDeegan has indicated a preference for minimal and efficient code, even after making changes to get a function working. There may be opportunities to remove or optimize parts of the code such as sleep/wait/advances.
Learnt from: PhilipDeegan
Repo: PHAREHUB/PHARE PR: 784
File: tests/simulator/test_restarts.py:333-339
Timestamp: 2024-07-26T22:04:34.160Z
Learning: PhilipDeegan has indicated a preference for minimal and efficient code, even after making changes to get a function working. There may be opportunities to remove or optimize parts of the code such as sleep/wait/advances.
Learnt from: PhilipDeegan
Repo: PHAREHUB/PHARE PR: 1068
File: src/amr/data/field/coarsening/electric_field_coarsener.hpp:1-2
Timestamp: 2025-09-17T13:35:11.533Z
Learning: PhilipDeegan prefers header guard names that include the full directory path structure, following the pattern PHARE_[PATH_WITH_UNDERSCORES]_HPP. For example, a file at src/amr/data/field/coarsening/electric_field_coarsener.hpp should use PHARE_AMR_DATA_FIELD_COARSENING_ELECTRIC_FIELD_COARSENER_HPP as the header guard.
Learnt from: PhilipDeegan
Repo: PHAREHUB/PHARE PR: 888
File: src/hdf5/detail/h5/h5_file.hpp:78-79
Timestamp: 2024-10-09T08:32:15.667Z
Learning: Avoid commenting on draft PRs for the user PhilipDeegan.
Learnt from: PhilipDeegan
Repo: PHAREHUB/PHARE PR: 888
File: src/hdf5/detail/h5/h5_file.hpp:78-79
Timestamp: 2024-09-05T17:02:58.784Z
Learning: Avoid commenting on draft PRs for the user PhilipDeegan.
📚 Learning: 2024-10-18T13:23:32.074Z
Learnt from: PhilipDeegan
Repo: PHAREHUB/PHARE PR: 910
File: pyphare/pyphare/pharesee/hierarchy/hierarchy_utils.py:7-7
Timestamp: 2024-10-18T13:23:32.074Z
Learning: In the `pyphare.pharesee.hierarchy` module, importing `PatchHierarchy` and `format_timestamp` from `hierarchy.py` into `hierarchy_utils.py` is acceptable as long as `hierarchy.py` does not import `hierarchy_utils.py`, thereby avoiding a cyclic import.

Applied to files:

  • pyphare/pyphare/pharesee/hierarchy/hierarchy_utils.py
  • pyphare/pyphare/pharesee/hierarchy/hierarchy.py
📚 Learning: 2025-02-06T10:52:56.461Z
Learnt from: rochSmets
Repo: PHAREHUB/PHARE PR: 921
File: src/core/data/ions/ions.hpp:138-142
Timestamp: 2025-02-06T10:52:56.461Z
Learning: The bulk velocity calculation in PHARE must maintain physical consistency across both ion and electron calculations. Zero density cases need special handling that preserves the physics, particularly since the calculations are used in level initialization, ion updates, and electron physics.

Applied to files:

  • src/amr/level_initializer/hybrid_level_initializer.hpp
📚 Learning: 2025-11-10T09:37:57.021Z
Learnt from: PhilipDeegan
Repo: PHAREHUB/PHARE PR: 1084
File: src/diagnostic/diagnostic_model_view.hpp:219-221
Timestamp: 2025-11-10T09:37:57.021Z
Learning: In PHARE, temporary fields like tmpField_, tmpVec_, and tmpTensor_ in src/diagnostic/diagnostic_model_view.hpp use a name-based resource allocation pattern. Fields with names "PHARE_sumField", "PHARE_sumVec", and "PHARE_sumTensor" are allocated globally elsewhere in the resources manager (e.g., in src/amr/messengers/hybrid_hybrid_messenger_strategy.hpp), and components reference the same storage through identically-named field objects. These do not require local allocation via rm.enumerate().

Applied to files:

  • src/amr/messengers/refiner.hpp
  • src/amr/messengers/hybrid_hybrid_messenger_strategy.hpp
  • src/amr/messengers/field_max_transaction.hpp
📚 Learning: 2024-10-22T10:05:54.014Z
Learnt from: PhilipDeegan
Repo: PHAREHUB/PHARE PR: 0
File: :0-0
Timestamp: 2024-10-22T10:05:54.014Z
Learning: The `__eq__` method in `ParticleData` uses `self.dataset == that.dataset` for dataset comparison, which might not be sufficient for numpy arrays as it returns an array of booleans. The use of `np.array_equal` is suggested for a more robust comparison.

Applied to files:

  • pyphare/pyphare/pharesee/hierarchy/patchdata.py
🧬 Code graph analysis (14)
src/core/utilities/types.hpp (1)
src/amr/data/field/field_data.hpp (2)
  • max (475-486)
  • max (475-476)
src/amr/messengers/hybrid_messenger_strategy.hpp (1)
src/amr/messengers/hybrid_hybrid_messenger_strategy.hpp (16)
  • ions (412-421)
  • ions (412-412)
  • ions (425-450)
  • ions (425-425)
  • ions (452-487)
  • ions (452-452)
  • ions (490-502)
  • ions (490-490)
  • ions (513-552)
  • ions (513-514)
  • level (587-611)
  • level (587-589)
  • level (717-728)
  • level (717-717)
  • level (914-929)
  • level (914-914)
pyphare/pyphare/pharesee/hierarchy/hierarchy_utils.py (3)
pyphare/pyphare/pharesee/geometry.py (1)
  • hierarchy_overlaps (297-307)
pyphare/pyphare/pharesee/hierarchy/hierarchy.py (2)
  • zeros_like (617-631)
  • has_non_zero (633-646)
pyphare/pyphare/core/box.py (4)
  • amr_to_local (182-183)
  • shift (107-108)
  • select (186-187)
  • DataSelector (189-203)
src/amr/level_initializer/hybrid_level_initializer.hpp (1)
src/amr/messengers/hybrid_hybrid_messenger_strategy.hpp (16)
  • ions (412-421)
  • ions (412-412)
  • ions (425-450)
  • ions (425-425)
  • ions (452-487)
  • ions (452-452)
  • ions (490-502)
  • ions (490-490)
  • ions (513-552)
  • ions (513-514)
  • level (587-611)
  • level (587-589)
  • level (717-728)
  • level (717-717)
  • level (914-929)
  • level (914-914)
pyphare/pyphare/pharesee/hierarchy/hierarchy.py (3)
pyphare/pyphare/pharesee/hierarchy/patchdata.py (1)
  • zeros_like (194-201)
pyphare/pyphare/core/box.py (2)
  • copy (68-69)
  • copy (138-142)
pyphare/pyphare/pharesee/hierarchy/patch.py (1)
  • copy (43-47)
src/amr/messengers/hybrid_messenger.hpp (2)
src/amr/messengers/hybrid_hybrid_messenger_strategy.hpp (16)
  • ions (412-421)
  • ions (412-412)
  • ions (425-450)
  • ions (425-425)
  • ions (452-487)
  • ions (452-452)
  • ions (490-502)
  • ions (490-490)
  • ions (513-552)
  • ions (513-514)
  • level (587-611)
  • level (587-589)
  • level (717-728)
  • level (717-717)
  • level (914-929)
  • level (914-914)
src/amr/messengers/hybrid_messenger_strategy.hpp (6)
  • ions (83-84)
  • ions (88-89)
  • ions (131-132)
  • ions (134-135)
  • ions (137-138)
  • level (121-121)
src/core/data/field/field_box.hpp (2)
src/amr/data/field/field_data.hpp (4)
  • src (311-311)
  • src (316-316)
  • max (475-486)
  • max (475-476)
src/amr/data/tensorfield/tensor_field_data.hpp (4)
  • src (339-339)
  • src (343-343)
  • max (525-536)
  • max (525-526)
src/amr/messengers/refiner.hpp (1)
src/amr/messengers/communicator.hpp (6)
  • algo (88-97)
  • algo (88-88)
  • algo (105-109)
  • algo (105-106)
  • algo (112-116)
  • algo (112-113)
pyphare/pyphare/core/box.py (2)
pyphare/pyphare/pharesee/hierarchy/patchdata.py (2)
  • select (94-116)
  • select (233-234)
src/core/utilities/box/box.hpp (1)
  • upper (96-96)
src/amr/data/tensorfield/tensor_field_data.hpp (2)
src/amr/data/field/field_data.hpp (22)
  • src (311-311)
  • src (316-316)
  • overlap (196-199)
  • overlap (196-196)
  • overlap (408-425)
  • overlap (408-408)
  • stream (207-238)
  • stream (207-208)
  • stream (245-249)
  • stream (245-246)
  • stream (312-313)
  • stream (317-318)
  • unpackStreamAndMax (466-472)
  • unpackStreamAndMax (466-467)
  • unpackStream (252-280)
  • unpackStream (252-253)
  • max (475-486)
  • max (475-476)
  • copy_ (335-351)
  • copy_ (335-337)
  • copy_ (360-402)
  • copy_ (360-360)
src/amr/messengers/field_max_transaction.hpp (5)
  • stream (63-63)
  • stream (65-65)
  • stream (69-69)
  • unpackStream (149-159)
  • unpackStream (149-149)
pyphare/pyphare/pharesee/hierarchy/patchdata.py (1)
pyphare/pyphare/pharesee/hierarchy/hierarchy.py (1)
  • zeros_like (617-631)
tests/functional/harris/harris_2d_diff.py (5)
pyphare/pyphare/cpp/__init__.py (1)
  • cpp_lib (6-9)
pyphare/pyphare/pharesee/run/run.py (9)
  • Run (35-293)
  • GetRanks (179-188)
  • filename (191-192)
  • GetB (92-100)
  • GetNi (116-118)
  • GetMassDensity (112-114)
  • GetE (102-110)
  • times (292-293)
  • all_times (275-290)
pyphare/pyphare/pharesee/hierarchy/hierarchy_utils.py (1)
  • diff_hierarchy (615-670)
tests/simulator/__init__.py (1)
  • SimulatorTest (200-266)
pyphare/pyphare/pharesee/hierarchy/__init__.py (1)
  • hierarchy_from (13-46)
src/amr/data/field/field_data.hpp (2)
src/amr/messengers/field_max_transaction.hpp (2)
  • unpackStream (149-159)
  • unpackStream (149-149)
src/amr/messengers/field_sum_transaction.hpp (5)
  • unpackStream (162-172)
  • unpackStream (162-162)
  • stream (76-76)
  • stream (78-78)
  • stream (82-82)
src/amr/messengers/field_max_transaction.hpp (2)
src/amr/messengers/field_sum_transaction.hpp (11)
  • dst_level (206-228)
  • dst_level (207-212)
  • stream (76-76)
  • stream (78-78)
  • stream (82-82)
  • packStream (153-159)
  • packStream (153-153)
  • unpackStream (162-172)
  • unpackStream (162-162)
  • level (230-237)
  • level (231-232)
src/amr/data/field/field_data.hpp (12)
  • overlap (196-199)
  • overlap (196-196)
  • overlap (408-425)
  • overlap (408-408)
  • stream (207-238)
  • stream (207-208)
  • stream (245-249)
  • stream (245-246)
  • stream (312-313)
  • stream (317-318)
  • unpackStream (252-280)
  • unpackStream (252-253)
🪛 GitHub Check: CodeQL
pyphare/pyphare/pharesee/hierarchy/hierarchy_utils.py

[notice] 617-617: Cyclic import
Import of module pyphare.pharesee.geometry begins an import cycle.

🪛 Ruff (0.14.7)
pyphare/pyphare/pharesee/hierarchy/hierarchy_utils.py

620-620: Prefer next(iter(diff_hier.time_hier.keys())) over single element slice

Replace with next(iter(diff_hier.time_hier.keys()))

(RUF015)


626-626: Avoid specifying long messages outside the exception class

(TRY003)

pyphare/pyphare/pharesee/hierarchy/hierarchy.py

624-624: Prefer next(iter(copy.time_hier.values())) over single element slice

Replace with next(iter(copy.time_hier.values()))

(RUF015)


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

Rename unused ilvl to _ilvl

(B007)


638-638: Prefer next(iter(self.time_hier.values())) over single element slice

Replace with next(iter(self.time_hier.values()))

(RUF015)


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

Rename unused ilvl to _ilvl

(B007)


642-642: Loop control variable key not used within loop body

Rename unused key to _key

(B007)


653-653: Prefer next(iter(self.time_hier.values())) over single element slice

Replace with next(iter(self.time_hier.values()))

(RUF015)


658-658: Loop control variable key not used within loop body

Rename unused key to _key

(B007)

pyphare/pyphare/core/box.py

187-187: Ambiguous variable name: l

(E741)


187-187: zip() without an explicit strict= parameter

Add explicit value for parameter strict=

(B905)

tests/functional/harris/harris_2d_diff.py

1-1: Shebang is present but file is not executable

(EXE001)


60-60: Unused function argument: x

(ARG001)


68-68: Ambiguous variable name: l

(E741)


103-103: Unused function argument: x

(ARG001)


103-103: Unused function argument: y

(ARG001)


115-115: Unused function argument: x

(ARG001)


115-115: Unused function argument: y

(ARG001)


118-118: Unused function argument: x

(ARG001)


118-118: Unused function argument: y

(ARG001)


121-121: Unused function argument: x

(ARG001)


121-121: Unused function argument: y

(ARG001)


185-185: f-string without any placeholders

Remove extraneous f prefix

(F541)


215-215: f-string without any placeholders

Remove extraneous f prefix

(F541)


230-230: f-string without any placeholders

Remove extraneous f prefix

(F541)


278-278: Test for membership should be not in

Convert to not in

(E713)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Analyze (cpp)
  • GitHub Check: build (ubuntu-latest, clang)
  • GitHub Check: build (ubuntu-latest, gcc)
  • GitHub Check: build (macos-latest)
  • GitHub Check: build (macos-14)
🔇 Additional comments (36)
pyphare/pyphare/core/gridlayout.py (1)

12-121: Consistent addition of "value": "primal" entries in yee_centering

The new "value": "primal" entries for all directions are consistent and integrate cleanly with yee_centering_lower and yee_element_is_primal, giving a straightforward way to treat a generic "value" quantity as primal without impacting existing logic in this file. Looks good as-is.

pyphare/pyphare/simulator/simulator.py (1)

84-88: Defaulting print_one_line to False looks consistent; just confirm expected UX/logging impact

Docstring and implementation now agree that single‑line progress printing is opt‑in, with print_eol staying "\n" by default and print_eol explicitly overriding print_one_line when provided. Behavior is coherent; the only concern is that this is a silent default change that may alter CLI/log formatting for existing callers relying on the previous default. Worth double‑checking that this shift (more verbose, line‑by‑line output) is intended for all current usages.

Also applies to: 100-103

src/amr/messengers/field_sum_transaction.hpp (1)

1-4: Header guard and include path refactor look good.

The header guard follows the full directory path convention per project standards. The include path simplification from core/utilities/logger/logger_defaults.hpp to core/logger.hpp is cleaner and maintains consistency with the logging macros used throughout the file (PHARE_LOG_SCOPE at lines 100, 121, 131, 141, 148, 155, 164, 178, 185, 225, 234).

Please verify that core/logger.hpp exports all the logging macros and utilities previously available from core/utilities/logger/logger_defaults.hpp to ensure no compilation or runtime issues arise.

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

211-219: LGTM!

The addition of the "name" field to intra-domain overlap dictionaries is consistent with the broader pattern in this function. The ref_pdname variable is correctly in scope from the enclosing loop.


221-230: LGTM!

The append helper function is correctly updated to accept and include the name parameter, maintaining consistency with the intra-domain overlap dictionary structure.


275-292: LGTM!

Both calls to append correctly pass ref_pdname as the first argument, ensuring border-induced overlaps have the same "name" field as intra-domain overlaps.

src/core/utilities/mpi_utils.hpp (1)

162-165: Excellent defensive programming for zero-size segments.

Explicitly checking for zero-size segments before constructing the vector from a pointer range prevents potential undefined behavior when offset could equal rcvBuff.size(). This is safer and clearer than relying on identical begin/end pointers.

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

287-288: LGTM! Safe guard prevents KeyError.

The conditional check ensures that only known diagnostics are mapped, preventing potential KeyError when processing diagnostic files not listed in quantities_per_file.

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

294-309: LGTM!

The test class follows the established SimulatorTest pattern with proper tearDown cleanup and MPI initialization. The test_run method correctly runs the simulation and validates diagnostics.

src/core/data/tensorfield/tensorfield.hpp (1)

208-211: LGTM!

Formatting-only change adding visual separation before the private section.

src/core/utilities/types.hpp (1)

540-545: LGTM!

The SetMax functor follows the established pattern of Equals and PlusEquals above, and is correctly used in FieldData::max() and TensorFieldData::max() methods.

src/amr/level_initializer/hybrid_level_initializer.hpp (1)

113-116: LGTM!

The fillIonBorders call is correctly placed after computing charge density and bulk velocity, synchronizing the ion borders via max-refiners before proceeding to J/E computations. This maintains physical consistency at patch boundaries.

src/amr/physical_models/hybrid_model.hpp (1)

182-186: LGTM - Max border fields registration for ions.

The addition of massDensity(), chargeDensity(), and velocity() to the max-border field collections aligns with the new max-border synchronization infrastructure. Note that chargeDensity() appears in both sumBorderFields (line 180, for populations) and maxBorderFields (line 184, for aggregate ions) - please verify this dual registration is intentional for the different border operations.

src/amr/messengers/hybrid_messenger_strategy.hpp (1)

137-139: LGTM - Consistent interface extension.

The new pure virtual fillIonBorders method follows the established pattern of fillFluxBorders and fillDensityBorders, maintaining interface consistency.

src/amr/solvers/solver_ppc.hpp (1)

583-584: LGTM - Ion border fill placement after ion update.

The call to fillIonBorders is correctly positioned after ionUpdater_.updateIons(), ensuring that the max-border synchronization operates on fully updated ion data.

src/amr/messengers/hybrid_messenger.hpp (1)

359-363: LGTM - Consistent delegation pattern.

The fillIonBorders method correctly delegates to the strategy, following the established pattern of fillFluxBorders and fillDensityBorders.

src/amr/messengers/mhd_hybrid_messenger_strategy.hpp (1)

126-131: LGTM - Appropriate stub implementation.

The empty override for fillIonBorders is consistent with other no-op fill methods in MHDHybridMessengerStrategy, which is expected since MHD-Hybrid transitions don't require ion-specific border handling.

src/amr/messengers/hybrid_messenger_info.hpp (1)

69-72: LGTM - Clean data structure extension.

The new maxBorderFields and maxBorderVecFields members are appropriately placed and follow the naming convention established by sumBorderFields. These will carry the field names for max-border operations.

src/amr/messengers/refiner.hpp (2)

25-26: LGTM - Enum extension for max-border types.

The new PatchFieldBorderMax and PatchVecFieldBorderMax enum values follow the established naming convention, mirroring the existing *BorderSum variants.


204-208: Good addition of fallback error handling.

The else branch with runtime_error provides a safety net for unhandled RefinerType values, which improves maintainability as new types are added.

src/amr/data/tensorfield/tensor_field_data.hpp (2)

343-345: LGTM!

The new max and unpackStreamAndMax declarations are consistent with the existing sum and unpackStreamAndSum pattern in this class.


514-536: LGTM!

The unpackStreamAndMax and max implementations correctly mirror the existing sum-path patterns, using SetMaxOp instead of PlusEqualOp. The implementation properly:

  • Uses core::SetMax<value_type> as the operator
  • Delegates to the templated unpackStream and copy_ methods
  • Includes dimension equality assertion via TBOX_ASSERT_OBJDIM_EQUALITY2
  • Performs proper dynamic casts for overlap and source data

This aligns well with the analogous implementation in src/amr/data/field/field_data.hpp.

src/amr/messengers/hybrid_hybrid_messenger_strategy.hpp (5)

7-7: LGTM!

The include for core/utilities/types.hpp is necessary to access the SetMax utility used by the new max-border operations.


250-253: LGTM!

The registration of ionFluxBorderMaxRefiners_ and ionDensityBorderMaxRefiners_ follows the established pattern used for the sum refiners above.


456-456: LGTM!

Good defensive assertion to ensure the refiner count is consistent with the ion population count.


879-894: LGTM!

The registration of max-border refiners mirrors the existing sum-border refiner pattern. The assertions at lines 881 and 888 ensure configuration consistency with the hardcoded checks in fillIonBorders.


1045-1046: LGTM!

The new type aliases and member vectors for max-border refiners follow the established naming and declaration patterns used by the sum-border refiners.

Also applies to: 1055-1056

src/amr/data/field/field_data.hpp (3)

101-101: LGTM!

The const-correctness improvements on BoxOverlap and PatchData parameters are appropriate and align these method signatures with immutability expectations.

Also applies to: 157-158, 175-175, 196-196, 208-208, 246-246, 253-253


315-319: LGTM!

The new max and unpackStreamAndMax declarations are consistent with the existing sum and unpackStreamAndSum method pair.


465-486: LGTM!

The unpackStreamAndMax and max implementations correctly follow the established sum-path pattern:

  • Uses core::SetMax<value_type> as the operator
  • Delegates to the templated unpackStream and copy_ methods
  • Includes proper dimension equality assertion
  • Performs correct dynamic casts for overlap and source data

This implementation will be called by FieldBorderMaxTransaction::unpackStream and copyLocalData as shown in the relevant code snippets.

src/amr/messengers/field_max_transaction.hpp (6)

20-81: LGTM!

The class declaration follows the established pattern from FieldBorderSumTransaction. Constructor validation is thorough with appropriate assertions, and member variables are correctly initialized.


84-123: LGTM!

The message size computation methods correctly delegate to the underlying patch data's getDataStreamSize and canEstimateStreamSizeFromBox methods, consistent with the sum transaction pattern.


139-159: LGTM!

The packStream delegates to the source patch data correctly. The unpackStream properly casts to FieldData_t and invokes unpackStreamAndMax, which applies the max operation during unpacking.


169-186: LGTM!

The copyLocalData implementation correctly retrieves both source and destination patch data, casts them to FieldData_t, and applies the max operation over the overlap region.


189-225: LGTM!

The FieldBorderMaxTransactionFactory correctly implements the SAMRAI RefineTransactionFactory interface. The allocate method performs appropriate validation and constructs FieldBorderMaxTransaction instances. The preprocessScratchSpace no-op is consistent with the sum transaction factory.


162-186: printClassData throws unconditionally - verify if this is intentional.

The method throws std::runtime_error rather than providing debug output. Confirm whether this matches the implementation pattern in similar transaction classes and whether providing actual debug output is needed for this method.

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 (3)
pyphare/pyphare/pharesee/hierarchy/patchdata.py (1)

194-201: Avoid full dataset read in zeros_like; use metadata shape/dtype instead

The current zeros_like implementation does a full self.dataset[:] read just to obtain the shape:

copy.dataset = np.zeros(deepcopy(self.dataset[:].shape))

For large, HDF5‑backed fields this is a major and unnecessary I/O + memory hit. You only need shape (and ideally dtype), which are available from the dataset metadata.

A more efficient version that keeps the intended semantics would be:

def zeros_like(self):
    from copy import deepcopy

    copy = deepcopy(self)
    assert id(copy.dataset) == id(self.dataset)

    shape = getattr(self.dataset, "shape", None)
    dtype = getattr(self.dataset, "dtype", None)
    copy.dataset = np.zeros(shape, dtype=dtype)

    assert id(copy.dataset) != id(self.dataset)
    return copy

This preserves the geometry and creates a fresh zeroed array without ever reading the underlying data.

Based on learnings, this repeats a previously flagged performance issue around loading self.dataset[:] just for its shape.

pyphare/pyphare/pharesee/hierarchy/hierarchy_utils.py (1)

615-670: Cyclic import and minor inefficiencies already flagged.

The issues in this function were previously identified:

  1. Line 617: Cyclic import with pyphare.pharesee.geometry (CodeQL warning)
  2. Line 620: Use next(iter(diff_hier.time_hier.keys())) instead of list slice
  3. Line 658: if np.any(diff): is simpler than if len(np.nonzero(diff)[0]):
  4. Lines 667-668: Redundant has_non_zero() check after per-patch assertions
pyphare/pyphare/pharesee/hierarchy/hierarchy.py (1)

648-662: Bug: max() returns incorrect results for negative-only data.

Initializing val with zeros means if all values in a level are negative, the reported max will incorrectly be 0. This was previously flagged and the suggested fix initializes with None or -np.inf.

🧹 Nitpick comments (9)
pyphare/pyphare/core/box.py (1)

183-204: Box‑based selection and DataSelector wrapper look fine; minor lint/style nits

select(data, box) correctly turns an inclusive [lower, upper] Box into NumPy slices, and DataSelector gives a convenient DataSelector(data)[box] / [...] = val API without extra copies. This is a clean, minimal addition.

If you want to keep Ruff happy and slightly improve readability:

  • Rename the loop variable in select to avoid l (e.g. lo or lower_i) and,
  • Since Box.__init__ already enforces lower.shape == upper.shape, you can add strict=True to zip if your supported Python version allows it:
def select(data, box):
    return data[tuple(
        slice(lo, hi + 1) for lo, hi in zip(box.lower, box.upper, strict=True)
    )]

None of this changes behavior; it just quiets linters and clarifies intent.

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

31-315: Consider turning printed diffs into quantitative assertions

The diff() / check_diags() flow currently just prints max B/ρ/E differences and always passes unless an exception is raised. That’s useful for exploratory runs, but as a regression/functional test it won’t catch degradations in the Harris equilibrium.

If the goal is to enforce near‑equilibrium numerically, consider adding simple checks like:

  • assert differ.max() < tol_B for B,
  • assert differ.max() < tol_rho for ion charge/mass densities,
  • assert differ.max() < tol_E for E,

with explicit tolerances. That would turn this into a genuine guardrail instead of a smoke test that only fails on I/O errors.


168-260: Minor test‑side cleanups (optional): f‑strings and membership test style

A few low‑impact polish points, mostly to satisfy Ruff and keep the test lean:

  • In plot_file_for_qty, you can simplify the nested formatting:

    def plot_file_for_qty(plot_dir, qty, time, extra=""):
        return f"{plot_dir}/harris_t{time:.10f}_{qty}_{extra}.png"
  • Remove redundant f prefixes where there’s no interpolation, e.g.:

    filename=plot_file_for_qty(plot_dir, "ranks", new_time, f"L{ilvl}")
    # similarly for "ionCharge", "ionMass"
  • Prefer if "Unable to synchronously open object" not in err: over if not "..." in err: in post_advance.

These are cosmetic only; they won’t affect behavior but will quiet the listed lint warnings.

src/core/data/field/field_box.hpp (1)

61-83: max_of_fields is correct; consider reusing operate_on_fields and adding <algorithm>

The new max_of_fields helper does the right thing—element‑wise dst = max(dst, src) over matching lcl_box ranges with an assertion on equal sizes. That fits well with the rest of the field‑box utilities.

Two small, optional tweaks:

  • To avoid duplicating the iteration logic, you could implement this in terms of operate_on_fields and the existing core::SetMax functor:

    template<typename FieldBoxT>
    void max_of_fields(FieldBoxT& dst, FieldBoxT const& src)
    {
        operate_on_fields<core::SetMax<typename FieldBoxT::value_type>>(dst, src);
    }

    (or similar, depending on how you expose value_type).

  • Since you’re using std::max, explicitly including <algorithm> here would make the dependency local instead of relying on transitive includes.

Both are non‑functional cleanups; the current implementation is already correct.

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

617-631: Minor: Use iterator instead of list slice.

The list(...)[0] pattern creates unnecessary temporary lists. Also, ilvl is unused in the loop.

-        hier = (list(copy.time_hier.values()))[0]
+        hier = next(iter(copy.time_hier.values()))

-        for ilvl, lvl in hier.items():
+        for lvl in hier.values():

633-646: Minor: Simplify has_non_zero implementation.

Similar inefficiencies: unused loop variables and np.any() would be cleaner.

-        hier = (list(self.time_hier.values()))[0]
+        hier = next(iter(self.time_hier.values()))

-        for ilvl, lvl in hier.items():
+        for lvl in hier.values():
             for patch in lvl:
-                for key, pd in patch.patch_datas.items():
-                    check = np.nonzero(pd.dataset[:])
-                    if len(check[0]):
+                for pd in patch.patch_datas.values():
+                    if np.any(pd.dataset[:]):
                         return True
src/amr/messengers/hybrid_hybrid_messenger_strategy.hpp (2)

250-253: Max-border refiner wiring looks consistent; consider tightening invariants

The new max-border machinery is wired coherently end‑to‑end:

  • Type aliases and member vectors (FieldGhostMaxRefinerPool, VecFieldGhostMaxRefinerPool, ionDensityBorderMaxRefiners_, ionFluxBorderMaxRefiners_) mirror the existing sum-based pools.
  • registerInitComms populates the pools from info->maxBorderFields (2 scalars) and info->maxBorderVecFields (1 vector), with appropriate ghost overlap fill patterns.
  • registerLevel forwards level registration to all max refiners, in line with the sum refiners.
  • fillIonBorders simply invokes fill on all max refiners, which is exactly what you want for a max reduction (no scratch-copy round trip needed as with sumVec_ / sumField_).

The hard-coded expectations in fillIonBorders:

assert(ionFluxBorderMaxRefiners_.size() == 1);
assert(ionDensityBorderMaxRefiners_.size() == 2);

are correct for the current Harris use case but bake in magic constants. If you expect this to evolve (e.g., more max-tracked fields), consider:

  • Asserting against info-side sizes (or a named constexpr count) rather than raw literals, or
  • Storing the expected counts once when building the pools and asserting against those, so the invariants remain obvious at the single source of truth.

Functionally this is fine; the suggestion is purely to make future refactors safer and more self‑documenting.

Also applies to: 490-502, 881-894, 1045-1047, 1055-1057


452-459: New density-border sanity check is helpful; optional stronger assertion

The added assertion

assert(popDensityBorderSumRefiners_.size() % ions.size() == 0);

usefully encodes the implicit assumption that the number of density-border refiners is an integer multiple of the number of ion populations.

Since the loop below always uses i * fieldsPerPop and i * fieldsPerPop + 1, you still implicitly require at least two fields per population. If there is any chance info->sumBorderFields changes shape later, you might optionally tighten this to:

assert(popDensityBorderSumRefiners_.size() == 2 * ions.size());

or at least fieldsPerPop >= 2, to fail fast if the mapping between fields and populations ever drifts.

src/amr/messengers/field_max_transaction.hpp (1)

1-52: Interface and guard are sound; consider override and defaulted dtor

The transaction class and factory are structured consistently with the existing sum variant, and the header guard follows the PHARE path-based naming convention; no functional issues here.

For cleanliness and future-proofing, you might:

  • Mark all overridden virtuals with override (and drop redundant virtual), including the destructor:
~FieldBorderMaxTransaction() override = default;
bool canEstimateIncomingMessageSize() override;
size_t computeIncomingMessageSize() override;
...
void printClassData(std::ostream& stream) const override;
  • Optionally replace the /** ... TODO */ class doc with a brief sentence explaining that this is the max-border analogue of FieldBorderSumTransaction to avoid “TODO” surviving long-term.

These are minor, non-blocking polish items.

Also applies to: 71-81, 190-225

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0f7bb60 and 9f6c674.

📒 Files selected for processing (26)
  • pyphare/pyphare/core/box.py (1 hunks)
  • pyphare/pyphare/core/gridlayout.py (3 hunks)
  • pyphare/pyphare/pharesee/geometry.py (3 hunks)
  • pyphare/pyphare/pharesee/hierarchy/hierarchy.py (1 hunks)
  • pyphare/pyphare/pharesee/hierarchy/hierarchy_utils.py (1 hunks)
  • pyphare/pyphare/pharesee/hierarchy/patchdata.py (1 hunks)
  • pyphare/pyphare/pharesee/run/run.py (1 hunks)
  • pyphare/pyphare/simulator/simulator.py (2 hunks)
  • src/amr/data/field/field_data.hpp (8 hunks)
  • src/amr/data/tensorfield/tensor_field_data.hpp (2 hunks)
  • src/amr/level_initializer/hybrid_level_initializer.hpp (1 hunks)
  • src/amr/messengers/field_max_transaction.hpp (1 hunks)
  • src/amr/messengers/field_sum_transaction.hpp (1 hunks)
  • src/amr/messengers/hybrid_hybrid_messenger_strategy.hpp (7 hunks)
  • src/amr/messengers/hybrid_messenger.hpp (1 hunks)
  • src/amr/messengers/hybrid_messenger_info.hpp (1 hunks)
  • src/amr/messengers/hybrid_messenger_strategy.hpp (1 hunks)
  • src/amr/messengers/mhd_hybrid_messenger_strategy.hpp (1 hunks)
  • src/amr/messengers/refiner.hpp (6 hunks)
  • src/amr/physical_models/hybrid_model.hpp (1 hunks)
  • src/amr/solvers/solver_ppc.hpp (1 hunks)
  • src/core/data/field/field_box.hpp (1 hunks)
  • src/core/data/tensorfield/tensorfield.hpp (1 hunks)
  • src/core/utilities/mpi_utils.hpp (1 hunks)
  • src/core/utilities/types.hpp (1 hunks)
  • tests/functional/harris/harris_2d_diff.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (11)
  • src/amr/physical_models/hybrid_model.hpp
  • src/core/data/tensorfield/tensorfield.hpp
  • src/amr/messengers/mhd_hybrid_messenger_strategy.hpp
  • src/amr/messengers/hybrid_messenger_info.hpp
  • src/amr/level_initializer/hybrid_level_initializer.hpp
  • src/core/utilities/types.hpp
  • src/amr/messengers/hybrid_messenger_strategy.hpp
  • src/amr/messengers/hybrid_messenger.hpp
  • pyphare/pyphare/simulator/simulator.py
  • pyphare/pyphare/core/gridlayout.py
  • src/amr/data/field/field_data.hpp
🧰 Additional context used
📓 Path-based instructions (1)
**/*.hpp

⚙️ CodeRabbit configuration file

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

Files:

  • src/amr/data/tensorfield/tensor_field_data.hpp
  • src/core/utilities/mpi_utils.hpp
  • src/amr/solvers/solver_ppc.hpp
  • src/amr/messengers/refiner.hpp
  • src/amr/messengers/field_sum_transaction.hpp
  • src/amr/messengers/hybrid_hybrid_messenger_strategy.hpp
  • src/amr/messengers/field_max_transaction.hpp
  • src/core/data/field/field_box.hpp
🧠 Learnings (4)
📓 Common learnings
Learnt from: PhilipDeegan
Repo: PHAREHUB/PHARE PR: 784
File: tests/simulator/test_restarts.py:333-339
Timestamp: 2024-10-09T08:32:15.667Z
Learning: PhilipDeegan has indicated a preference for minimal and efficient code, even after making changes to get a function working. There may be opportunities to remove or optimize parts of the code such as sleep/wait/advances.
Learnt from: PhilipDeegan
Repo: PHAREHUB/PHARE PR: 784
File: tests/simulator/test_restarts.py:333-339
Timestamp: 2024-07-26T22:04:34.160Z
Learning: PhilipDeegan has indicated a preference for minimal and efficient code, even after making changes to get a function working. There may be opportunities to remove or optimize parts of the code such as sleep/wait/advances.
Learnt from: PhilipDeegan
Repo: PHAREHUB/PHARE PR: 1068
File: src/amr/data/field/coarsening/electric_field_coarsener.hpp:1-2
Timestamp: 2025-09-17T13:35:11.533Z
Learning: PhilipDeegan prefers header guard names that include the full directory path structure, following the pattern PHARE_[PATH_WITH_UNDERSCORES]_HPP. For example, a file at src/amr/data/field/coarsening/electric_field_coarsener.hpp should use PHARE_AMR_DATA_FIELD_COARSENING_ELECTRIC_FIELD_COARSENER_HPP as the header guard.
Learnt from: PhilipDeegan
Repo: PHAREHUB/PHARE PR: 888
File: src/hdf5/detail/h5/h5_file.hpp:78-79
Timestamp: 2024-10-09T08:32:15.667Z
Learning: Avoid commenting on draft PRs for the user PhilipDeegan.
Learnt from: PhilipDeegan
Repo: PHAREHUB/PHARE PR: 888
File: src/hdf5/detail/h5/h5_file.hpp:78-79
Timestamp: 2024-09-05T17:02:58.784Z
Learning: Avoid commenting on draft PRs for the user PhilipDeegan.
📚 Learning: 2025-11-10T09:37:57.021Z
Learnt from: PhilipDeegan
Repo: PHAREHUB/PHARE PR: 1084
File: src/diagnostic/diagnostic_model_view.hpp:219-221
Timestamp: 2025-11-10T09:37:57.021Z
Learning: In PHARE, temporary fields like tmpField_, tmpVec_, and tmpTensor_ in src/diagnostic/diagnostic_model_view.hpp use a name-based resource allocation pattern. Fields with names "PHARE_sumField", "PHARE_sumVec", and "PHARE_sumTensor" are allocated globally elsewhere in the resources manager (e.g., in src/amr/messengers/hybrid_hybrid_messenger_strategy.hpp), and components reference the same storage through identically-named field objects. These do not require local allocation via rm.enumerate().

Applied to files:

  • src/amr/messengers/refiner.hpp
  • src/amr/messengers/hybrid_hybrid_messenger_strategy.hpp
  • src/amr/messengers/field_max_transaction.hpp
📚 Learning: 2024-10-18T13:23:32.074Z
Learnt from: PhilipDeegan
Repo: PHAREHUB/PHARE PR: 910
File: pyphare/pyphare/pharesee/hierarchy/hierarchy_utils.py:7-7
Timestamp: 2024-10-18T13:23:32.074Z
Learning: In the `pyphare.pharesee.hierarchy` module, importing `PatchHierarchy` and `format_timestamp` from `hierarchy.py` into `hierarchy_utils.py` is acceptable as long as `hierarchy.py` does not import `hierarchy_utils.py`, thereby avoiding a cyclic import.

Applied to files:

  • pyphare/pyphare/pharesee/hierarchy/hierarchy.py
  • pyphare/pyphare/pharesee/hierarchy/hierarchy_utils.py
📚 Learning: 2024-10-22T10:05:54.014Z
Learnt from: PhilipDeegan
Repo: PHAREHUB/PHARE PR: 0
File: :0-0
Timestamp: 2024-10-22T10:05:54.014Z
Learning: The `__eq__` method in `ParticleData` uses `self.dataset == that.dataset` for dataset comparison, which might not be sufficient for numpy arrays as it returns an array of booleans. The use of `np.array_equal` is suggested for a more robust comparison.

Applied to files:

  • pyphare/pyphare/pharesee/hierarchy/patchdata.py
🧬 Code graph analysis (7)
src/amr/data/tensorfield/tensor_field_data.hpp (2)
src/amr/data/field/field_data.hpp (22)
  • src (311-311)
  • src (316-316)
  • overlap (196-199)
  • overlap (196-196)
  • overlap (408-425)
  • overlap (408-408)
  • stream (207-238)
  • stream (207-208)
  • stream (245-249)
  • stream (245-246)
  • stream (312-313)
  • stream (317-318)
  • unpackStreamAndMax (466-472)
  • unpackStreamAndMax (466-467)
  • unpackStream (252-280)
  • unpackStream (252-253)
  • max (475-486)
  • max (475-476)
  • copy_ (335-351)
  • copy_ (335-337)
  • copy_ (360-402)
  • copy_ (360-360)
src/amr/messengers/field_max_transaction.hpp (5)
  • stream (63-63)
  • stream (65-65)
  • stream (69-69)
  • unpackStream (149-159)
  • unpackStream (149-149)
pyphare/pyphare/pharesee/hierarchy/hierarchy.py (2)
pyphare/pyphare/pharesee/hierarchy/patchdata.py (1)
  • zeros_like (194-201)
pyphare/pyphare/pharesee/hierarchy/patch.py (1)
  • copy (43-47)
pyphare/pyphare/pharesee/hierarchy/patchdata.py (2)
pyphare/pyphare/pharesee/hierarchy/hierarchy.py (1)
  • zeros_like (617-631)
pyphare/pyphare/pharesee/hierarchy/patch.py (1)
  • copy (43-47)
tests/functional/harris/harris_2d_diff.py (4)
pyphare/pyphare/pharesee/hierarchy/hierarchy_utils.py (1)
  • diff_hierarchy (615-670)
tests/simulator/__init__.py (1)
  • SimulatorTest (200-266)
tests/simulator/test_advance.py (1)
  • AdvanceTestBase (33-869)
pyphare/pyphare/pharein/__init__.py (1)
  • NO_GUI (60-64)
src/amr/messengers/field_max_transaction.hpp (1)
src/amr/messengers/field_sum_transaction.hpp (25)
  • dst_level (206-228)
  • dst_level (207-212)
  • stream (76-76)
  • stream (78-78)
  • stream (82-82)
  • canEstimateIncomingMessageSize (98-115)
  • canEstimateIncomingMessageSize (98-98)
  • getSourceProcessor (139-143)
  • getSourceProcessor (139-139)
  • computeIncomingMessageSize (119-126)
  • computeIncomingMessageSize (119-119)
  • computeOutgoingMessageSize (129-136)
  • computeOutgoingMessageSize (129-129)
  • getDestinationProcessor (146-150)
  • getDestinationProcessor (146-146)
  • packStream (153-159)
  • packStream (153-153)
  • unpackStream (162-172)
  • unpackStream (162-162)
  • printClassData (176-180)
  • printClassData (176-176)
  • copyLocalData (183-199)
  • copyLocalData (183-183)
  • level (230-237)
  • level (231-232)
src/core/data/field/field_box.hpp (2)
src/amr/data/field/field_data.hpp (4)
  • src (311-311)
  • src (316-316)
  • max (475-486)
  • max (475-476)
src/amr/data/tensorfield/tensor_field_data.hpp (4)
  • src (339-339)
  • src (343-343)
  • max (525-536)
  • max (525-526)
pyphare/pyphare/pharesee/hierarchy/hierarchy_utils.py (3)
pyphare/pyphare/pharesee/geometry.py (1)
  • hierarchy_overlaps (297-307)
pyphare/pyphare/pharesee/hierarchy/hierarchy.py (2)
  • zeros_like (617-631)
  • has_non_zero (633-646)
pyphare/pyphare/core/box.py (2)
  • amr_to_local (182-183)
  • shift (107-108)
🪛 GitHub Check: CodeQL
pyphare/pyphare/pharesee/hierarchy/hierarchy_utils.py

[notice] 617-617: Cyclic import
Import of module pyphare.pharesee.geometry begins an import cycle.

🪛 Ruff (0.14.7)
pyphare/pyphare/core/box.py

187-187: Ambiguous variable name: l

(E741)


187-187: zip() without an explicit strict= parameter

Add explicit value for parameter strict=

(B905)

pyphare/pyphare/pharesee/hierarchy/hierarchy.py

624-624: Prefer next(iter(copy.time_hier.values())) over single element slice

Replace with next(iter(copy.time_hier.values()))

(RUF015)


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

Rename unused ilvl to _ilvl

(B007)


638-638: Prefer next(iter(self.time_hier.values())) over single element slice

Replace with next(iter(self.time_hier.values()))

(RUF015)


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

Rename unused ilvl to _ilvl

(B007)


642-642: Loop control variable key not used within loop body

Rename unused key to _key

(B007)


653-653: Prefer next(iter(self.time_hier.values())) over single element slice

Replace with next(iter(self.time_hier.values()))

(RUF015)

tests/functional/harris/harris_2d_diff.py

1-1: Shebang is present but file is not executable

(EXE001)


60-60: Unused function argument: x

(ARG001)


68-68: Ambiguous variable name: l

(E741)


103-103: Unused function argument: x

(ARG001)


103-103: Unused function argument: y

(ARG001)


115-115: Unused function argument: x

(ARG001)


115-115: Unused function argument: y

(ARG001)


118-118: Unused function argument: x

(ARG001)


118-118: Unused function argument: y

(ARG001)


121-121: Unused function argument: x

(ARG001)


121-121: Unused function argument: y

(ARG001)


185-185: f-string without any placeholders

Remove extraneous f prefix

(F541)


218-218: f-string without any placeholders

Remove extraneous f prefix

(F541)


233-233: f-string without any placeholders

Remove extraneous f prefix

(F541)


284-284: Test for membership should be not in

Convert to not in

(E713)

pyphare/pyphare/pharesee/hierarchy/hierarchy_utils.py

620-620: Prefer next(iter(diff_hier.time_hier.keys())) over single element slice

Replace with next(iter(diff_hier.time_hier.keys()))

(RUF015)


626-626: Avoid specifying long messages outside the exception class

(TRY003)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: build (ubuntu-latest, gcc)
  • GitHub Check: build (ubuntu-latest, clang)
  • GitHub Check: Analyze (python)
  • GitHub Check: Analyze (cpp)
  • GitHub Check: build (macos-latest)
  • GitHub Check: build (macos-14)
🔇 Additional comments (16)
pyphare/pyphare/pharesee/run/run.py (1)

275-289: Guarding against unknown diagnostic files looks correct

Wrapping the assignment in if basename in quantities_per_file prevents KeyError when extra .h5 files are present while preserving behavior for known diagnostics. This is a sensible robustness improvement; just ensure quantities_per_file stays in sync with any new “core” diags you care about exposing via all_times().

If you want to verify coverage, you can quickly inspect which basenames appear in the current output directory and compare them to quantities_per_file.

src/amr/messengers/field_sum_transaction.hpp (1)

4-4: Logger include switch is consistent and non‑disruptive

Using "core/logger.hpp" here aligns this transaction with the central logging header used elsewhere. Given the existing PHARE_LOG_SCOPE calls, this is the right dependency and should not affect behavior.

src/amr/solvers/solver_ppc.hpp (1)

547-589: Ion border fill ordering looks reasonable; confirm intended semantics

Calling fillIonBorders after ionUpdater_.updateIons(state.ions) fits the comment that Ni/Vi must be computed before filling pure ghost nodes and mirrors the pattern used for other ghost/border fills. Given the existing sequence:

  • updatePopulations
  • setTime(ions)
  • fill flux/density/moment/ghost-particle borders
  • updateIons
  • now fillIonBorders

this should give border code the fully-updated ion state at newTime. Please just confirm that fillIonBorders is indeed defined in terms of the post‑updateIons quantities and doesn’t need to run earlier in the sequence.

src/core/utilities/mpi_utils.hpp (1)

145-169: Zero‑length segment handling in collectVector correctly avoids UB

Branching on perMPISize[i] == 0 and emplacing an empty Vector avoids constructing from &rcvBuff[offset] when the global receive buffer is empty (e.g., all ranks have size 0). For non‑zero entries the behavior is unchanged. This is a solid fix for the all‑zero case without impacting existing usage.

src/amr/messengers/refiner.hpp (3)

25-26: LGTM: New RefinerType enum values for max-based border synchronization.

The new PatchFieldBorderMax and PatchVecFieldBorderMax enum values follow the established pattern for sum-based refiners.


204-208: Good defensive programming: fallback error for unhandled RefinerType.

The final else branch ensures that if a new RefinerType is added but not handled in registerLevel, a clear runtime error is thrown rather than silently producing undefined behavior.


115-137: Unable to verify review comment due to repository access constraints.

The review comment suggests adding PatchTensorFieldBorderMax for consistency with existing tensor field border operations. However, verification cannot be completed because the repository cannot be cloned in the sandbox environment. The claim that PatchTensorFieldBorderSum exists at lines 95-103 and that PatchTensorFieldBorderMax is missing requires direct inspection of the source file to confirm. Additionally, determining whether this represents a genuine design gap or an incorrect assumption requires understanding the actual usage patterns of tensor fields in the codebase and the RefinerType enum definition.

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

211-219: LGTM: Adding "name" field to overlap dictionaries.

The addition of "name": ref_pdname to overlap entries enables consumers (like diff_hierarchy) to identify which patch data quantity the overlap corresponds to without needing to inspect the pdatas directly.


221-230: LGTM: Updated append helper signature.

The name parameter addition to the local append helper is consistent with the intra-domain overlap changes and maintains the same dictionary structure for all overlap entries.


275-292: LGTM: Consistent usage of the updated append function.

Both calls to append now correctly pass ref_pdname as the first argument, ensuring all border-induced overlaps include the "name" field.

src/amr/data/tensorfield/tensor_field_data.hpp (2)

343-345: LGTM: Method declarations follow established patterns.

The new max and unpackStreamAndMax declarations mirror the existing sum and unpackStreamAndSum declarations, maintaining API consistency.


514-536: SetMax implementation and consistency with sum operations requires codebase verification.

The code snippet shows clean, correct syntax with proper type aliasing and method structure. However, without access to the PHARE repository, I cannot verify:

  • The SetMax definition in core utilities
  • Consistency with SetSum and sum operation counterparts
  • Pattern alignment with field_data.hpp

The original approval is reasonable based on the snippet's quality, but the specific verification claims remain unconfirmed.

src/amr/messengers/hybrid_hybrid_messenger_strategy.hpp (1)

4-9: Include of core/utilities/types.hpp is appropriate

Bringing in core/utilities/types.hpp here is reasonable given the use of PHARE type utilities (e.g., tensor helpers) in this strategy; no issues from a cleanliness or dependency perspective.

src/amr/messengers/field_max_transaction.hpp (3)

85-123: Message-size estimation and streaming mirror the sum transaction correctly

canEstimateIncomingMessageSize, computeIncomingMessageSize, computeOutgoingMessageSize, getSourceProcessor, getDestinationProcessor, and packStream all follow the same pattern as the sum-border transaction:

  • They use the appropriate patch level (d_src_level vs d_dst_level) and d_refine_data[d_item_id]->d_src / d_scratch indices.
  • They operate over *d_overlap as expected for border exchanges.
  • getSourceProcessor / getDestinationProcessor delegate to the owning ranks of the source/destination boxes, which is the right thing for SAMRAI’s transaction routing.

Given that the implementation intentionally clones the existing sum-transaction logic, there are no obvious correctness or performance issues here.

Also applies to: 126-147


149-186: Max reduction semantics depend on FieldData_t API but look coherent

The core max logic:

onode_dst_data->unpackStreamAndMax(stream, *d_overlap);
...
onode_dst_data->max(*onode_src_data, *d_overlap);

is the natural analogue of the sum version and will do the right thing as long as FieldData_t implements:

  • unpackStreamAndMax(MessageStream&, BoxOverlap const&), and
  • max(FieldData_t const&, BoxOverlap const&).

This keeps the transaction layer thin and lets the field type own the element-wise max semantics, which matches the existing design for sums. printClassData throwing is also consistent with the sum transaction’s “should not be called” stance.

Overall the max reduction path is conceptually correct; just ensure the FieldData_t specializations used with this header provide those two methods.


217-224: Factory noop preprocessScratchSpace is fine and consistent

The factory’s preprocessScratchSpace implementation is intentionally a no-op with logging, mirroring the sum-border factory. That keeps behavior predictable and avoids unexpected scratch mutations before the max transactions run; no changes needed here.

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 (5)
pyphare/pyphare/pharein/simulation.py (1)

408-409: Clarify ghost-overlap comment and reuse existing array for default smallest_patch_size

The new default smallest_patch_size based on 2 * max_ghosts + 1 makes sense for avoiding ghost overlap, but the comment is a bit misleading (“smallest_patch_size * 2 + 1”), and you recompute the same array you already stored in small_invalid_patch_size.

You can clarify intent and avoid the duplicate np_array_ify call with:

-    # to prevent primal ghost overlaps of non adjacent patches, we need smallest_patch_size * 2 + 1
-    smallest_patch_size = phare_utilities.np_array_ify(max_ghosts, ndim) * 2 + 1
+    # default smallest_patch_size to 2 * max_ghosts + 1 to prevent primal ghost overlaps of non adjacent patches
+    smallest_patch_size = small_invalid_patch_size * 2 + 1

Note that the validation below still only enforces smallest_patch_size > small_invalid_patch_size (i.e. > max_ghosts), so user-specified values can be smaller than 2 * max_ghosts + 1. If the “no overlap” condition is meant to hold even for custom smallest_patch_size, you may want to tighten that check accordingly; otherwise the current behavior is fine as an expert override.

tools/diff_diags.py (4)

1-1: Align shebang with intended usage (EXE001)

Ruff flags the shebang because the file isn’t executable. If this is meant to be run as python tools/diff_diags.py, consider dropping the shebang; if it’s intended as a CLI script, make the file executable in the repo.


25-26: Simplify time formatting in plot_file_for_qty

You can avoid the nested .format call and make the f-string clearer:

-def plot_file_for_qty(plot_dir, qty, time, extra=""):
-    return f"{plot_dir}/harris_t{"{:.10f}".format(time)}_{qty}_{extra}.png"
+def plot_file_for_qty(plot_dir, qty, time, extra=""):
+    return f"{plot_dir}/harris_t{time:.10f}_{qty}_{extra}.png"

This is a bit more direct while keeping the same behavior. Based on learnings, this keeps the helper minimal and efficient.


42-42: Remove unnecessary f prefixes on constant strings (F541)

Ruff’s F541 warnings come from f-strings without placeholders. These can be plain strings:

-            filename=plot_file_for_qty(plot_dir, f"ranks", new_time, f"L{ilvl}"),
+            filename=plot_file_for_qty(plot_dir, "ranks", new_time, f"L{ilvl}"),
...
-                filename=plot_file_for_qty(
-                    plot_dir, f"ionCharge", new_time, f"L{ilvl}"
-                ),
+                filename=plot_file_for_qty(
+                    plot_dir, "ionCharge", new_time, f"L{ilvl}"
+                ),
...
-                filename=plot_file_for_qty(plot_dir, f"ionMass", new_time, f"L{ilvl}"),
+                filename=plot_file_for_qty(plot_dir, "ionMass", new_time, f"L{ilvl}"),

This should clear the F541 diagnostics without changing behavior.

Also applies to: 96-96, 111-111


141-145: Use not in for membership test (E713)

Ruff suggests the canonical not in form here:

-            if not "Unable to synchronously open object" in err:  # no diag for time
+            if "Unable to synchronously open object" not in err:  # no diag for time

Same logic, slightly clearer and matches the linter expectation.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9f6c674 and 56d0efd.

📒 Files selected for processing (2)
  • pyphare/pyphare/pharein/simulation.py (1 hunks)
  • tools/diff_diags.py (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: PhilipDeegan
Repo: PHAREHUB/PHARE PR: 784
File: tests/simulator/test_restarts.py:333-339
Timestamp: 2024-10-09T08:32:15.667Z
Learning: PhilipDeegan has indicated a preference for minimal and efficient code, even after making changes to get a function working. There may be opportunities to remove or optimize parts of the code such as sleep/wait/advances.
Learnt from: PhilipDeegan
Repo: PHAREHUB/PHARE PR: 784
File: tests/simulator/test_restarts.py:333-339
Timestamp: 2024-07-26T22:04:34.160Z
Learning: PhilipDeegan has indicated a preference for minimal and efficient code, even after making changes to get a function working. There may be opportunities to remove or optimize parts of the code such as sleep/wait/advances.
Learnt from: PhilipDeegan
Repo: PHAREHUB/PHARE PR: 1068
File: src/amr/data/field/coarsening/electric_field_coarsener.hpp:1-2
Timestamp: 2025-09-17T13:35:11.533Z
Learning: PhilipDeegan prefers header guard names that include the full directory path structure, following the pattern PHARE_[PATH_WITH_UNDERSCORES]_HPP. For example, a file at src/amr/data/field/coarsening/electric_field_coarsener.hpp should use PHARE_AMR_DATA_FIELD_COARSENING_ELECTRIC_FIELD_COARSENER_HPP as the header guard.
Learnt from: PhilipDeegan
Repo: PHAREHUB/PHARE PR: 888
File: src/hdf5/detail/h5/h5_file.hpp:78-79
Timestamp: 2024-10-09T08:32:15.667Z
Learning: Avoid commenting on draft PRs for the user PhilipDeegan.
Learnt from: PhilipDeegan
Repo: PHAREHUB/PHARE PR: 888
File: src/hdf5/detail/h5/h5_file.hpp:78-79
Timestamp: 2024-09-05T17:02:58.784Z
Learning: Avoid commenting on draft PRs for the user PhilipDeegan.
🧬 Code graph analysis (2)
pyphare/pyphare/pharein/simulation.py (2)
pyphare/pyphare/core/phare_utilities.py (1)
  • np_array_ify (62-67)
pyphare/pyphare/core/gridlayout.py (1)
  • ndim (224-225)
tools/diff_diags.py (7)
pyphare/pyphare/cpp/__init__.py (1)
  • cpp_lib (6-9)
pyphare/pyphare/pharesee/run/run.py (7)
  • Run (35-293)
  • GetRanks (179-188)
  • GetE (102-110)
  • GetB (92-100)
  • GetNi (116-118)
  • GetMassDensity (112-114)
  • all_times (275-290)
pyphare/pyphare/pharesee/hierarchy/hierarchy_utils.py (1)
  • diff_hierarchy (615-670)
tests/simulator/test_advance.py (2)
  • AdvanceTestBase (33-869)
  • base_test_overlaped_fields_are_equal (217-387)
pyphare/pyphare/pharein/__init__.py (1)
  • NO_GUI (60-64)
pyphare/pyphare/pharesee/hierarchy/hierarchy.py (4)
  • levelNbr (222-225)
  • plot_patches (342-355)
  • levels (214-217)
  • has_non_zero (633-646)
pyphare/pyphare/pharesee/hierarchy/__init__.py (1)
  • hierarchy_from (13-46)
🪛 Ruff (0.14.7)
tools/diff_diags.py

1-1: Shebang is present but file is not executable

(EXE001)


42-42: f-string without any placeholders

Remove extraneous f prefix

(F541)


96-96: f-string without any placeholders

Remove extraneous f prefix

(F541)


111-111: f-string without any placeholders

Remove extraneous f prefix

(F541)


144-144: Test for membership should be not in

Convert to not in

(E713)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: build (ubuntu-latest, gcc)
  • GitHub Check: Analyze (cpp)
  • GitHub Check: build (ubuntu-latest, clang)
  • GitHub Check: build (macos-latest)
  • GitHub Check: build (macos-14)
🔇 Additional comments (1)
tools/diff_diags.py (1)

153-162: Diagnostic loop and MPI startup look good

The check_diags() + __main__ wiring (using Run(diag_dir).all_times()["B"] and startMPI() once at entry) is straightforward and matches the surrounding test/Run abstractions; no functional issues spotted.

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 (6)
pyphare/pyphare/pharesee/hierarchy/hierarchy.py (1)

648-662: CRITICAL: Fix max() to handle negative data.

Line 654 initializes val with zeros, causing max() to incorrectly report 0 when all values on a level are negative. Given that this PR involves signed fields (magnetic field B, electric field E per the PR description), this is a correctness bug that will produce invalid results for negative-only data.

Example: If all data is [-5, -3, -1], the current code reports 0 instead of the correct maximum -1.

Apply this diff to fix the initialization and tighten the implementation:

     def max(self, qty=None):
         """only works for fields and vecfields with 1 time"""
 
         assert len(self.time_hier) == 1
 
-        hier = (list(self.time_hier.values()))[0]
-        val = [0 for ilvl, lvl in hier.items()]
+        hier = next(iter(self.time_hier.values()))
+        max_level = max(hier.keys())
+        val = [None] * (max_level + 1)
 
         for ilvl, lvl in hier.items():
             for patch in lvl:
-                for key, pd in patch.patch_datas.items():
-                    if qty is None or key == qty:
-                        val[ilvl] = max(np.max(pd.dataset), val[ilvl])
+                for key, pd in patch.patch_datas.items():
+                    if qty is None or key == qty:
+                        level_max = np.max(pd.dataset[:])
+                        if val[ilvl] is None or level_max > val[ilvl]:
+                            val[ilvl] = level_max
 
         return val

This preserves the public API (a list indexed by level number) while correctly handling negative-only data.

pyphare/pyphare/pharesee/hierarchy/hierarchy_utils.py (4)

617-617: Cyclic import risk persists.

This import has been flagged by CodeQL as creating a module cycle. The previous review already identified this issue.


620-620: Use iterator instead of list conversion.

Replace list(diff_hier.time_hier.keys())[0] with next(iter(diff_hier.time_hier)) to avoid creating an intermediate list.


658-658: Simplify non-zero check.

if np.any(diff): is cleaner and dimension-agnostic compared to if len(np.nonzero(diff)[0]):.


667-668: Expensive re-scan for final assertion.

The diff_hier.has_non_zero() call re-scans all datasets even though per-patch assertions already verified non-zeros. Consider removing or guarding behind a debug flag.

pyphare/pyphare/pharesee/hierarchy/patchdata.py (1)

194-201: Inefficient full dataset read for shape metadata.

This issue was already flagged in a previous review. Using self.dataset[:].shape forces a full HDF5 read just to obtain the shape. Use self.dataset.shape and self.dataset.dtype directly from metadata instead:

 def zeros_like(self):
     from copy import deepcopy

     copy = deepcopy(self)
     assert id(copy.dataset) == id(self.dataset)
-    copy.dataset = np.zeros(deepcopy(self.dataset[:].shape))
+    copy.dataset = np.zeros(self.dataset.shape, dtype=getattr(self.dataset, "dtype", None))
     assert id(copy.dataset) != id(self.dataset)
     return copy
🧹 Nitpick comments (17)
pyphare/pyphare/pharesee/geometry.py (1)

211-230: Overlap dict structure updated; docstring now out of date

The addition of "name": ref_pdname and the append(name, ...) helper keeps the runtime behavior coherent and makes overlaps easier to label, but the compute_overlaps docstring above still documents only three keys (pdatas, box, offset). The actual structure now includes at least "name" and "patches" as well.

Consider updating the docstring to list all keys so downstream users don’t rely on stale docs.

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

617-631: Consider minor optimizations for zeros_like().

The logic is correct, but a few optional refinements can improve readability and efficiency:

  • Line 624: Use next(iter(copy.time_hier.values())) instead of list(...)[0] for better performance.
  • Line 626: The loop variable ilvl is unused; iterate over hier.values() directly.

Apply this diff:

     def zeros_like(self):
         """only works for fields and vecfields with 1 time"""
         from copy import deepcopy
 
         assert len(self.time_hier) == 1
         copy = deepcopy(self)
 
-        hier = (list(copy.time_hier.values()))[0]
+        hier = next(iter(copy.time_hier.values()))
 
-        for ilvl, lvl in hier.items():
+        for lvl in hier.values():
             for patch in lvl:
                 for key, pd in patch.patch_datas.items():
                     patch.patch_datas[key] = pd.zeros_like()
         assert not copy.has_non_zero()
         return copy

633-646: Simplify has_non_zero() with np.any() for efficiency.

The current implementation using np.nonzero() and length checks can be streamlined with np.any(), which short-circuits on the first non-zero value and is more direct.

Apply this diff:

     def has_non_zero(self):
         """only works for fields and vecfields with 1 time"""
 
         assert len(self.time_hier) == 1
 
-        hier = (list(self.time_hier.values()))[0]
+        hier = next(iter(self.time_hier.values()))
 
-        for ilvl, lvl in hier.items():
+        for lvl in hier.values():
             for patch in lvl:
-                for key, pd in patch.patch_datas.items():
-                    check = np.nonzero(pd.dataset[:])
-                    if len(check[0]):
+                for pd in patch.patch_datas.values():
+                    if np.any(pd.dataset[:]):
                         return True
         return False
pyphare/pyphare/core/box.py (1)

186-187: Rename ambiguous variable l to lo or lower.

The variable name l can be confused with 1 or I. A clearer name improves readability.

 def select(data, box):
-    return data[tuple([slice(l, u + 1) for l, u in zip(box.lower, box.upper)])]
+    return data[tuple([slice(lo, hi + 1) for lo, hi in zip(box.lower, box.upper)])]
pyphare/pyphare/pharesee/hierarchy/hierarchy_utils.py (2)

655-661: Consider consistent variable naming for clarity.

dif0/dif1 (current values) vs diff (computed difference) is slightly confusing. Consider renaming to curr_diff0/curr_diff1 or similar to clarify these are the existing values being compared against the newly computed diff.

-            dif0 = boxm.select(diff_data0, box_pd1)
-            dif1 = boxm.select(diff_data1, box_pd2)
+            existing0 = boxm.select(diff_data0, box_pd1)
+            existing1 = boxm.select(diff_data1, box_pd2)

             if len(np.nonzero(diff)[0]):
                 
-                boxm.DataSelector(diff_data0)[box_pd1] = np.maximum(dif0, diff)
-                boxm.DataSelector(diff_data1)[box_pd2] = np.maximum(dif1, diff)
+                boxm.DataSelector(diff_data0)[box_pd1] = np.maximum(existing0, diff)
+                boxm.DataSelector(diff_data1)[box_pd2] = np.maximum(existing1, diff)

628-628: Use boolean instead of int for flag.

found = False and found = True would be more idiomatic for a boolean flag.

-    found = 0
+    found = False
     for ilvl, overlaps in hierarchy_overlaps(hier, time).items():
         ...
-                found = 1
+                found = True
src/core/data/field/field_box.hpp (1)

71-82: Ensure <algorithm> is included for std::max; optional alignment with generic ops

The elementwise max logic and iterator pattern look good and are consistent with operate_on_fields. Two points:

  1. To keep this header self‑contained, it should not rely on transitive includes for std::max. If core/def.hpp or other headers stop including <algorithm>, this will break. I’d recommend adding the include here:
 #include "core/utilities/box/box.hpp"
 
 #include <vector>
 #include <cstddef>
 #include <type_traits>
+ #include <algorithm> // for std::max
  1. Given the broader PR adds generic operator machinery (e.g., operate_on_fields<Operator> and a SetMax-like operation), you might consider expressing this helper via that path to avoid a special‑case implementation, if it doesn’t complicate call sites. If you feel that extra indirection hurts readability/minimalism, the current direct implementation is fine.
src/amr/messengers/refiner.hpp (1)

122-132: Minor comment clarity suggestion.

The comment "schedule used to == max" reads a bit awkwardly. Consider rephrasing to "schedule used to compute max" or "schedule used to set max values" for clarity.

-            // schedule used to == max of density and flux for populations
+            // schedule used to set max of density and flux for populations
             // on complete overlaped ghost box nodes
src/amr/messengers/hybrid_hybrid_messenger_strategy.hpp (1)

490-502: Hard-coded size assertions may be fragile.

The assertions ionFluxBorderMaxRefiners_.size() == 1 and ionDensityBorderMaxRefiners_.size() == 2 couple this implementation to a specific configuration. If the number of max-border fields changes, these assertions will fail without clear indication of why.

Consider either:

  1. Removing the assertions if they're purely for debugging, or
  2. Adding a clarifying comment explaining why these specific sizes are expected, or
  3. Making the logic work with any valid size
         void fillIonBorders(IonsT& ions, level_t& level, double const fillTime) override
         {
-            //
-            assert(ionFluxBorderMaxRefiners_.size() == 1);
-            assert(ionDensityBorderMaxRefiners_.size() == 2);
+            // Expects 1 flux refiner (ions.velocity()) and 2 density refiners (charge/mass density)
+            assert(ionFluxBorderMaxRefiners_.size() == 1 && "Expected 1 ion flux max refiner");
+            assert(ionDensityBorderMaxRefiners_.size() == 2 && "Expected 2 ion density max refiners");
 
             for (auto& refiner : ionFluxBorderMaxRefiners_)
                 refiner.fill(level.getLevelNumber(), fillTime);
             for (auto& refiner : ionDensityBorderMaxRefiners_)
                 refiner.fill(level.getLevelNumber(), fillTime);
-
-            //
         }
tools/diff_diags.py (7)

42-42: Remove extraneous f prefix from string literal.

The string f"ranks" has no placeholders, so the f prefix is unnecessary.

-            filename=plot_file_for_qty(plot_dir, f"ranks", new_time, f"L{ilvl}"),
+            filename=plot_file_for_qty(plot_dir, "ranks", new_time, f"L{ilvl}"),

96-96: Remove extraneous f prefix from string literal.

Same issue as above - f"ionCharge" has no placeholders.

-                    plot_dir, f"ionCharge", new_time, f"L{ilvl}"
+                    plot_dir, "ionCharge", new_time, f"L{ilvl}"

111-111: Remove extraneous f prefix from string literal.

Same issue - f"ionMass" has no placeholders.

-                filename=plot_file_for_qty(plot_dir, f"ionMass", new_time, f"L{ilvl}"),
+                filename=plot_file_for_qty(plot_dir, "ionMass", new_time, f"L{ilvl}"),

144-144: Use not in for membership test.

The idiomatic Python pattern is not in rather than not ... in.

-            if not "Unable to synchronously open object" in err:  # no diag for time
+            if "Unable to synchronously open object" not in err:  # no diag for time

129-129: Module-level test instantiation may cause import-time side effects.

Creating AdvanceTestBase(rethrow=True) at module import time could trigger unintended side effects. Consider moving this inside check_time or check_diags if the test object doesn't need to persist across calls.

-test = AdvanceTestBase(rethrow=True)  # change to False for debugging images
-
 def check_time(new_time):
+    test = AdvanceTestBase(rethrow=True)  # change to False for debugging images
     if cpp.mpi_rank() == 0:

139-148: Error handling silently swallows some exceptions.

The KeyError handler only logs when the error message doesn't contain a specific string, meaning some legitimate errors might be silently ignored. Consider being more explicit about which errors are expected.


1-1: Shebang present but file is not executable.

If this script is intended to be run directly (as indicated by the if __name__ == "__main__": block), consider making it executable with chmod +x.

src/amr/messengers/field_operate_transaction.hpp (1)

164-170: printClassData always throws - consider implementing or documenting.

This method unconditionally throws a runtime error. If it's intentionally not implemented, consider either:

  1. Adding a // TODO comment explaining the intent
  2. Providing a minimal implementation that prints class info

The documentation block at line 17 already says "TODO" which is helpful.

 template<typename FieldData_t, typename Operation>
 void FieldBorderOpTransaction<FieldData_t, Operation>::printClassData(std::ostream& stream) const
 {
     PHARE_LOG_SCOPE(2, "FieldBorderOpTransaction::printClassData");
-
-    throw std::runtime_error("FieldBorderOpTransaction::printClassData!");
+    // TODO: Implement proper class data printing
+    stream << "FieldBorderOpTransaction: not fully implemented\n";
 }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3411cc1 and e90cb51.

📒 Files selected for processing (28)
  • pyphare/pyphare/core/box.py (1 hunks)
  • pyphare/pyphare/core/gridlayout.py (3 hunks)
  • pyphare/pyphare/pharein/simulation.py (1 hunks)
  • pyphare/pyphare/pharesee/geometry.py (3 hunks)
  • pyphare/pyphare/pharesee/hierarchy/hierarchy.py (1 hunks)
  • pyphare/pyphare/pharesee/hierarchy/hierarchy_utils.py (1 hunks)
  • pyphare/pyphare/pharesee/hierarchy/patchdata.py (1 hunks)
  • pyphare/pyphare/pharesee/run/run.py (1 hunks)
  • pyphare/pyphare/simulator/simulator.py (2 hunks)
  • src/amr/data/field/field_data.hpp (9 hunks)
  • src/amr/data/tensorfield/tensor_field_data.hpp (3 hunks)
  • src/amr/level_initializer/hybrid_level_initializer.hpp (1 hunks)
  • src/amr/messengers/field_operate_transaction.hpp (7 hunks)
  • src/amr/messengers/hybrid_hybrid_messenger_strategy.hpp (7 hunks)
  • src/amr/messengers/hybrid_messenger.hpp (1 hunks)
  • src/amr/messengers/hybrid_messenger_info.hpp (1 hunks)
  • src/amr/messengers/hybrid_messenger_strategy.hpp (1 hunks)
  • src/amr/messengers/mhd_hybrid_messenger_strategy.hpp (1 hunks)
  • src/amr/messengers/refiner.hpp (7 hunks)
  • src/amr/physical_models/hybrid_model.hpp (1 hunks)
  • src/amr/solvers/solver_ppc.hpp (1 hunks)
  • src/core/data/field/field_box.hpp (1 hunks)
  • src/core/data/tensorfield/tensorfield.hpp (1 hunks)
  • src/core/utilities/mpi_utils.hpp (1 hunks)
  • src/core/utilities/types.hpp (1 hunks)
  • src/diagnostic/diagnostic_model_view.hpp (2 hunks)
  • tests/simulator/__init__.py (1 hunks)
  • tools/diff_diags.py (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/core/data/tensorfield/tensorfield.hpp
🚧 Files skipped from review as they are similar to previous changes (9)
  • src/core/utilities/types.hpp
  • src/core/utilities/mpi_utils.hpp
  • src/amr/messengers/hybrid_messenger_info.hpp
  • src/amr/messengers/hybrid_messenger_strategy.hpp
  • tests/simulator/init.py
  • src/amr/physical_models/hybrid_model.hpp
  • pyphare/pyphare/pharein/simulation.py
  • src/amr/messengers/mhd_hybrid_messenger_strategy.hpp
  • pyphare/pyphare/pharesee/run/run.py
🧰 Additional context used
📓 Path-based instructions (1)
**/*.hpp

⚙️ CodeRabbit configuration file

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

Files:

  • src/amr/level_initializer/hybrid_level_initializer.hpp
  • src/amr/messengers/hybrid_messenger.hpp
  • src/core/data/field/field_box.hpp
  • src/amr/solvers/solver_ppc.hpp
  • src/amr/messengers/hybrid_hybrid_messenger_strategy.hpp
  • src/amr/messengers/refiner.hpp
  • src/diagnostic/diagnostic_model_view.hpp
  • src/amr/messengers/field_operate_transaction.hpp
  • src/amr/data/field/field_data.hpp
  • src/amr/data/tensorfield/tensor_field_data.hpp
🧠 Learnings (4)
📓 Common learnings
Learnt from: PhilipDeegan
Repo: PHAREHUB/PHARE PR: 784
File: tests/simulator/test_restarts.py:333-339
Timestamp: 2024-10-09T08:32:15.667Z
Learning: PhilipDeegan has indicated a preference for minimal and efficient code, even after making changes to get a function working. There may be opportunities to remove or optimize parts of the code such as sleep/wait/advances.
Learnt from: PhilipDeegan
Repo: PHAREHUB/PHARE PR: 784
File: tests/simulator/test_restarts.py:333-339
Timestamp: 2024-07-26T22:04:34.160Z
Learning: PhilipDeegan has indicated a preference for minimal and efficient code, even after making changes to get a function working. There may be opportunities to remove or optimize parts of the code such as sleep/wait/advances.
Learnt from: PhilipDeegan
Repo: PHAREHUB/PHARE PR: 1068
File: src/amr/data/field/coarsening/electric_field_coarsener.hpp:1-2
Timestamp: 2025-09-17T13:35:11.533Z
Learning: PhilipDeegan prefers header guard names that include the full directory path structure, following the pattern PHARE_[PATH_WITH_UNDERSCORES]_HPP. For example, a file at src/amr/data/field/coarsening/electric_field_coarsener.hpp should use PHARE_AMR_DATA_FIELD_COARSENING_ELECTRIC_FIELD_COARSENER_HPP as the header guard.
Learnt from: PhilipDeegan
Repo: PHAREHUB/PHARE PR: 888
File: src/hdf5/detail/h5/h5_file.hpp:78-79
Timestamp: 2024-10-09T08:32:15.667Z
Learning: Avoid commenting on draft PRs for the user PhilipDeegan.
Learnt from: PhilipDeegan
Repo: PHAREHUB/PHARE PR: 888
File: src/hdf5/detail/h5/h5_file.hpp:78-79
Timestamp: 2024-09-05T17:02:58.784Z
Learning: Avoid commenting on draft PRs for the user PhilipDeegan.
📚 Learning: 2024-10-18T13:23:32.074Z
Learnt from: PhilipDeegan
Repo: PHAREHUB/PHARE PR: 910
File: pyphare/pyphare/pharesee/hierarchy/hierarchy_utils.py:7-7
Timestamp: 2024-10-18T13:23:32.074Z
Learning: In the `pyphare.pharesee.hierarchy` module, importing `PatchHierarchy` and `format_timestamp` from `hierarchy.py` into `hierarchy_utils.py` is acceptable as long as `hierarchy.py` does not import `hierarchy_utils.py`, thereby avoiding a cyclic import.

Applied to files:

  • pyphare/pyphare/pharesee/hierarchy/hierarchy_utils.py
  • pyphare/pyphare/pharesee/hierarchy/hierarchy.py
📚 Learning: 2025-11-10T09:37:57.021Z
Learnt from: PhilipDeegan
Repo: PHAREHUB/PHARE PR: 1084
File: src/diagnostic/diagnostic_model_view.hpp:219-221
Timestamp: 2025-11-10T09:37:57.021Z
Learning: In PHARE, temporary fields like tmpField_, tmpVec_, and tmpTensor_ in src/diagnostic/diagnostic_model_view.hpp use a name-based resource allocation pattern. Fields with names "PHARE_sumField", "PHARE_sumVec", and "PHARE_sumTensor" are allocated globally elsewhere in the resources manager (e.g., in src/amr/messengers/hybrid_hybrid_messenger_strategy.hpp), and components reference the same storage through identically-named field objects. These do not require local allocation via rm.enumerate().

Applied to files:

  • src/amr/messengers/hybrid_hybrid_messenger_strategy.hpp
  • src/amr/messengers/refiner.hpp
  • src/diagnostic/diagnostic_model_view.hpp
  • src/amr/messengers/field_operate_transaction.hpp
📚 Learning: 2024-10-22T10:05:54.014Z
Learnt from: PhilipDeegan
Repo: PHAREHUB/PHARE PR: 0
File: :0-0
Timestamp: 2024-10-22T10:05:54.014Z
Learning: The `__eq__` method in `ParticleData` uses `self.dataset == that.dataset` for dataset comparison, which might not be sufficient for numpy arrays as it returns an array of booleans. The use of `np.array_equal` is suggested for a more robust comparison.

Applied to files:

  • pyphare/pyphare/pharesee/hierarchy/patchdata.py
🧬 Code graph analysis (8)
src/amr/level_initializer/hybrid_level_initializer.hpp (1)
src/amr/messengers/hybrid_hybrid_messenger_strategy.hpp (16)
  • ions (412-421)
  • ions (412-412)
  • ions (425-450)
  • ions (425-425)
  • ions (452-487)
  • ions (452-452)
  • ions (490-502)
  • ions (490-490)
  • ions (513-552)
  • ions (513-514)
  • level (587-611)
  • level (587-589)
  • level (717-728)
  • level (717-717)
  • level (914-929)
  • level (914-914)
src/amr/messengers/hybrid_messenger.hpp (2)
src/amr/messengers/hybrid_hybrid_messenger_strategy.hpp (16)
  • ions (412-421)
  • ions (412-412)
  • ions (425-450)
  • ions (425-425)
  • ions (452-487)
  • ions (452-452)
  • ions (490-502)
  • ions (490-490)
  • ions (513-552)
  • ions (513-514)
  • level (587-611)
  • level (587-589)
  • level (717-728)
  • level (717-717)
  • level (914-929)
  • level (914-914)
src/amr/messengers/hybrid_messenger_strategy.hpp (6)
  • ions (83-84)
  • ions (88-89)
  • ions (131-132)
  • ions (134-135)
  • ions (137-138)
  • level (121-121)
pyphare/pyphare/core/box.py (3)
pyphare/pyphare/pharesee/hierarchy/patchdata.py (2)
  • select (94-116)
  • select (233-234)
pyphare/pyphare/pharesee/particles.py (1)
  • select (102-135)
src/core/utilities/box/box.hpp (1)
  • upper (96-96)
pyphare/pyphare/pharesee/hierarchy/patchdata.py (2)
pyphare/pyphare/pharesee/hierarchy/hierarchy.py (1)
  • zeros_like (617-631)
pyphare/pyphare/pharesee/hierarchy/patch.py (1)
  • copy (43-47)
src/amr/data/field/field_data.hpp (1)
src/amr/data/tensorfield/tensor_field_data.hpp (26)
  • source (114-157)
  • source (114-114)
  • source (180-189)
  • source (180-180)
  • source (382-385)
  • source (382-382)
  • overlap (218-221)
  • overlap (218-218)
  • overlap (454-482)
  • overlap (454-454)
  • unpackStream (284-318)
  • unpackStream (284-285)
  • stream (229-269)
  • stream (229-230)
  • stream (277-281)
  • stream (277-278)
  • unpackStreamAnd (346-347)
  • unpackStreamAnd (491-495)
  • unpackStreamAnd (491-492)
  • operate (344-344)
  • operate (500-509)
  • operate (500-501)
  • copy_ (363-379)
  • copy_ (363-365)
  • copy_ (388-450)
  • copy_ (388-389)
src/amr/data/tensorfield/tensor_field_data.hpp (1)
src/amr/data/field/field_data.hpp (11)
  • overlap (200-203)
  • overlap (200-200)
  • overlap (411-428)
  • overlap (411-411)
  • operate (316-316)
  • operate (452-461)
  • operate (452-453)
  • copy_ (338-354)
  • copy_ (338-340)
  • copy_ (363-405)
  • copy_ (363-363)
tools/diff_diags.py (6)
pyphare/pyphare/cpp/__init__.py (1)
  • cpp_lib (6-9)
pyphare/pyphare/pharesee/run/run.py (8)
  • Run (35-293)
  • GetRanks (179-188)
  • filename (191-192)
  • GetE (102-110)
  • GetB (92-100)
  • GetNi (116-118)
  • GetMassDensity (112-114)
  • times (292-293)
pyphare/pyphare/pharesee/hierarchy/hierarchy_utils.py (1)
  • diff_hierarchy (615-670)
pyphare/pyphare/pharein/__init__.py (1)
  • NO_GUI (60-64)
pyphare/pyphare/pharesee/hierarchy/__init__.py (1)
  • hierarchy_from (13-46)
pyphare/pyphare/pharein/simulation.py (1)
  • check_time (142-187)
pyphare/pyphare/pharesee/hierarchy/hierarchy.py (1)
pyphare/pyphare/pharesee/hierarchy/patchdata.py (1)
  • zeros_like (194-201)
🪛 Ruff (0.14.8)
pyphare/pyphare/pharesee/hierarchy/hierarchy_utils.py

620-620: Prefer next(iter(diff_hier.time_hier.keys())) over single element slice

Replace with next(iter(diff_hier.time_hier.keys()))

(RUF015)


626-626: Avoid specifying long messages outside the exception class

(TRY003)

pyphare/pyphare/core/box.py

187-187: Ambiguous variable name: l

(E741)


187-187: zip() without an explicit strict= parameter

Add explicit value for parameter strict=

(B905)

tools/diff_diags.py

1-1: Shebang is present but file is not executable

(EXE001)


42-42: f-string without any placeholders

Remove extraneous f prefix

(F541)


96-96: f-string without any placeholders

Remove extraneous f prefix

(F541)


111-111: f-string without any placeholders

Remove extraneous f prefix

(F541)


144-144: Test for membership should be not in

Convert to not in

(E713)

pyphare/pyphare/pharesee/hierarchy/hierarchy.py

624-624: Prefer next(iter(copy.time_hier.values())) over single element slice

Replace with next(iter(copy.time_hier.values()))

(RUF015)


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

Rename unused ilvl to _ilvl

(B007)


638-638: Prefer next(iter(self.time_hier.values())) over single element slice

Replace with next(iter(self.time_hier.values()))

(RUF015)


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

Rename unused ilvl to _ilvl

(B007)


642-642: Loop control variable key not used within loop body

Rename unused key to _key

(B007)


653-653: Prefer next(iter(self.time_hier.values())) over single element slice

Replace with next(iter(self.time_hier.values()))

(RUF015)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Analyze (python)
  • GitHub Check: Analyze (cpp)
  • GitHub Check: build (ubuntu-latest, gcc)
  • GitHub Check: build (ubuntu-latest, clang)
  • GitHub Check: build (macos-latest)
  • GitHub Check: build (macos-14)
🔇 Additional comments (27)
pyphare/pyphare/simulator/simulator.py (2)

80-88: Docstring now matches runtime default for print_one_line

The updated description (“default False”) correctly reflects the new behavior and clarifies that single-line, in-place progress now has to be explicitly enabled. Looks consistent with the implementation below.


99-104: Behavior change: default progress output now line-based instead of in-place

By switching kwargs.get("print_one_line", False) to default False, run() will emit one line per advance unless callers explicitly set print_one_line=True (or provide a custom print_eol). This looks intentional and the override order (print_eol kwarg winning over print_one_line) is sensible; just be aware this may increase log verbosity compared to previous defaults.

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

275-292: Consistent naming for border overlaps looks good

Using append(ref_pdname, ...) for both overlap and other_ovrlp ensures border-derived overlaps carry the same "name" labeling as intra-domain entries, without changing existing offset semantics. This is a clean, minimal extension.

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

189-203: LGTM - clever pattern for enabling Box-based slice assignment.

The DataSelector class provides a clean API for assigning values to numpy array slices via Box indexing. The [:] assignment in __setitem__ correctly modifies the underlying array view.

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

46-48: "value": "primal" entries look consistent; verify no unintended iteration effects

Adding "value": "primal" for x/y/z keeps yee_centering structurally consistent with existing metadata patterns like "tags". However, confirm that any code iterating over yee_centering[direction] keys does not inadvertently treat "value" as a physical field rather than metadata.

Also applies to: 82-84, 118-120

src/amr/level_initializer/hybrid_level_initializer.hpp (1)

116-116: LGTM!

The new fillIonBorders call is correctly placed after ion moments are computed and at level scope (outside the patch loop), consistent with the existing fillFluxBorders and fillDensityBorders pattern.

src/diagnostic/diagnostic_model_view.hpp (2)

9-9: LGTM!

Header include updated to match the renamed transaction infrastructure.


173-180: LGTM!

The refactoring from FieldBorderSumTransactionFactory to the generic FieldBorderOpTransactionFactory<TensorFieldData_t, PlusEqualsOp> maintains equivalent behavior while aligning with the operation-generalization pattern introduced across the codebase.

src/amr/solvers/solver_ppc.hpp (1)

583-584: LGTM!

The new fillIonBorders call is appropriately placed after updateIons completes, ensuring ion borders are filled with the final computed values. The placement follows the established messenger call pattern in this function.

src/amr/messengers/hybrid_messenger.hpp (1)

359-362: LGTM!

The new fillIonBorders method follows the established delegation pattern and is consistent with the existing fillFluxBorders and fillDensityBorders methods.

src/amr/messengers/refiner.hpp (4)

6-7: LGTM!

Headers updated to include the new operation-based transaction infrastructure and type utilities.


25-26: LGTM!

New RefinerType enum values for max-border operations extend the refiner capabilities appropriately.


37-38: LGTM!

The operation type aliases provide clean parameterization for the transaction factories.


212-216: Good defensive programming.

Adding the final else branch that throws ensures compile-time exhaustiveness checking is supplemented with runtime safety for any unhandled RefinerType values.

src/amr/messengers/hybrid_hybrid_messenger_strategy.hpp (3)

1045-1057: LGTM!

The new type aliases and member containers for max-refiner pools follow the established pattern used for sum-refiner pools. The naming is consistent and the initialization pattern matches.


250-254: LGTM!

The level registration for max-refiners correctly mirrors the pattern used for sum-refiners above, ensuring proper schedule creation at each level.


879-894: Verify assertion consistency between this code and fillIonBorders function.

The assertions at lines 881 and 888 check exact sizes before populating refiner pools. If HybridMessengerInfo's maxBorderFields and maxBorderVecFields sizes are guaranteed invariants, these assertions are reasonable defensive checks. However, confirm that both assertion sites (here and in fillIonBorders) remain synchronized if field counts ever change in future updates.

src/amr/data/field/field_data.hpp (4)

30-36: LGTM!

Good restructuring to clarify public/private access for type aliases. The value_type is correctly public while SetEqualOp is private as an implementation detail.


315-321: LGTM!

The new templated operate<Operation> and unpackStreamAnd<Operation> declarations provide a clean generic interface for different field operations (sum, max, equals). This aligns well with the corresponding changes in TensorFieldData.


443-461: LGTM!

The out-of-class template definitions correctly delegate to the existing copy_<Operation> and unpackStream<Operation> methods, maintaining consistency with the internal implementation while exposing the generic operation interface.


105-105: Good const-correctness improvement.

Changing const SAMRAI::hier::PatchData& to SAMRAI::hier::PatchData const& follows the east-const style consistently throughout the file.

src/amr/messengers/field_operate_transaction.hpp (3)

17-21: LGTM!

Good generalization from the sum-specific FieldBorderSumTransaction to the generic FieldBorderOpTransaction<FieldData_t, Operation>. This allows reuse with different operations (sum, max, equals) through the template parameter.


149-161: LGTM!

The unpackStream method correctly uses the templated unpackStreamAnd<Operation> to apply the generic operation during unpacking.


172-190: LGTM!

The copyLocalData method correctly uses the templated operate<Operation> for local data operations, maintaining consistency with the remote (stream-based) path.

src/amr/data/tensorfield/tensor_field_data.hpp (3)

49-53: LGTM!

The restructuring to expose value_type as public while keeping SetEqualOp private mirrors the changes in field_data.hpp, maintaining consistency between the two field data implementations.


343-347: LGTM!

The new templated method declarations match those in FieldData, ensuring a consistent API across both field data types for the generic operation pattern.


489-509: LGTM!

The out-of-class template definitions correctly implement the generic operation pattern:

  • unpackStreamAnd<Operation> delegates to unpackStream<Operation>
  • operate<Operation> delegates to copy_<Operation>

This is consistent with the corresponding implementation in FieldData.

@PHAREHUB PHAREHUB deleted a comment from coderabbitai bot Dec 9, 2025
levelNbrs : limit working set of hierarchy levels to those requested, if scalar, returns just that level
time : the simulation time to access the appropriate data for the requested time
"""
from .hierarchy.hierarchy_utils import is_root_lvl # avoid cyclic imports

Check notice

Code scanning / CodeQL

Cyclic import Note

Import of module
pyphare.pharesee.hierarchy.hierarchy_utils
begins an import cycle.
* This transaction defines these override to these methods to call specific methods
* of FieldData to perform the += instead of =.
* These methods are copyAndSum and unpackStreamAndSum.
/** * @brief FieldBorderOpTransaction is TODO
Copy link
Member Author

Choose a reason for hiding this comment

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

@nicolasaunai suggest to comment?

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.

1 participant