Skip to content

Commit f379ba2

Browse files
authored
check for domain overlaps (#1129)
check for domain overlaps & test emergency dump is made
1 parent 786b529 commit f379ba2

File tree

5 files changed

+91
-27
lines changed

5 files changed

+91
-27
lines changed

src/amr/level_initializer/hybrid_level_initializer.hpp

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,24 @@
11
#ifndef PHARE_HYBRID_LEVEL_INITIALIZER_HPP
22
#define PHARE_HYBRID_LEVEL_INITIALIZER_HPP
33

4-
#include "amr/level_initializer/level_initializer.hpp"
5-
#include "amr/messengers/hybrid_messenger.hpp"
6-
#include "amr/messengers/messenger.hpp"
7-
#include "amr/physical_models/hybrid_model.hpp"
8-
#include "amr/physical_models/physical_model.hpp"
9-
#include "amr/resources_manager/amr_utils.hpp"
10-
#include "core/data/grid/gridlayout_utils.hpp"
11-
#include "core/data/ions/ions.hpp"
4+
#include "core/errors.hpp"
5+
#include "core/numerics/ohm/ohm.hpp"
6+
#include "core/utilities/mpi_utils.hpp"
127
#include "core/numerics/ampere/ampere.hpp"
13-
#include "core/numerics/interpolator/interpolator.hpp"
148
#include "core/numerics/moments/moments.hpp"
15-
#include "core/numerics/ohm/ohm.hpp"
9+
#include "core/data/grid/gridlayout_utils.hpp"
10+
#include "core/numerics/interpolator/interpolator.hpp"
11+
12+
#include "amr/messengers/messenger.hpp"
13+
#include "amr/messengers/hybrid_messenger.hpp"
14+
#include "amr/resources_manager/amr_utils.hpp"
15+
#include "amr/physical_models/physical_model.hpp"
16+
#include "amr/level_initializer/level_initializer.hpp"
17+
1618
#include "initializer/data_provider.hpp"
1719

20+
#include <exception>
21+
1822
namespace PHARE
1923
{
2024
namespace solver
@@ -36,7 +40,7 @@ namespace solver
3640
PHARE::core::Ohm<GridLayoutT> ohm_;
3741
PHARE::core::Ampere<GridLayoutT> ampere_;
3842

39-
inline bool isRootLevel(int levelNumber) const { return levelNumber == 0; }
43+
inline bool isRootLevel(int const levelNumber) const { return levelNumber == 0; }
4044

4145
public:
4246
explicit HybridLevelInitializer(PHARE::initializer::PHAREDict const& dict)
@@ -79,6 +83,17 @@ namespace solver
7983
}
8084
}
8185

86+
try
87+
{
88+
amr::noDomainOverlapsOn(*hierarchy, levelNumber); // VERY BAD!
89+
}
90+
catch (std::exception const& ex)
91+
{
92+
PHARE_LOG_ERROR(ex.what());
93+
}
94+
if (core::mpi::any(core::Errors::instance().any()))
95+
throw core::DictionaryException{}("ID", "HybridLevelInitializer::initialize");
96+
8297
// now all particles are here, we must compute moments.
8398
auto& ions = hybridModel.state.ions;
8499
auto& rm = *hybridModel.resourcesManager;
@@ -167,6 +182,7 @@ namespace solver
167182
hybMessenger.fillElectricGhosts(E, level, 0.);
168183
}
169184

185+
170186
// quantities have been computed on the level,like the moments and J
171187
// that we later in the code need to get on level ghost nodes via
172188
// space and TIME interpolation. We thus need to save current values

src/amr/resources_manager/amr_utils.hpp

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <SAMRAI/hier/BoxOverlap.h>
1717
#include <SAMRAI/hier/HierarchyNeighbors.h>
1818
#include <SAMRAI/geom/CartesianPatchGeometry.h>
19+
#include <stdexcept>
1920

2021
namespace PHARE
2122
{
@@ -196,6 +197,33 @@ namespace amr
196197
}
197198

198199

200+
201+
NO_DISCARD auto inline getSameLevelNeighbors(SAMRAI::hier::Patch const& patch,
202+
SAMRAI::hier::PatchHierarchy const& hierarchy)
203+
{
204+
auto const lvlNbr = patch.getPatchLevelNumber();
205+
206+
return SAMRAI::hier::HierarchyNeighbors{hierarchy, lvlNbr, lvlNbr}.getSameLevelNeighbors(
207+
patch.getBox(), lvlNbr);
208+
}
209+
210+
void inline noDomainOverlapsOn(SAMRAI::hier::PatchHierarchy const& hierarchy, int const ilvl)
211+
{
212+
for (auto const& patch : *hierarchy.getPatchLevel(ilvl))
213+
for (auto const& neighbox : getSameLevelNeighbors(*patch, hierarchy))
214+
if (auto const overlap = patch->getBox() * neighbox; !overlap.empty())
215+
throw std::runtime_error(
216+
"CATASTROPHIC ERROR: Patch domain overlap detected on level: "
217+
+ std::to_string(ilvl));
218+
}
219+
220+
void inline noDomainOverlapsOn(SAMRAI::hier::PatchHierarchy const& hierarchy)
221+
{
222+
for (int iLevel = 0; iLevel < hierarchy.getNumberOfLevels(); ++iLevel)
223+
noDomainOverlapsOn(hierarchy, iLevel);
224+
}
225+
226+
199227
// potentially to replace with SAMRAI coarse to fine boundary stuff
200228
template<typename GridLayoutT> // fow now it gives us a box for only patch ghost layer
201229
NO_DISCARD auto makeNonLevelGhostBoxFor(SAMRAI::hier::Patch const& patch,
@@ -207,8 +235,7 @@ namespace amr
207235
auto const domBox = phare_box_from<dimension>(domain);
208236
auto const particleGhostBox = grow(domBox, GridLayoutT::nbrParticleGhosts());
209237

210-
SAMRAI::hier::HierarchyNeighbors const hier_nbrs{hierarchy, lvlNbr, lvlNbr};
211-
auto const neighbors = hier_nbrs.getSameLevelNeighbors(domain, lvlNbr);
238+
auto const neighbors = getSameLevelNeighbors(patch, hierarchy);
212239
std::vector<core::Box<int, GridLayoutT::dimension>> patchGhostLayerBoxes;
213240
patchGhostLayerBoxes.reserve(neighbors.size() + 1);
214241
patchGhostLayerBoxes.emplace_back(domBox);
@@ -245,7 +272,7 @@ namespace amr
245272
int minLevel, int maxLevel, Args&&... args)
246273
{
247274
for (int iLevel = minLevel; iLevel < hierarchy.getNumberOfLevels() && iLevel <= maxLevel;
248-
iLevel++)
275+
++iLevel)
249276
{
250277
visitLevel<GridLayout>(*hierarchy.getPatchLevel(iLevel), resman,
251278
std::forward<Action>(action), std::forward<Args...>(args...));

src/core/errors.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#include "core/def.hpp"
55

6+
#include <stdexcept>
67
#include <string>
78
#include <sstream>
89
#include <iostream>
@@ -44,6 +45,13 @@ class DictionaryException : public std::exception
4445
return (msg = (*this)()).c_str();
4546
}
4647

48+
std::string id() const
49+
{
50+
if (!dict_.contains("ID"))
51+
throw std::runtime_error("DictionaryException has no ID");
52+
return (*this)["ID"].template to<std::string>();
53+
}
54+
4755
private:
4856
Dict_t dict_;
4957
};

src/simulator/simulator.hpp

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -262,9 +262,7 @@ void Simulator<opts>::diagnostics_init(initializer::PHAREDict const& dict)
262262
}
263263
}
264264

265-
this->allowEmergencyDumps = dict.contains("allow_emergency_dumps")
266-
? dict["allow_emergency_dumps"].template to<bool>()
267-
: false;
265+
this->allowEmergencyDumps = cppdict::get_value(dict, "allow_emergency_dumps", false);
268266
}
269267

270268

@@ -410,6 +408,12 @@ void Simulator<opts>::initialize()
410408

411409
integrator_->initialize();
412410
}
411+
catch (core::DictionaryException const& ex)
412+
{
413+
handle_dictionary_exception(ex);
414+
error = std::string{"DICTIONARY EXCEPTION CAUGHT: "} + ex.what();
415+
PHARE_LOG_ERROR(*error);
416+
}
413417
catch (std::exception const& e)
414418
{
415419
error = std::string{"EXCEPTION CAUGHT: "} + e.what();
@@ -486,16 +490,14 @@ double Simulator<opts>::advance(double dt)
486490
template<auto opts>
487491
void Simulator<opts>::handle_dictionary_exception(core::DictionaryException const& ex)
488492
{
493+
constexpr static std::array dump_exceptions{"Updater::updatePopulations",
494+
"HybridLevelInitializer::initialize"};
495+
489496
if (this->allowEmergencyDumps)
490-
{
491-
if (ex["ID"].template to<std::string>() == "Updater::updatePopulations")
492-
{
493-
for (auto level_nbr = 0; level_nbr < hierarchy_->getMaxNumberOfLevels(); ++level_nbr)
494-
{
495-
this->dMan->dump_level(level_nbr, currentTime_);
496-
}
497-
}
498-
}
497+
for (auto const& exception_id : dump_exceptions)
498+
if (ex.id() == exception_id)
499+
for (int ilvl = 0; ilvl < hierarchy_->getMaxNumberOfLevels(); ++ilvl)
500+
this->dMan->dump_level(ilvl, currentTime_);
499501
}
500502

501503
template<auto opts>

tests/simulator/test_exceptions.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,13 @@
33
import unittest
44

55
import pyphare.pharein as ph
6+
from pyphare.pharesee.run import Run
67
from pyphare.simulator import simulator
78

89

10+
ph.NO_GUI()
11+
12+
913
def setup_model(ppc=100):
1014
def density(*xyz):
1115
return 1.0
@@ -60,6 +64,10 @@ def vthz(*xyz):
6064
},
6165
)
6266
ph.ElectronModel(closure="isothermal", Te=0.12)
67+
68+
ph.FluidDiagnostics( # NO TIMESTAMPS!
69+
quantity="density", write_timestamps=[], population_name="protons"
70+
)
6371
return model
6472

6573

@@ -72,7 +80,7 @@ def vthz(*xyz):
7280
"dl": 0.3,
7381
"diag_options": {
7482
"format": "phareh5",
75-
"options": {"dir": out, "mode": "overwrite"},
83+
"options": {"dir": out, "mode": "overwrite", "allow_emergency_dumps": True},
7684
},
7785
}
7886

@@ -94,6 +102,9 @@ def test(self):
94102
simulator.exit_on_exception = False
95103
simulator.Simulator(simulation).run()
96104

105+
# the following will fail if there is no emergency diag
106+
Run(out).GetN(0, "protons").plot(filename="exceptional_pop_density.png")
107+
97108

98109
if __name__ == "__main__":
99110
simulator.startMPI()

0 commit comments

Comments
 (0)