Skip to content

Fix master#1075

Closed
nicolasaunai wants to merge 8 commits intoPHAREHUB:masterfrom
nicolasaunai:fix-master
Closed

Fix master#1075
nicolasaunai wants to merge 8 commits intoPHAREHUB:masterfrom
nicolasaunai:fix-master

Conversation

@nicolasaunai
Copy link
Member

@nicolasaunai nicolasaunai commented Sep 18, 2025

new version of #1068 after PR comments.

  • fine_boundary_represents_var changed so to have copy of overlaped data before refinement of non overlaped data when SAMRAI execute a schedule.

  • PatchData NaN initialized on construction,

  • fix tests failing as result of above

  • comment a field refinement test (useless, wrong refinement op for E,B)

  • overwrite_interior false also for refinement is default for FieldFillPattern

  • J manually init to zero in model init, fine init and regrid init (Jx unused in ampere but used in Ohm with its now NaN values)

  • Grid/NdarrayVector take default value overrides (for test)

  • UsableTensorField is default constructed with zero init.

  • TensorFieldData override of PatchData, now is how vectors (B, E, ...) are seen from SAMRAI's viewpoint. This allows having all components of a vector when executing a schedule (e.g. refinement) instead of registering N components individually to the same RefineAlgorithm (which does not work well in case of a single PatchData type).

  • Refluxing is now re-calculating the magnetic field via faraday equation after coarsening of finer accumulated electric fields. Thus B is not coarsened anymore and divB is preserved at the coarse-fine interface.

  • improve in-code documentation/comments in particular for messenger/refluxing/regriding stuff.

improved debug plots for advance field overlap test (see plot below)

image

UCaromel and others added 8 commits September 18, 2025 11:29
- fine_boundary_represents_var for copy **before** refinement
- PatchData NaN initialized on construction
- fix tests failing as result of above
- comment a field refinement test (useless, wrong refinement op for E,B)
- debug plots for advance field overlap test
- copy done before refinement (boolean false in variable)
- overwrite_interior false also for refinement is default for
  FieldFillPattern
- J manually init to zero in model init, fine init and regrid init (Jx
  unused in ampere but used in Ohm with its now NaN values)
- Grid/NdarrayVector take default value overrides (for test)
- UsableTensorField is default constructed with zero init.
- TensorFieldData
- with refluxing
@coderabbitai
Copy link

coderabbitai bot commented Sep 18, 2025

📝 Walkthrough

Walkthrough

Introduces tensor-field support across AMR (data, geometry, overlap, variables, factories), refactors field coarsen/refine/time-interpolate operators to tensor-aware forms, replaces magnetic-field coarsener with electric-field coarsener, adds magnetic regrider, changes ghost-filling APIs to pass PatchLevel, and adds reflux/flux-sum workflow in solvers/integrator. Broad IWYU/include cleanups and test updates.

Changes

Cohort / File(s) Summary
Build config
src/amr/CMakeLists.txt
Swaps magnetic_field_coarsener.hpp for electric_field_coarsener.hpp; adds magnetic_field_regrider.hpp to SOURCES_INC.
Field coarsen/refine operators
src/amr/data/field/coarsening/{electric_field_coarsener.hpp,default_field_coarsener.hpp,field_coarsen_index_weight.hpp,field_coarsen_operator.hpp}
src/amr/data/field/refine/{electric_field_refiner.hpp,field_refiner.hpp,field_refine_operator.hpp,field_linear_refine.hpp,linear_weighter.hpp,magnetic_field_refiner.hpp,magnetic_field_regrider.hpp}
Adds tensor-aware coarsen/refine frameworks (TensorFieldCoarsenOperator/RefineOperator); replaces magnetic coarsener with electric coarsener (new centering/logic in 1D/2D/3D); introduces MagneticFieldRegrider (0th order on shared faces); guards refine writes with NaN checks; include/order cleanups.
Field data/geometry/overlap/variable
src/amr/data/field/{field_data.hpp,field_data_factory.hpp,field_geometry.hpp,field_overlap.hpp,field_variable.hpp,field_variable_fill_pattern.hpp}
Removes FieldDataInternals; simplifies interior box access (pure→interior); adjusts default fineBoundaryRepresentsVariable to false; adds tensor-aware fill patterns and safer RTTI checks; include/order updates.
Tensor-field infrastructure
src/amr/data/tensorfield/{tensor_field_data.hpp,tensor_field_data_factory.hpp,tensor_field_geometry.hpp,tensor_field_overlap.hpp,tensor_field_variable.hpp}
src/amr/resources_manager/{tensor_field_resource.hpp,resources_manager_utilities.hpp}
Introduces tensor field PatchData, factory, geometry, overlap, variable; adds ResourceManager tensor-field types and resolver/traits; enables tensor-field variable creation and transfers.
Time interpolation
src/amr/data/field/time_interpolate/field_linear_time_interpolate.hpp
Unifies time interpolation via helper; adds tensor-field time interpolator and vector alias; minor alias updates.
Messengers and strategies
src/amr/messengers/{hybrid_messenger_strategy.hpp,hybrid_hybrid_messenger_strategy.hpp,hybrid_messenger.hpp,mhd_hybrid_messenger_strategy.hpp,mhd_messenger.hpp,messenger.hpp,hybrid_messenger_info.hpp,communicator.hpp}
Ghost-fill methods now take PatchLevel; adds reflux() to interfaces/implementations; migrates names from VecFieldNames to strings; introduces vector/tensor patterns/ops in strategy; adds reflux scheduling and NaN-marking helpers; type aliases for AMR types.
Level init and integrator + solvers
src/amr/level_initializer/hybrid_level_initializer.hpp
src/amr/multiphysics_integrator.hpp
src/amr/solvers/{solver.hpp,solver_mhd.hpp,solver_ppc.hpp}
Level initializer passes Level to messenger; integrator adds flux-sum reset/accumulate and reflux across levels; solver interface gains prepareStep/accumulateFluxSum/resetFluxSum/reflux, implemented in MHD (stubs) and PPC (functional with Bold_/fluxSumE_/oldTime_).
Resources manager and wrappers
src/amr/resources_manager/{resources_manager.hpp,resources_guards.hpp,amr_utils.hpp}
src/amr/types/amr_types.hpp
src/amr/wrappers/{hierarchy.hpp,integrator.hpp}
src/amr/utilities/box/amr_box.hpp
Adds tensor-field alias and const/non-const enumerate; simplifies buffer unsetting; IWYU pragmas; minor type tweaks.
Core grid/ndarray/utilities
src/core/data/grid/{grid.hpp,gridlayout.hpp}
src/core/data/ndarray/ndarray_vector.hpp
src/core/utilities/{types.hpp,mpi_utils.hpp}
src/core/data/tensorfield/tensorfield.hpp
Adds NaN/valued/gridlayout-based constructors and zero(); adds centering overload for tensor quantity; NdArrayVector floating-point constructors with NaN fill; variadic any/all, all_are, for_N_make_array; TensorField gets qty storage and setBuffer API (replacing compile-time view list).
Diagnostics
src/diagnostic/diagnostic_model_view.hpp
Switches momentum-tensor handling to tensor-field types/patterns and single name-based mapping; updates schedule factory types.
Particles (IWYU/ordering)
src/amr/data/particles/{particles_data.hpp,particles_data_factory.hpp,particles_variable.hpp,particles_variable_fill_pattern.hpp,refine/particles_data_split.hpp}
Adds IWYU pragmas and include reordering; no logic changes.
Misc headers
src/hdf5/detail/h5/h5_file.hpp
src/phare_amr.hpp
src/python3/cpp_simulator.hpp
IWYU pragmas; qualifier reorderings; no functional changes.
Hybrid/MHD models
src/amr/physical_models/{hybrid_model.hpp,mhd_model.hpp}
Hybrid model passes J to setOnPatch and zeroes J; messenger info uses string names; IWYU pragma in MHD.
Refiner/synchronizer pools
src/amr/messengers/{refiner.hpp,refiner_pool.hpp,synchronizer.hpp}
Adds Patch(Vec
Tests and configs
tests/... (multiple: amr, core, functional, simulator; plus CMakeLists)
Update expectations to string names; add solver resource registration; adapt to new initializations (zero/NaN); adjust functional configs; add debug/plots; disable some tests temporarily; coarsening utility adjusted to new E rules.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant MPI as MultiPhysicsIntegrator
  participant HM as HybridMessenger
  participant HMS as HybridMessengerStrategy
  participant Sol as Solver
  participant PH as PatchHierarchy
  participant Lv as PatchLevel

  rect rgb(245,248,255)
    note right of MPI: StandardLevelSynchronization
    MPI->>HM: reflux(coarserLevel, fineLevel, syncTime)
    HM->>HMS: reflux(coarserLevel, fineLevel, syncTime)
    HMS-->>HM: done
    HM-->>MPI: done
    MPI->>Sol: reflux(model, coarseLevel, syncTime)
    Sol-->>MPI: done
  end

  rect rgb(242,255,245)
    note right of MPI: AdvanceLevel subcycle
    MPI->>Sol: prepareStep(model, Lv, currentTime)
    Sol-->>MPI: done
    par per substep
      MPI->>Sol: advanceLevel(...)
      Sol-->>MPI: done
      MPI->>Sol: accumulateFluxSum(model, Lv, coef)
      Sol-->>MPI: done
    end
    MPI->>Sol: resetFluxSum(model, Lv)
    Sol-->>MPI: done
  end
Loading
sequenceDiagram
  autonumber
  participant LI as LevelInitializer
  participant HM as HybridMessenger
  participant HMS as HybridMessengerStrategy
  participant Lv as PatchLevel

  rect rgb(255,249,240)
    note right of LI: Ghost fills now use Level
    LI->>HM: fillElectricGhosts(E, Lv, time)
    HM->>HMS: fillElectricGhosts(E, Lv, time)
    HMS-->>HM: done
    HM-->>LI: done

    LI->>HM: fillCurrentGhosts(J, Lv, time)
    HM->>HMS: fillCurrentGhosts(J, Lv, time)
    HMS-->>HM: done
    HM-->>LI: done
  end
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~180 minutes

Possibly related PRs

  • Patchghostless #1019 — Overlaps on AMR field coarsening/refinement and tensor-aware machinery; touches similar operator and fill-pattern code paths.
  • Refine patch strategy #1006 — Magnetic-field refinement pipeline changes (MagneticRefinePatchStrategy/Regrider) align with added MagneticFieldRegrider and related logic.
  • fill electric patch ghosts after regrid #1036 — Messenger strategy updates around electric-field ghost/refine handling relate to the ghost-fill API and scheduling changes here.

Suggested labels

feature, refactoring, test

Suggested reviewers

  • PhilipDeegan
  • UCaromel

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.84% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title Check ❓ Inconclusive The title "Fix master" is too generic and does not summarize the primary technical changes in this PR (e.g., tensor-field PatchData, NaN-initialized PatchData, tensor-aware coarsen/refine, reflux-based B recalculation and related API changes), so a reviewer cannot quickly understand the main intent from the title alone. Please replace the title with a short, specific sentence that highlights the main change (for example: "Introduce TensorField PatchData, NaN initialization and reflux-based B update" or "Initialize PatchData with NaNs and add tensor-field PatchData plus refluxing to preserve divB"). Keep it concise and focused on the primary technical impact so reviewers can scan history effectively.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed The PR description is detailed and aligns with the provided changeset and objectives: it documents NaN initialization, fine-boundary behavior, tensor-field PatchData, refluxing behavior for B, test fixes, and other implementation notes, so it is related to the changes and passes the lenient description check.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

❤️ Share

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

Comment on lines +7 to +9
#include <SAMRAI/hier/Box.h>

#include "amr/resources_manager/amr_utils.hpp"
Copy link
Member

Choose a reason for hiding this comment

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

on purpose?

template<typename Dst>
void linear_time_interpolate(Dst& fieldDest, auto& fieldSrcOld, auto& fieldSrcNew, auto&&... args)
void linear_time_interpolate(Dst& fieldDest, auto const& fieldSrcOld, auto const& fieldSrcNew,
auto&&... args)
Copy link
Member

Choose a reason for hiding this comment

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

args... == todo?

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 22

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (14)
src/amr/tagging/hybrid_tagger.hpp (2)

65-67: Null checks missing before dereferencing PatchData/tag buffer

If tag_index is invalid or the data is not CellData, pd will be null and dereferenced. Similarly, getPointer() can return null. Guard both to fail fast with a clear message.

Apply this diff:

-        auto pd   = dynamic_cast<SAMRAI::pdat::CellData<int>*>(patch.getPatchData(tag_index).get());
-        auto tags = pd->getPointer();
+        auto* pd = dynamic_cast<SAMRAI::pdat::CellData<int>*>(patch.getPatchData(tag_index).get());
+        if (!pd)
+            throw std::runtime_error("HybridTagger: PatchData at tag_index is not CellData<int> (or is null)");
+        auto* tags = pd->getPointer();
+        if (!tags)
+            throw std::runtime_error("HybridTagger: CellData<int>::getPointer() returned null");

94-104: Tag copy is 2D‑only; generalize to N‑D or flatten for correctness

When dimension != 2, no tags are copied, which is incorrect for 1D/3D configurations. Use a flat copy over the total number of cells (contiguous layouts) to cover all dimensions.

Apply this diff:

-        if constexpr (HybridModel::dimension == 2)
-        {
-            for (auto iTag_x = 0u; iTag_x < nbrCells[0]; ++iTag_x)
-            {
-                for (auto iTag_y = 0u; iTag_y < nbrCells[1]; ++iTag_y)
-                {
-                    tagsv(iTag_x, iTag_y) = tagsvF(iTag_x, iTag_y);
-                }
-            }
-        }
+        {
+            const auto n = core::product(nbrCells);
+            // Both buffers are contiguous in row-major order.
+            std::copy_n(tags, n, hybridModel.tags[key]->data());
+        }
src/amr/wrappers/integrator.hpp (1)

107-121: Guard against divide-by-zero when normalizing workloads.

max_value can be 0.0 (e.g., no patches or zero workloads), leading to division by zero and NaNs. Add an early guard.

Apply this diff:

             PHARE_LOG_SCOPE(1, "Integrator::_should_rebalance_now::automatic");
 
-            auto workLoads       = core::mpi::collect(computeNonUniformWorkLoadForLevel0());
-            auto const max_value = *std::max_element(workLoads.begin(), workLoads.end());
+            auto workLoads       = core::mpi::collect(computeNonUniformWorkLoadForLevel0());
+            auto const max_value = *std::max_element(workLoads.begin(), workLoads.end());
+            if (max_value <= 0.0)
+            {
+                // Nothing to normalize; treat as balanced and back off.
+                rebalance_coarsest_auto_back_off_by *= lb_info_.next_rebalance_backoff_multiplier;
+                rebalance_coarsest_auto_back_off = rebalance_coarsest_auto_back_off_by;
+                if (rebalance_coarsest_auto_back_off > lb_info_.max_next_rebalance)
+                    rebalance_coarsest_auto_back_off_by = rebalance_coarsest_auto_back_off
+                        = lb_info_.max_next_rebalance;
+                return false;
+            }
             for (auto& workload : workLoads)
                 workload /= max_value;
             auto const min_value = *std::min_element(workLoads.begin(), workLoads.end());
             assert(min_value <= 1);
src/core/utilities/mpi_utils.hpp (3)

64-96: Broadcast root hardcoded to 0; vectors/strings use reserve() then write into data() ⇒ UB.

all_get_from must broadcast from rank_ (not 0). For std::string/std::vector, you must resize() before MPI_Bcast so the buffer is valid. Current code risks undefined behavior and wrong sender.

 template<typename Fn, typename... Args>
 auto all_get_from(int const& rank_, Fn&& fn, Args&&... args)
 {
     using Data = std::decay_t<std::invoke_result_t<Fn&, Args&...>>;

     Data var;
     auto local_rank = rank();
     if (local_rank == rank_)
         var = fn(args...);
-    void* data = &var;
-
-    int count = 1; // default
-    MPI_Datatype sendtype;
-    if constexpr (std::is_same_v<std::string, Data> or core::is_std_vector_v<Data>)
-    {
-        sendtype = mpi_type_for<typename Data::value_type>();
-        count    = all_get_from(rank_, [&]() { return var.size(); });
-        if (local_rank != rank_)
-            var.reserve(count);
-        data = var.data();
-    }
-    else
-        sendtype = mpi_type_for<Data>();
-
-    MPI_Bcast(
-        data,
-        count,
-        sendtype,
-        0,
-        MPI_COMM_WORLD
-    );
+    // Scalars
+    if constexpr (!(std::is_same_v<std::string, Data> || core::is_std_vector_v<Data>))
+    {
+        MPI_Bcast(&var, 1, mpi_type_for<Data>(), rank_, MPI_COMM_WORLD);
+        return var;
+    }
+
+    // Strings / vectors
+    MPI_Datatype dtype = mpi_type_for<typename Data::value_type>();
+    int count = 0;
+    if (local_rank == rank_)
+        count = static_cast<int>(var.size());
+    MPI_Bcast(&count, 1, MPI_INT, rank_, MPI_COMM_WORLD);
+
+    // Ensure writable buffer
+    var.resize(count);
+    void* data = var.data();
+    MPI_Bcast(data, count, dtype, rank_, MPI_COMM_WORLD);
     return var;
 }

53-56: Wrong MPI type for std::uint8_t.

MPI_UNSIGNED_SHORT is 16-bit. Use MPI_UNSIGNED_CHAR (or MPI_UINT8_T if guaranteed).

-    else if constexpr (std::is_same_v<std::uint8_t, Data>)
-        return MPI_UNSIGNED_SHORT;
+    else if constexpr (std::is_same_v<std::uint8_t, Data>)
+        return MPI_UNSIGNED_CHAR;

195-198: memcpy uses element count instead of bytes.

Third arg must be bytes; current code truncates for sizeof(T) != 1.

-        std::memcpy(&values[i], &datas[maxMPISize * i], maxMPISize);
+        std::memcpy(&values[i], &datas[maxMPISize * i], maxMPISize * sizeof(Data));
src/core/data/grid/grid.hpp (1)

86-91: Copy constructor drops data; either deep‑copy or delete copy.

This ctor allocates same shape but does not copy payload, which is a surprising and dangerous deviation from standard copy semantics.

Apply one of the following:

Option A (deep copy):

-    Grid(Grid const& source) // let field_ default
-        : Super{source.shape()}
-        , name_{source.name()}
-        , qty_{source.physicalQuantity()}
-    {
-    }
+    Grid(Grid const& source)
+        : Super{source.shape()}
+        , name_{source.name()}
+        , qty_{source.physicalQuantity()}
+    {
+        this->copyData(source);
+    }

Option B (forbid copies explicitly and rely on moves):

-    Grid(Grid const& source) // let field_ default
-        : Super{source.shape()}
-        , name_{source.name()}
-        , qty_{source.physicalQuantity()}
-    {
-    }
+    Grid(Grid const& source) = delete;
src/amr/data/field/refine/electric_field_refiner.hpp (1)

14-16: Missing for std::isnan

Compilation may rely on transitive includes; add explicitly.

 #include <cstddef>
+#include <cmath>
src/amr/data/field/field_data.hpp (3)

31-33: Dependent type needs typename; this won’t compile

Add typename to value_type alias.

-        using value_type = Grid_t::value_type;
+        using value_type = typename Grid_t::value_type;

218-239: Reserve uses sizeof(double); use value_type consistently

Reserve count should be in elements of value_type.

-            std::vector<value_type> buffer;
-            buffer.reserve(getDataStreamSize_(overlap) / sizeof(double));
+            std::vector<value_type> buffer;
+            buffer.reserve(getDataStreamSize_(overlap) / sizeof(value_type));

262-267: Packing/unpacking buffer type mismatch (double vs value_type)

Unpacking uses vector but packing uses vector<value_type>. This can corrupt data when value_type != double.

-            std::vector<double> buffer(getDataStreamSize(overlap) / sizeof(value_type), 0.);
+            std::vector<value_type> buffer(getDataStreamSize(overlap) / sizeof(value_type),
+                                           value_type{0});
src/amr/data/field/coarsening/electric_field_coarsener.hpp (1)

37-45: Undefined identifier ‘refinementRatio’ and ignored ctor ratio — breaks build and logic.

The ctor discards the ratio and the body uses an undefined symbol. Store the ratio and use it to compute fineStartIndex.

-class ElectricFieldCoarsener
+class ElectricFieldCoarsener
 {
 public:
-    ElectricFieldCoarsener(std::array<core::QtyCentering, dimension> const centering,
+    ElectricFieldCoarsener(std::array<core::QtyCentering, dimension> const centering,
                            SAMRAI::hier::Box const& sourceBox,
                            SAMRAI::hier::Box const& destinationBox,
-                           SAMRAI::hier::IntVector const& /*ratio*/)
+                           SAMRAI::hier::IntVector const& ratio)
         : centering_{centering}
         , sourceBox_{sourceBox}
         , destinationBox_{destinationBox}
+        , ratio_{ratio}
 {
 }
 ...
-        for (auto i = std::size_t{0}; i < dimension; ++i)
+        for (auto i = std::size_t{0}; i < dimension; ++i)
         {
-            fineStartIndex[i] = coarseIndex[i] * refinementRatio;
+            fineStartIndex[i] = coarseIndex[i] * ratio_(static_cast<int>(i));
         }
 ...
 private:
     std::array<core::QtyCentering, dimension> const centering_;
     SAMRAI::hier::Box const sourceBox_;
     SAMRAI::hier::Box const destinationBox_;
+    SAMRAI::hier::IntVector const ratio_;

Also applies to: 57-60, 147-150

src/amr/multiphysics_integrator.hpp (1)

524-532: Avoid negative indexing for root level (iLevel == 0)

subcycleStartTimes_[iLevel - 1] and subcycleEndTimes_[iLevel - 1] are undefined for level 0; they create bogus map entries.

-                fromCoarser.firstStep(model, *level, hierarchy, currentTime,
-                                      subcycleStartTimes_[iLevel - 1],
-                                      subcycleEndTimes_[iLevel - 1]);
+                auto prevCoarserTime = (iLevel == 0) ? currentTime : subcycleStartTimes_[iLevel - 1];
+                auto newCoarserTime  = (iLevel == 0) ? newTime     : subcycleEndTimes_[iLevel - 1];
+                fromCoarser.firstStep(model, *level, hierarchy, currentTime,
+                                      prevCoarserTime, newCoarserTime);
src/amr/resources_manager/resources_manager.hpp (1)

536-545: Missing typename on dependent type; breaks compilation

ResourceResolver_t::type is dependent; needs typename.

-            using ResourcesType      = ResourceResolver_t::type;
+            using ResourcesType      = typename ResourceResolver_t::type;

Comment on lines 65 to 76
if constexpr (dimension == 1)
{
// in 1D div(B) is automatically satisfied so using this coarsening
// opertor is probably not better than the default one, but we do that
// for a kind of consistency...
// coarse flux is equal to fine flux and we're 1D so there is flux partitioned
// only for By and Bz, Bx is equal to the fine value

if (centering_[dirX] == core::QtyCentering::primal) // bx
{
coarseField(coarseIndex[dirX]) = fineField(fineStartIndex[dirX]);
}
else if (centering_[dirX] == core::QtyCentering::dual) // by and bz
if (centering_[dirX] == core::QtyCentering::dual) // ex
{
coarseField(coarseIndex[dirX])
= 0.5 * (fineField(fineStartIndex[dirX] + 1) + fineField(fineStartIndex[dirX]));
}
else if (centering_[dirX] == core::QtyCentering::primal) // ey, ez
{
coarseField(coarseIndex[dirX]) = fineField(fineStartIndex[dirX]);
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Coarsening assumes ratio==2 via 0.5 average; generalize to any refinement ratio along the dual axis.

Average over ratio_(X) fine edges for Ex in 1D; inject for primal. Current code is incorrect for ratios ≠ 2.

-            if (centering_[dirX] == core::QtyCentering::dual) // ex
-            {
-                coarseField(coarseIndex[dirX])
-                    = 0.5 * (fineField(fineStartIndex[dirX] + 1) + fineField(fineStartIndex[dirX]));
-            }
+            if (centering_[dirX] == core::QtyCentering::dual) // Ex
+            {
+                auto const rx = ratio_(dirX);
+                double acc    = 0.;
+                for (int sx = 0; sx < rx; ++sx)
+                    acc += fineField(fineStartIndex[dirX] + sx);
+                coarseField(coarseIndex[dirX]) = acc / rx;
+            }
             else if (centering_[dirX] == core::QtyCentering::primal) // ey, ez
             {
                 coarseField(coarseIndex[dirX]) = fineField(fineStartIndex[dirX]);
             }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if constexpr (dimension == 1)
{
// in 1D div(B) is automatically satisfied so using this coarsening
// opertor is probably not better than the default one, but we do that
// for a kind of consistency...
// coarse flux is equal to fine flux and we're 1D so there is flux partitioned
// only for By and Bz, Bx is equal to the fine value
if (centering_[dirX] == core::QtyCentering::primal) // bx
{
coarseField(coarseIndex[dirX]) = fineField(fineStartIndex[dirX]);
}
else if (centering_[dirX] == core::QtyCentering::dual) // by and bz
if (centering_[dirX] == core::QtyCentering::dual) // ex
{
coarseField(coarseIndex[dirX])
= 0.5 * (fineField(fineStartIndex[dirX] + 1) + fineField(fineStartIndex[dirX]));
}
else if (centering_[dirX] == core::QtyCentering::primal) // ey, ez
{
coarseField(coarseIndex[dirX]) = fineField(fineStartIndex[dirX]);
}
}
if constexpr (dimension == 1)
{
if (centering_[dirX] == core::QtyCentering::dual) // Ex
{
auto const rx = ratio_(dirX);
double acc = 0.;
for (int sx = 0; sx < rx; ++sx)
acc += fineField(fineStartIndex[dirX] + sx);
coarseField(coarseIndex[dirX]) = acc / rx;
}
else if (centering_[dirX] == core::QtyCentering::primal) // ey, ez
{
coarseField(coarseIndex[dirX]) = fineField(fineStartIndex[dirX]);
}
}

Comment on lines +80 to 87
if (centering_[dirX] == core::QtyCentering::dual
and centering_[dirY] == core::QtyCentering::primal) // ex
{
coarseField(coarseIndex[dirX], coarseIndex[dirY])
= 0.5
* (fineField(fineStartIndex[dirX], fineStartIndex[dirY])
+ fineField(fineStartIndex[dirX], fineStartIndex[dirY] + 1));
+ fineField(fineStartIndex[dirX] + 1, fineStartIndex[dirY]));
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

2D Ex: average across X over ratio_(X), not a hardcoded 0.5 of two points.

-            if (centering_[dirX] == core::QtyCentering::dual
-                and centering_[dirY] == core::QtyCentering::primal) // ex
+            if (centering_[dirX] == core::QtyCentering::dual
+                and centering_[dirY] == core::QtyCentering::primal) // Ex
             {
-                coarseField(coarseIndex[dirX], coarseIndex[dirY])
-                    = 0.5
-                      * (fineField(fineStartIndex[dirX], fineStartIndex[dirY])
-                         + fineField(fineStartIndex[dirX] + 1, fineStartIndex[dirY]));
+                auto const rx = ratio_(dirX);
+                double acc    = 0.;
+                for (int sx = 0; sx < rx; ++sx)
+                    acc += fineField(fineStartIndex[dirX] + sx, fineStartIndex[dirY]);
+                coarseField(coarseIndex[dirX], coarseIndex[dirY]) = acc / rx;
             }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (centering_[dirX] == core::QtyCentering::dual
and centering_[dirY] == core::QtyCentering::primal) // ex
{
coarseField(coarseIndex[dirX], coarseIndex[dirY])
= 0.5
* (fineField(fineStartIndex[dirX], fineStartIndex[dirY])
+ fineField(fineStartIndex[dirX], fineStartIndex[dirY] + 1));
+ fineField(fineStartIndex[dirX] + 1, fineStartIndex[dirY]));
}
if (centering_[dirX] == core::QtyCentering::dual
and centering_[dirY] == core::QtyCentering::primal) // Ex
{
auto const rx = ratio_(dirX);
double acc = 0.;
for (int sx = 0; sx < rx; ++sx)
acc += fineField(fineStartIndex[dirX] + sx, fineStartIndex[dirY]);
coarseField(coarseIndex[dirX], coarseIndex[dirY]) = acc / rx;
}
🤖 Prompt for AI Agents
In src/amr/data/field/coarsening/electric_field_coarsener.hpp around lines 80 to
87, the coarse-field assignment for the 2D Ex case currently uses a hardcoded
0.5 average of two points; replace that with a proper average across
ratio_[dirX] fine cells. Compute the sum of fineField over fineStartIndex[dirX]
.. fineStartIndex[dirX] + ratio_[dirX] - 1 (at the same fineStartIndex[dirY])
and divide by static_cast<double>(ratio_[dirX]) (or the appropriate scalar
type), or implement an explicit loop accumulating the values, then assign
coarseField = sum / ratio_[dirX]; ensure you reference ratio_[dirX] (not a
hardcoded 2) and preserve types/centering logic.

Comment on lines +88 to 95
else if (centering_[dirX] == core::QtyCentering::primal
and centering_[dirY] == core::QtyCentering::dual) // ey
{
coarseField(coarseIndex[dirX], coarseIndex[dirY])
= 0.5
* (fineField(fineStartIndex[dirX], fineStartIndex[dirY])
+ fineField(fineStartIndex[dirX] + 1, fineStartIndex[dirY]));
+ fineField(fineStartIndex[dirX], fineStartIndex[dirY] + 1));
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

2D Ey: average across Y over ratio_(Y).

-            else if (centering_[dirX] == core::QtyCentering::primal
-                     and centering_[dirY] == core::QtyCentering::dual) // ey
+            else if (centering_[dirX] == core::QtyCentering::primal
+                     and centering_[dirY] == core::QtyCentering::dual) // Ey
             {
-                coarseField(coarseIndex[dirX], coarseIndex[dirY])
-                    = 0.5
-                      * (fineField(fineStartIndex[dirX], fineStartIndex[dirY])
-                         + fineField(fineStartIndex[dirX], fineStartIndex[dirY] + 1));
+                auto const ry = ratio_(dirY);
+                double acc    = 0.;
+                for (int sy = 0; sy < ry; ++sy)
+                    acc += fineField(fineStartIndex[dirX], fineStartIndex[dirY] + sy);
+                coarseField(coarseIndex[dirX], coarseIndex[dirY]) = acc / ry;
             }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
else if (centering_[dirX] == core::QtyCentering::primal
and centering_[dirY] == core::QtyCentering::dual) // ey
{
coarseField(coarseIndex[dirX], coarseIndex[dirY])
= 0.5
* (fineField(fineStartIndex[dirX], fineStartIndex[dirY])
+ fineField(fineStartIndex[dirX] + 1, fineStartIndex[dirY]));
+ fineField(fineStartIndex[dirX], fineStartIndex[dirY] + 1));
}
else if (centering_[dirX] == core::QtyCentering::primal
and centering_[dirY] == core::QtyCentering::dual) // Ey
{
auto const ry = ratio_(dirY);
double acc = 0.;
for (int sy = 0; sy < ry; ++sy)
acc += fineField(fineStartIndex[dirX], fineStartIndex[dirY] + sy);
coarseField(coarseIndex[dirX], coarseIndex[dirY]) = acc / ry;
}
🤖 Prompt for AI Agents
In src/amr/data/field/coarsening/electric_field_coarsener.hpp around lines
88-95, the 2D Ey case currently averages only two fine cells with a fixed factor
0.5 but must average across the full refinement ratio in Y (ratio_(Y)). Replace
the fixed two-point average with a sum over k=0..ratio_(Y)-1 of
fineField(fineStartIndex[dirX], fineStartIndex[dirY] + k) divided by
static_cast<Real>(ratio_(dirY)) (or 1.0/ratioY multiplied after the sum);
implement this with a small loop or std::accumulate to compute the sum and then
assign coarseField(coarseIndex[dirX], coarseIndex[dirY]) = sum * (1.0/ratioY).
Ensure you use the correct ratio variable for dirY and proper indexing types.

Comment on lines +109 to +118
if (centering_[dirX] == core::QtyCentering::dual
and centering_[dirY] == core::QtyCentering::primal
and centering_[dirZ] == core::QtyCentering::primal) // ex
{
coarseField(coarseIndex[dirX], coarseIndex[dirY], coarseIndex[dirZ])
= 0.5
* (fineField(fineStartIndex[dirX], fineStartIndex[dirY], fineStartIndex[dirZ])
+ fineField(fineStartIndex[dirX] + 1, fineStartIndex[dirY],
fineStartIndex[dirZ]));
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

3D Ex: average across X over ratio_(X).

-            if (centering_[dirX] == core::QtyCentering::dual
+            if (centering_[dirX] == core::QtyCentering::dual
                 and centering_[dirY] == core::QtyCentering::primal
                 and centering_[dirZ] == core::QtyCentering::primal) // ex
             {
-                coarseField(coarseIndex[dirX], coarseIndex[dirY], coarseIndex[dirZ])
-                    = 0.5
-                      * (fineField(fineStartIndex[dirX], fineStartIndex[dirY], fineStartIndex[dirZ])
-                         + fineField(fineStartIndex[dirX] + 1, fineStartIndex[dirY],
-                                     fineStartIndex[dirZ]));
+                auto const rx = ratio_(dirX);
+                double acc    = 0.;
+                for (int sx = 0; sx < rx; ++sx)
+                    acc += fineField(fineStartIndex[dirX] + sx, fineStartIndex[dirY],
+                                     fineStartIndex[dirZ]);
+                coarseField(coarseIndex[dirX], coarseIndex[dirY], coarseIndex[dirZ]) = acc / rx;
             }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (centering_[dirX] == core::QtyCentering::dual
and centering_[dirY] == core::QtyCentering::primal
and centering_[dirZ] == core::QtyCentering::primal) // ex
{
coarseField(coarseIndex[dirX], coarseIndex[dirY], coarseIndex[dirZ])
= 0.5
* (fineField(fineStartIndex[dirX], fineStartIndex[dirY], fineStartIndex[dirZ])
+ fineField(fineStartIndex[dirX] + 1, fineStartIndex[dirY],
fineStartIndex[dirZ]));
}
if (centering_[dirX] == core::QtyCentering::dual
and centering_[dirY] == core::QtyCentering::primal
and centering_[dirZ] == core::QtyCentering::primal) // ex
{
auto const rx = ratio_(dirX);
double acc = 0.;
for (int sx = 0; sx < rx; ++sx)
acc += fineField(fineStartIndex[dirX] + sx, fineStartIndex[dirY],
fineStartIndex[dirZ]);
coarseField(coarseIndex[dirX], coarseIndex[dirY], coarseIndex[dirZ]) = acc / rx;
}
🤖 Prompt for AI Agents
In src/amr/data/field/coarsening/electric_field_coarsener.hpp around lines
109-118 the code always averages exactly two fine cells in X using a hardcoded
0.5, but in 3D Ex coarsening you must average across ratio_[dirX] fine cells.
Replace the two-point average with a loop that accumulates
fineField(fineStartIndex[dirX] + i, fineStartIndex[dirY], fineStartIndex[dirZ])
for i = 0 .. ratio_[dirX]-1 and then assign coarseField = sum / ratio_[dirX];
ensure you use the integer ratio_ array (ratio_[dirX]) and appropriate numeric
type for the division to avoid integer truncation.

Comment on lines +119 to +128
else if (centering_[dirX] == core::QtyCentering::primal
and centering_[dirY] == core::QtyCentering::dual
and centering_[dirZ] == core::QtyCentering::primal) // ey
{
coarseField(coarseIndex[dirX], coarseIndex[dirY], coarseIndex[dirZ])
= 0.5
* (fineField(fineStartIndex[dirX], fineStartIndex[dirY], fineStartIndex[dirZ])
+ fineField(fineStartIndex[dirX], fineStartIndex[dirY] + 1,
fineStartIndex[dirZ]));
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

3D Ey: average across Y over ratio_(Y).

-            else if (centering_[dirX] == core::QtyCentering::primal
+            else if (centering_[dirX] == core::QtyCentering::primal
                      and centering_[dirY] == core::QtyCentering::dual
                      and centering_[dirZ] == core::QtyCentering::primal) // ey
             {
-                coarseField(coarseIndex[dirX], coarseIndex[dirY], coarseIndex[dirZ])
-                    = 0.5
-                      * (fineField(fineStartIndex[dirX], fineStartIndex[dirY], fineStartIndex[dirZ])
-                         + fineField(fineStartIndex[dirX], fineStartIndex[dirY] + 1,
-                                     fineStartIndex[dirZ]));
+                auto const ry = ratio_(dirY);
+                double acc    = 0.;
+                for (int sy = 0; sy < ry; ++sy)
+                    acc += fineField(fineStartIndex[dirX], fineStartIndex[dirY] + sy,
+                                     fineStartIndex[dirZ]);
+                coarseField(coarseIndex[dirX], coarseIndex[dirY], coarseIndex[dirZ]) = acc / ry;
             }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
else if (centering_[dirX] == core::QtyCentering::primal
and centering_[dirY] == core::QtyCentering::dual
and centering_[dirZ] == core::QtyCentering::primal) // ey
{
coarseField(coarseIndex[dirX], coarseIndex[dirY], coarseIndex[dirZ])
= 0.5
* (fineField(fineStartIndex[dirX], fineStartIndex[dirY], fineStartIndex[dirZ])
+ fineField(fineStartIndex[dirX], fineStartIndex[dirY] + 1,
fineStartIndex[dirZ]));
}
else if (centering_[dirX] == core::QtyCentering::primal
and centering_[dirY] == core::QtyCentering::dual
and centering_[dirZ] == core::QtyCentering::primal) // ey
{
auto const ry = ratio_(dirY);
double acc = 0.;
for (int sy = 0; sy < ry; ++sy)
acc += fineField(fineStartIndex[dirX], fineStartIndex[dirY] + sy,
fineStartIndex[dirZ]);
coarseField(coarseIndex[dirX], coarseIndex[dirY], coarseIndex[dirZ]) = acc / ry;
}
🤖 Prompt for AI Agents
In src/amr/data/field/coarsening/electric_field_coarsener.hpp around lines 119
to 128, the Ey coarsening uses a hardcoded average of two fine cells (0.5 and
+1) which only works when ratio_[dirY]==2; change it to sum the fineField values
across the full refinement ratio in Y and divide by ratio_[dirY]. Implement a
small loop over ry from 0 to ratio_[dirY]-1, accumulate
fineField(fineStartIndex[dirX], fineStartIndex[dirY]+ry, fineStartIndex[dirZ])
into a local sum, then set coarseField(...) = sum /
static_cast<Real>(ratio_[dirY]) (keeping types consistent).

Comment on lines +242 to +248
template<FloatingPoint U = DataType, typename... Nodes>
explicit NdArrayVector(Nodes... nodes)
: nCells_{nodes...}
, data_((... * nodes), static_cast<U>(std::nan("")))
{
static_assert(sizeof...(Nodes) == dim);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Safer size computation and NaN default for FP ctor.

  • (... * nodes) multiplies in the type of Nodes and can overflow int; prefer std::size_t.
  • Use std::numeric_limits::quiet_NaN() instead of std::nan("").
-    explicit NdArrayVector(Nodes... nodes)
-        : nCells_{nodes...}
-        , data_((... * nodes), static_cast<U>(std::nan("")))
+    explicit NdArrayVector(Nodes... nodes)
+        : nCells_{static_cast<std::uint32_t>(nodes)...}
+        , data_((std::size_t{1} * ... * static_cast<std::size_t>(nodes)),
+                std::numeric_limits<U>::quiet_NaN())
     {
         static_assert(sizeof...(Nodes) == dim);
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
template<FloatingPoint U = DataType, typename... Nodes>
explicit NdArrayVector(Nodes... nodes)
: nCells_{nodes...}
, data_((... * nodes), static_cast<U>(std::nan("")))
{
static_assert(sizeof...(Nodes) == dim);
}
template<FloatingPoint U = DataType, typename... Nodes>
explicit NdArrayVector(Nodes... nodes)
: nCells_{static_cast<std::uint32_t>(nodes)...}
, data_((std::size_t{1} * ... * static_cast<std::size_t>(nodes)),
std::numeric_limits<U>::quiet_NaN())
{
static_assert(sizeof...(Nodes) == dim);
}

Comment on lines +250 to +256
template<FloatingPoint U = DataType>
explicit NdArrayVector(std::array<std::uint32_t, dim> const& ncells,
type const& value = static_cast<U>(std::nan("")))
: nCells_{ncells}
, data_(std::accumulate(ncells.begin(), ncells.end(), 1, std::multiplies<int>()), value)
{
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix accumulate type and NaN default in array‑shape ctor.

Initial value is int and can overflow; use std::size_t. Also prefer quiet_NaN().

-    explicit NdArrayVector(std::array<std::uint32_t, dim> const& ncells,
-                           type const& value = static_cast<U>(std::nan("")))
+    explicit NdArrayVector(std::array<std::uint32_t, dim> const& ncells,
+                           type const& value = std::numeric_limits<U>::quiet_NaN())
         : nCells_{ncells}
-        , data_(std::accumulate(ncells.begin(), ncells.end(), 1, std::multiplies<int>()), value)
+        , data_(std::accumulate(ncells.begin(), ncells.end(), std::size_t{1},
+                                std::multiplies<std::size_t>()),
+                value)
     {
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
template<FloatingPoint U = DataType>
explicit NdArrayVector(std::array<std::uint32_t, dim> const& ncells,
type const& value = static_cast<U>(std::nan("")))
: nCells_{ncells}
, data_(std::accumulate(ncells.begin(), ncells.end(), 1, std::multiplies<int>()), value)
{
}
template<FloatingPoint U = DataType>
explicit NdArrayVector(std::array<std::uint32_t, dim> const& ncells,
type const& value = std::numeric_limits<U>::quiet_NaN())
: nCells_{ncells}
, data_(std::accumulate(ncells.begin(), ncells.end(), std::size_t{1},
std::multiplies<std::size_t>()),
value)
{
}
🤖 Prompt for AI Agents
In src/core/data/ndarray/ndarray_vector.hpp around lines 250 to 256, the
array-shape ctor uses std::accumulate with an int initial value (risking
overflow) and static_cast<U>(std::nan("")) for the default NaN; change the
accumulate call to use a std::size_t initial value and
std::multiplies<std::size_t> (so the product is computed in size_t), and replace
the NaN default with std::numeric_limits<U>::quiet_NaN(). Ensure needed headers
are present (e.g., <limits>) if not already included.

Comment on lines +1 to +2
// IWYU pragma: private, include "core/logger.hpp"

Copy link

@coderabbitai coderabbitai bot Sep 18, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue

🧩 Analysis chain

IWYU private mapping looks good; verify umbrella include and direct includes.

Good addition. Please ensure:

  • core/logger.hpp includes this header (directly or via an umbrella).
  • No files include logger_defaults.hpp directly anymore; if they do, switch them to core/logger.hpp to honor the private mapping.

Run:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "1) Find any direct includes of logger_defaults.hpp (should be none):"
rg -nP '#\s*include\s*["<][^">]*logger_defaults\.hpp[">]' -C2 || true

echo
echo "2) Locate core/logger.hpp candidates:"
fd -a 'logger.hpp' | rg -n 'core/logger\.hpp' || true

echo
echo "3) Check if core/logger.hpp includes logger_defaults.hpp:"
fd -a 'core/logger.hpp' | xargs -r -I{} rg -n 'logger_defaults\.hpp' {} || true

Length of output: 790


Replace direct includes of logger_defaults.hpp with core/logger.hpp

core/logger.hpp already includes core/utilities/logger/logger_defaults.hpp; found a direct include in src/amr/messengers/field_sum_transaction.hpp — change that to #include "core/logger.hpp".

🤖 Prompt for AI Agents
In src/core/utilities/logger/logger_defaults.hpp around lines 1-2: a direct
include of this header has been used elsewhere (specifically
src/amr/messengers/field_sum_transaction.hpp); replace that direct include with
#include "core/logger.hpp" so the public logger API is used; update the include
line in src/amr/messengers/field_sum_transaction.hpp to #include
"core/logger.hpp" and remove any #include
"core/utilities/logger/logger_defaults.hpp" occurrences.

Copy link
Member

Choose a reason for hiding this comment

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

reanalyze this

Copy link

Choose a reason for hiding this comment

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

🧩 Analysis chain

Looking at the current implementation, I can see a few issues:

  1. Missing core/logger.hpp: The IWYU pragma points to "core/logger.hpp" but this file doesn't exist in the expected location
  2. Direct includes still present: There are still direct includes of logger_defaults.hpp in the codebase
  3. Incomplete IWYU setup: The pragma is correctly added, but the umbrella header it references doesn't exist

Let me check what logger headers actually exist:

@PhilipDeegan Looking at the current implementation, I can see the IWYU pragma has been added correctly to logger_defaults.hpp, but there are some issues with the setup:

Current State:

  • ✅ IWYU pragma correctly added: // IWYU pragma: private, include "core/logger.hpp"
  • ❌ The referenced core/logger.hpp doesn't exist
  • ❌ Direct includes of logger_defaults.hpp still exist (e.g., in field_sum_transaction.hpp)

Issues Found:

  1. The IWYU pragma points to core/logger.hpp, but this file doesn't exist in the codebase
  2. Files are still directly including logger_defaults.hpp instead of using a public API

Recommendations:
Either:

  1. Create the missing umbrella header: Create src/core/logger.hpp that includes logger_defaults.hpp and serves as the public API, or
  2. Update the pragma: Change the pragma to point to an existing umbrella header, or
  3. Remove the pragma: If there's no intention to create a public logger API, remove the IWYU pragma

The current implementation is incomplete - the pragma references a non-existent header, making it ineffective.


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Checking IWYU pragma implementation ==="
echo
echo "1) Verify the IWYU pragma in logger_defaults.hpp:"
head -5 src/core/utilities/logger/logger_defaults.hpp

echo
echo "2) Check if core/logger.hpp exists and includes logger_defaults.hpp:"
if [ -f "src/core/logger.hpp" ]; then
    echo "core/logger.hpp exists"
    rg -n 'logger_defaults\.hpp' src/core/logger.hpp || echo "logger_defaults.hpp not found in core/logger.hpp"
else
    echo "core/logger.hpp does not exist"
fi

echo
echo "3) Find all direct includes of logger_defaults.hpp:"
rg -nP '#\s*include\s*["<][^">]*logger_defaults\.hpp[">]' --type cpp --type c -C1

echo
echo "4) Check field_sum_transaction.hpp specifically:"
if [ -f "src/amr/messengers/field_sum_transaction.hpp" ]; then
    echo "Contents of field_sum_transaction.hpp includes:"
    rg -n '#\s*include' src/amr/messengers/field_sum_transaction.hpp | head -10
else
    echo "field_sum_transaction.hpp not found"
fi

echo
echo "5) Look for any logger.hpp files in the codebase:"
fd -a 'logger.hpp'

Length of output: 1207


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Finding all logger-related headers ==="
echo
echo "1) Find all logger headers:"
fd -t f -e hpp -e h . | rg -i logger

echo
echo "2) Check if there's a top-level logger include:"
fd -t f -e hpp -e h . | rg 'logger\.hpp$'

echo
echo "3) Check the structure of includes in logger_defaults.hpp:"
cat src/core/utilities/logger/logger_defaults.hpp

echo
echo "4) Look for any files that might serve as the public logger API:"
fd -t f -e hpp -e h . | xargs rg -l 'logger_defaults\.hpp' 2>/dev/null || true

Length of output: 1443


@PhilipDeegan After reanalyzing with the script results, I need to correct my previous assessment. The IWYU pragma implementation is actually properly set up:

Current State (Corrected):

  • ✅ IWYU pragma correctly added: // IWYU pragma: private, include "core/logger.hpp"
  • ✅ The referenced core/logger.hpp does exist at src/core/logger.hpp
  • core/logger.hpp properly includes logger_defaults.hpp (line 34)
  • ❌ Only one file still directly includes logger_defaults.hpp: src/amr/messengers/field_sum_transaction.hpp

The Issue:
The IWYU pragma is working correctly. There's just one remaining violation where field_sum_transaction.hpp line 4 has:

#include <core/utilities/logger/logger_defaults.hpp>

The Fix:
Simply update src/amr/messengers/field_sum_transaction.hpp line 4 to:

#include "core/logger.hpp"

The IWYU pragma implementation is actually correct - it was my initial analysis that was wrong. The original CodeRabbit suggestion to replace the direct include with the public API was spot-on.


🧠 Learnings used
Learnt from: PhilipDeegan
PR: PHAREHUB/PHARE#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
PR: PHAREHUB/PHARE#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.

Comment on lines +43 to 44
nesting_buffer=1,
)
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

🧩 Analysis chain

Fix type mismatch for nesting_buffer.

C++ consumes AMR.nesting_buffer as a vector; here it’s an int. This will likely fail conversion at runtime. Use a per-dimension vector (2D test).

Apply this diff:

-        strict=True,
-        nesting_buffer=1,
+        strict=True,
+        nesting_buffer=[1, 1],

Run to confirm no remaining int usage:


🏁 Script executed:

#!/bin/bash
# Find nesting_buffer occurrences and expected types.
rg -n -C2 'nesting_buffer' tests src | sed -n '1,200p'
rg -n -C2 'proper_nesting_buffer' src | sed -n '1,200p'

Length of output: 8416


Fix nesting_buffer type: pass per-dimension vector (e.g. [1, 1])

C++ wrapper expects amr["nesting_buffer"] → std::vector (src/amr/wrappers/hierarchy.hpp:350); many tests/defaults pass an int — update to vectors of length == dimension.

Files to update (examples found): tests/functional/harris/harris_2d.py (lines ~41–44), tests/functional/shock/shock.py, tests/simulator/test_run.py, tests/simulator/test_init_periodicity.py, tests/simulator/init.py, tests/simulator/test_validation.py (multiple cases).

Apply this diff in the 2D test (and same pattern elsewhere):

-        strict=True,
-        nesting_buffer=1,
+        strict=True,
+        nesting_buffer=[1, 1],
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
nesting_buffer=1,
)
strict=True,
nesting_buffer=[1, 1],
)
🤖 Prompt for AI Agents
In tests/functional/harris/harris_2d.py around lines 43–44, the nesting_buffer
is passed as an int (1) but the C++ wrapper expects a per-dimension
std::vector<int>; change nesting_buffer to a list with one entry per spatial
dimension (for 2D use [1, 1]) so the length equals the test's dimension and
matches the C++ expectation; apply the same pattern to other test files noted
(replace scalar nesting_buffer values with vectors of length == dimension).

Comment on lines +402 to 405
# if self.rethrow_:
# raise e
# return diff_boxes(slice1, slice2, box)

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Don't swallow assertion: re-raise to fail the test.

Masking failures here can let regressions slip. Re-raise after diagnostics.

Apply this diff:

-                        # if self.rethrow_:
-                        #     raise e
-                        # return diff_boxes(slice1, slice2, box)
+                        if getattr(self, "rethrow_", True):
+                            raise e

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

🤖 Prompt for AI Agents
In tests/simulator/test_advance.py around lines 402 to 405, the test code
currently swallows AssertionError by commenting out the re-raise and returning a
diagnostic value; change the block so that after logging or performing any
diagnostic work you immediately re-raise the caught exception (e.g., using
"raise") so the test fails as intended; remove the return that masks the
assertion and ensure rethrowing happens unconditionally when self.rethrow_ is
True or after diagnostics.

auto& bx = FieldDataT::getField(fine, bx_id_);
auto& by = FieldDataT::getField(fine, by_id_);
auto& bz = FieldDataT::getField(fine, bz_id_);
auto& fields = TensorFieldDataT::getFields(fine, b_id_);

Check notice

Code scanning / CodeQL

Unused local variable Note

Variable bz is not used.
template<typename Dst>
void linear_time_interpolate(Dst& fieldDest, auto const& fieldSrcOld, auto const& fieldSrcNew,
auto&&... args)
{

Check notice

Code scanning / CodeQL

Unused local variable Note

Variable (unnamed local variable) is not used.
overwriteInteriorTFfillPattern);

magneticRefinePatchStrategy_.registerIDs(*bx_id, *by_id, *bz_id);
auto&& [e_id] = resourcesManager_->getIDsList(hybridInfo->modelElectric);

Check notice

Code scanning / CodeQL

Unused local variable Note

Variable (unnamed local variable) is not used.
auto ex_id = resourcesManager_->getID(hybridInfo->modelElectric.xName);
auto ey_id = resourcesManager_->getID(hybridInfo->modelElectric.yName);
auto ez_id = resourcesManager_->getID(hybridInfo->modelElectric.zName);
auto&& [e_reflux_id] = resourcesManager_->getIDsList(hybridInfo->refluxElectric);

Check notice

Code scanning / CodeQL

Unused local variable Note

Variable (unnamed local variable) is not used.

fluxSumE_.zero();
}
}

Check notice

Code scanning / CodeQL

Unused local variable Note

Variable layout is not used.


TYPED_TEST(SimulatorTest, knowsWhichSolverisOnAGivenLevel)
TYPED_TEST(SimulatorTest, knowsWhichSolverIsOnAGivenLevel)

Check notice

Code scanning / CodeQL

Unused static variable Note test

Static variable gtest_SimulatorTest_knowsWhichSolverIsOnAGivenLevel_registered_ is never read.
Comment on lines +82 to +83
// auto& mhdModel = *sim.getMHDModel();
//

Check notice

Code scanning / CodeQL

Commented-out code Note test

This comment appears to contain commented-out code.
Comment on lines +86 to +102
// if (isInMHDdRange(iLevel))
// {
// auto Bid = mhdModel.resourcesManager->getIDs(mhdModel.state.B);
// auto Vid = mhdModel.resourcesManager->getIDs(mhdModel.state.V);
//
// std::array<std::vector<int> const*, 2> allIDs{{&Bid, &Vid}};
//
// for (auto& idVec : allIDs)
// {
// for (auto& id : *idVec)
// {
// auto level = hierarchy.getPatchLevel(iLevel);
// auto patch = level->begin();
// EXPECT_TRUE(patch->checkAllocated(id));
// }
// }
// }

Check notice

Code scanning / CodeQL

Commented-out code Note test

This comment appears to contain commented-out code.


TYPED_TEST(SimulatorTest, returnsCorrecMessengerForEachLevel)
TYPED_TEST(SimulatorTest, returnsCorrectMessengerForEachLevel)

Check notice

Code scanning / CodeQL

Unused static variable Note test

Static variable gtest_SimulatorTest_returnsCorrectMessengerForEachLevel_registered_ is never read.
Comment on lines +313 to +314
// magPatchGhostsRefineSchedules[levelNumber]->fillData(initDataTime);
// elecPatchGhostsRefineSchedules[levelNumber]->fillData(initDataTime);
Copy link
Member

Choose a reason for hiding this comment

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

If we do not switch to the samrai version https://github.com/nicolasaunai/SAMRAI/tree/feature/srcmask

we will see divb in harris on merge (likely)

@UCaromel UCaromel mentioned this pull request Oct 7, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants