-
Notifications
You must be signed in to change notification settings - Fork 30
Difference hierarchies / max border schedules #1115
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
📝 WalkthroughWalkthroughAdds 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
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
📜 Recent review detailsConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ 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)
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. Comment |
There was a problem hiding this 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
las ambiguous (could be confused with1). Consider usinglowerandupperfor 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=Truetozip()(Python 3.10+), but sinceBox.__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] = valby 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 reusingoperate_on_fieldswithSetMaxoperator to avoid duplication.This function duplicates the iteration logic from
operate_on_fieldsabove. SinceSetMaxalready exists incore/utilities/types.hppand 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_valsincesrcis const-qualified.tests/functional/harris/harris_2d_diff.py (3)
68-69: Rename ambiguous variablelfor clarity.The variable
lis easily confused with1orI. Consider using a more descriptive name likelengthorscale.- 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 extraneousfprefix 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: Usenot ininstead ofnot ... infor 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 timesrc/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() == 1andionDensityBorderMaxRefiners_.size() == 2are hardcoded magic numbers. If the configuration inregisterInitCommschanges (e.g.,maxBorderFieldsormaxBorderVecFieldssizes change), these assertions will fail without clear context.Consider either:
- Deriving the expected size from a shared constant or configuration
- 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
📒 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.hppsrc/amr/messengers/hybrid_messenger_strategy.hppsrc/amr/level_initializer/hybrid_level_initializer.hppsrc/amr/messengers/mhd_hybrid_messenger_strategy.hppsrc/amr/messengers/hybrid_messenger_info.hppsrc/amr/messengers/hybrid_messenger.hppsrc/core/data/field/field_box.hppsrc/amr/messengers/refiner.hppsrc/amr/physical_models/hybrid_model.hppsrc/core/data/tensorfield/tensorfield.hppsrc/amr/messengers/field_sum_transaction.hppsrc/amr/data/tensorfield/tensor_field_data.hppsrc/amr/solvers/solver_ppc.hppsrc/amr/messengers/hybrid_hybrid_messenger_strategy.hppsrc/core/utilities/mpi_utils.hppsrc/amr/data/field/field_data.hppsrc/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.pypyphare/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.hppsrc/amr/messengers/hybrid_hybrid_messenger_strategy.hppsrc/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 inyee_centeringThe new
"value": "primal"entries for all directions are consistent and integrate cleanly withyee_centering_lowerandyee_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: Defaultingprint_one_lineto False looks consistent; just confirm expected UX/logging impactDocstring and implementation now agree that single‑line progress printing is opt‑in, with
print_eolstaying"\n"by default andprint_eolexplicitly overridingprint_one_linewhen 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.hpptocore/logger.hppis 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.hppexports all the logging macros and utilities previously available fromcore/utilities/logger/logger_defaults.hppto 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. Theref_pdnamevariable is correctly in scope from the enclosing loop.
221-230: LGTM!The
appendhelper function is correctly updated to accept and include thenameparameter, maintaining consistency with the intra-domain overlap dictionary structure.
275-292: LGTM!Both calls to
appendcorrectly passref_pdnameas 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
offsetcould equalrcvBuff.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
SimulatorTestpattern with proper tearDown cleanup and MPI initialization. Thetest_runmethod 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
SetMaxfunctor follows the established pattern ofEqualsandPlusEqualsabove, and is correctly used inFieldData::max()andTensorFieldData::max()methods.src/amr/level_initializer/hybrid_level_initializer.hpp (1)
113-116: LGTM!The
fillIonBorderscall 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(), andvelocity()to the max-border field collections aligns with the new max-border synchronization infrastructure. Note thatchargeDensity()appears in bothsumBorderFields(line 180, for populations) andmaxBorderFields(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
fillIonBordersmethod follows the established pattern offillFluxBordersandfillDensityBorders, maintaining interface consistency.src/amr/solvers/solver_ppc.hpp (1)
583-584: LGTM - Ion border fill placement after ion update.The call to
fillIonBordersis correctly positioned afterionUpdater_.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
fillIonBordersmethod correctly delegates to the strategy, following the established pattern offillFluxBordersandfillDensityBorders.src/amr/messengers/mhd_hybrid_messenger_strategy.hpp (1)
126-131: LGTM - Appropriate stub implementation.The empty override for
fillIonBordersis consistent with other no-op fill methods inMHDHybridMessengerStrategy, 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
maxBorderFieldsandmaxBorderVecFieldsmembers are appropriately placed and follow the naming convention established bysumBorderFields. 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
PatchFieldBorderMaxandPatchVecFieldBorderMaxenum values follow the established naming convention, mirroring the existing*BorderSumvariants.
204-208: Good addition of fallback error handling.The
elsebranch withruntime_errorprovides a safety net for unhandledRefinerTypevalues, which improves maintainability as new types are added.src/amr/data/tensorfield/tensor_field_data.hpp (2)
343-345: LGTM!The new
maxandunpackStreamAndMaxdeclarations are consistent with the existingsumandunpackStreamAndSumpattern in this class.
514-536: LGTM!The
unpackStreamAndMaxandmaximplementations correctly mirror the existing sum-path patterns, usingSetMaxOpinstead ofPlusEqualOp. The implementation properly:
- Uses
core::SetMax<value_type>as the operator- Delegates to the templated
unpackStreamandcopy_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.hppis necessary to access theSetMaxutility used by the new max-border operations.
250-253: LGTM!The registration of
ionFluxBorderMaxRefiners_andionDensityBorderMaxRefiners_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
BoxOverlapandPatchDataparameters 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
maxandunpackStreamAndMaxdeclarations are consistent with the existingsumandunpackStreamAndSummethod pair.
465-486: LGTM!The
unpackStreamAndMaxandmaximplementations correctly follow the established sum-path pattern:
- Uses
core::SetMax<value_type>as the operator- Delegates to the templated
unpackStreamandcopy_methods- Includes proper dimension equality assertion
- Performs correct dynamic casts for overlap and source data
This implementation will be called by
FieldBorderMaxTransaction::unpackStreamandcopyLocalDataas 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
getDataStreamSizeandcanEstimateStreamSizeFromBoxmethods, consistent with the sum transaction pattern.
139-159: LGTM!The
packStreamdelegates to the source patch data correctly. TheunpackStreamproperly casts toFieldData_tand invokesunpackStreamAndMax, which applies the max operation during unpacking.
169-186: LGTM!The
copyLocalDataimplementation correctly retrieves both source and destination patch data, casts them toFieldData_t, and applies themaxoperation over the overlap region.
189-225: LGTM!The
FieldBorderMaxTransactionFactorycorrectly implements the SAMRAIRefineTransactionFactoryinterface. Theallocatemethod performs appropriate validation and constructsFieldBorderMaxTransactioninstances. ThepreprocessScratchSpaceno-op is consistent with the sum transaction factory.
162-186:printClassDatathrows unconditionally - verify if this is intentional.The method throws
std::runtime_errorrather 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.
There was a problem hiding this 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 inzeros_like; use metadata shape/dtype insteadThe current
zeros_likeimplementation does a fullself.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 copyThis 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:
- Line 617: Cyclic import with
pyphare.pharesee.geometry(CodeQL warning)- Line 620: Use
next(iter(diff_hier.time_hier.keys()))instead of list slice- Line 658:
if np.any(diff):is simpler thanif len(np.nonzero(diff)[0]):- Lines 667-668: Redundant
has_non_zero()check after per-patch assertionspyphare/pyphare/pharesee/hierarchy/hierarchy.py (1)
648-662: Bug:max()returns incorrect results for negative-only data.Initializing
valwith zeros means if all values in a level are negative, the reported max will incorrectly be0. This was previously flagged and the suggested fix initializes withNoneor-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, andDataSelectorgives a convenientDataSelector(data)[box]/[...] = valAPI 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
selectto avoidl(e.g.loorlower_i) and,- Since
Box.__init__already enforceslower.shape == upper.shape, you can addstrict=Truetozipif 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 assertionsThe
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_Bfor B,assert differ.max() < tol_rhofor ion charge/mass densities,assert differ.max() < tol_Efor 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 styleA 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
fprefixes 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:overif not "..." in err:inpost_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_fieldsis correct; consider reusingoperate_on_fieldsand adding<algorithm>The new
max_of_fieldshelper does the right thing—element‑wisedst = max(dst, src)over matchinglcl_boxranges 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_fieldsand the existingcore::SetMaxfunctor: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,ilvlis 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 Truesrc/amr/messengers/hybrid_hybrid_messenger_strategy.hpp (2)
250-253: Max-border refiner wiring looks consistent; consider tightening invariantsThe 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.registerInitCommspopulates the pools frominfo->maxBorderFields(2 scalars) andinfo->maxBorderVecFields(1 vector), with appropriate ghost overlap fill patterns.registerLevelforwards level registration to all max refiners, in line with the sum refiners.fillIonBorderssimply invokesfillon all max refiners, which is exactly what you want for a max reduction (no scratch-copy round trip needed as withsumVec_/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 namedconstexprcount) 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 assertionThe 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 * fieldsPerPopandi * fieldsPerPop + 1, you still implicitly require at least two fields per population. If there is any chanceinfo->sumBorderFieldschanges 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; consideroverrideand defaulted dtorThe 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 redundantvirtual), 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 ofFieldBorderSumTransactionto 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
📒 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.hppsrc/core/utilities/mpi_utils.hppsrc/amr/solvers/solver_ppc.hppsrc/amr/messengers/refiner.hppsrc/amr/messengers/field_sum_transaction.hppsrc/amr/messengers/hybrid_hybrid_messenger_strategy.hppsrc/amr/messengers/field_max_transaction.hppsrc/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.hppsrc/amr/messengers/hybrid_hybrid_messenger_strategy.hppsrc/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.pypyphare/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 correctWrapping the assignment in
if basename in quantities_per_filepreventsKeyErrorwhen extra.h5files are present while preserving behavior for known diagnostics. This is a sensible robustness improvement; just ensurequantities_per_filestays in sync with any new “core” diags you care about exposing viaall_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‑disruptiveUsing
"core/logger.hpp"here aligns this transaction with the central logging header used elsewhere. Given the existingPHARE_LOG_SCOPEcalls, 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 semanticsCalling
fillIonBordersafterionUpdater_.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
fillIonBordersthis should give border code the fully-updated ion state at
newTime. Please just confirm thatfillIonBordersis indeed defined in terms of the post‑updateIonsquantities and doesn’t need to run earlier in the sequence.src/core/utilities/mpi_utils.hpp (1)
145-169: Zero‑length segment handling incollectVectorcorrectly avoids UBBranching on
perMPISize[i] == 0and emplacing an emptyVectoravoids 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
PatchFieldBorderMaxandPatchVecFieldBorderMaxenum 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
RefinerTypeis added but not handled inregisterLevel, 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
PatchTensorFieldBorderMaxfor consistency with existing tensor field border operations. However, verification cannot be completed because the repository cannot be cloned in the sandbox environment. The claim thatPatchTensorFieldBorderSumexists at lines 95-103 and thatPatchTensorFieldBorderMaxis 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_pdnameto overlap entries enables consumers (likediff_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
nameparameter addition to the localappendhelper 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
appendnow correctly passref_pdnameas 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
maxandunpackStreamAndMaxdeclarations mirror the existingsumandunpackStreamAndSumdeclarations, 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
SetMaxdefinition in core utilities- Consistency with
SetSumand sum operation counterparts- Pattern alignment with
field_data.hppThe 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 ofcore/utilities/types.hppis appropriateBringing in
core/utilities/types.hpphere 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, andpackStreamall follow the same pattern as the sum-border transaction:
- They use the appropriate patch level (
d_src_levelvsd_dst_level) andd_refine_data[d_item_id]->d_src/d_scratchindices.- They operate over
*d_overlapas expected for border exchanges.getSourceProcessor/getDestinationProcessordelegate 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 onFieldData_tAPI but look coherentThe 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_timplements:
unpackStreamAndMax(MessageStream&, BoxOverlap const&), andmax(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.
printClassDatathrowing 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_tspecializations used with this header provide those two methods.
217-224: Factory nooppreprocessScratchSpaceis fine and consistentThe factory’s
preprocessScratchSpaceimplementation 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.
There was a problem hiding this 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_sizeThe new default
smallest_patch_sizebased on2 * max_ghosts + 1makes 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 insmall_invalid_patch_size.You can clarify intent and avoid the duplicate
np_array_ifycall 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 + 1Note 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 than2 * max_ghosts + 1. If the “no overlap” condition is meant to hold even for customsmallest_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 inplot_file_for_qtyYou can avoid the nested
.formatcall 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 unnecessaryfprefixes 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: Usenot infor membership test (E713)Ruff suggests the canonical
not inform 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 timeSame logic, slightly clearer and matches the linter expectation.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 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 goodThe
check_diags()+__main__wiring (usingRun(diag_dir).all_times()["B"]andstartMPI()once at entry) is straightforward and matches the surrounding test/Run abstractions; no functional issues spotted.
There was a problem hiding this 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: Fixmax()to handle negative data.Line 654 initializes
valwith zeros, causingmax()to incorrectly report0when 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 reports0instead 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 valThis 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]withnext(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 toif 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[:].shapeforces a full HDF5 read just to obtain the shape. Useself.dataset.shapeandself.dataset.dtypedirectly 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 dateThe addition of
"name": ref_pdnameand theappend(name, ...)helper keeps the runtime behavior coherent and makes overlaps easier to label, but thecompute_overlapsdocstring 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 forzeros_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 oflist(...)[0]for better performance.- Line 626: The loop variable
ilvlis unused; iterate overhier.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: Simplifyhas_non_zero()withnp.any()for efficiency.The current implementation using
np.nonzero()and length checks can be streamlined withnp.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 Falsepyphare/pyphare/core/box.py (1)
186-187: Rename ambiguous variableltoloorlower.The variable name
lcan be confused with1orI. 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) vsdiff(computed difference) is slightly confusing. Consider renaming tocurr_diff0/curr_diff1or similar to clarify these are the existing values being compared against the newly computeddiff.- 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 = Falseandfound = Truewould be more idiomatic for a boolean flag.- found = 0 + found = False for ilvl, overlaps in hierarchy_overlaps(hier, time).items(): ... - found = 1 + found = Truesrc/core/data/field/field_box.hpp (1)
71-82: Ensure<algorithm>is included forstd::max; optional alignment with generic opsThe elementwise max logic and iterator pattern look good and are consistent with
operate_on_fields. Two points:
- To keep this header self‑contained, it should not rely on transitive includes for
std::max. Ifcore/def.hppor 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
- Given the broader PR adds generic operator machinery (e.g.,
operate_on_fields<Operator>and aSetMax-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 nodessrc/amr/messengers/hybrid_hybrid_messenger_strategy.hpp (1)
490-502: Hard-coded size assertions may be fragile.The assertions
ionFluxBorderMaxRefiners_.size() == 1andionDensityBorderMaxRefiners_.size() == 2couple 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:
- Removing the assertions if they're purely for debugging, or
- Adding a clarifying comment explaining why these specific sizes are expected, or
- 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 extraneousfprefix from string literal.The string
f"ranks"has no placeholders, so thefprefix 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 extraneousfprefix 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 extraneousfprefix 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: Usenot infor membership test.The idiomatic Python pattern is
not inrather thannot ... 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 insidecheck_timeorcheck_diagsif 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
KeyErrorhandler 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 withchmod +x.src/amr/messengers/field_operate_transaction.hpp (1)
164-170:printClassDataalways throws - consider implementing or documenting.This method unconditionally throws a runtime error. If it's intentionally not implemented, consider either:
- Adding a
// TODOcomment explaining the intent- 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
📒 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.hppsrc/amr/messengers/hybrid_messenger.hppsrc/core/data/field/field_box.hppsrc/amr/solvers/solver_ppc.hppsrc/amr/messengers/hybrid_hybrid_messenger_strategy.hppsrc/amr/messengers/refiner.hppsrc/diagnostic/diagnostic_model_view.hppsrc/amr/messengers/field_operate_transaction.hppsrc/amr/data/field/field_data.hppsrc/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.pypyphare/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.hppsrc/amr/messengers/refiner.hppsrc/diagnostic/diagnostic_model_view.hppsrc/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 forprint_one_lineThe 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-placeBy switching
kwargs.get("print_one_line", False)to default False,run()will emit one line per advance unless callers explicitly setprint_one_line=True(or provide a customprint_eol). This looks intentional and the override order (print_eolkwarg winning overprint_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 goodUsing
append(ref_pdname, ...)for bothoverlapandother_ovrlpensures 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
DataSelectorclass 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 effectsAdding
"value": "primal"for x/y/z keepsyee_centeringstructurally consistent with existing metadata patterns like"tags". However, confirm that any code iterating overyee_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
fillIonBorderscall is correctly placed after ion moments are computed and at level scope (outside the patch loop), consistent with the existingfillFluxBordersandfillDensityBorderspattern.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
FieldBorderSumTransactionFactoryto the genericFieldBorderOpTransactionFactory<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
fillIonBorderscall is appropriately placed afterupdateIonscompletes, 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
fillIonBordersmethod follows the established delegation pattern and is consistent with the existingfillFluxBordersandfillDensityBordersmethods.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
RefinerTypeenum 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
elsebranch that throws ensures compile-time exhaustiveness checking is supplemented with runtime safety for any unhandledRefinerTypevalues.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 andfillIonBordersfunction.The assertions at lines 881 and 888 check exact sizes before populating refiner pools. If
HybridMessengerInfo'smaxBorderFieldsandmaxBorderVecFieldssizes are guaranteed invariants, these assertions are reasonable defensive checks. However, confirm that both assertion sites (here and infillIonBorders) 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_typeis correctly public whileSetEqualOpis private as an implementation detail.
315-321: LGTM!The new templated
operate<Operation>andunpackStreamAnd<Operation>declarations provide a clean generic interface for different field operations (sum, max, equals). This aligns well with the corresponding changes inTensorFieldData.
443-461: LGTM!The out-of-class template definitions correctly delegate to the existing
copy_<Operation>andunpackStream<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&toSAMRAI::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
FieldBorderSumTransactionto the genericFieldBorderOpTransaction<FieldData_t, Operation>. This allows reuse with different operations (sum, max, equals) through the template parameter.
149-161: LGTM!The
unpackStreammethod correctly uses the templatedunpackStreamAnd<Operation>to apply the generic operation during unpacking.
172-190: LGTM!The
copyLocalDatamethod correctly uses the templatedoperate<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_typeas public while keepingSetEqualOpprivate mirrors the changes infield_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 tounpackStream<Operation>operate<Operation>delegates tocopy_<Operation>This is consistent with the corresponding implementation in
FieldData.
| 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
pyphare.pharesee.hierarchy.hierarchy_utils
| * 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nicolasaunai suggest to comment?
output from new harris diff test