OPALX (Object Oriented Parallel Accelerator Library for Exascal) master (dc2a29eed580)
OPALX
Loading...
Searching...
No Matches
TestBinnedFieldSolver.cpp
Go to the documentation of this file.
1
28#include <gtest/gtest.h>
29
30#include <cmath>
31#include <memory>
32#include <random>
33#include <string>
34
37#include "Ippl.h"
38#include "PartBunch/PartBunch.h"
39#include "Structure/Beam.h"
41#include "Structure/DataSink.h"
43#include "Utilities/Options.h"
44#include "Utility/Inform.h"
45
46namespace {
47
48 class TestableFieldSolverCmd : public FieldSolverCmd {
49 public:
50 void setType(const std::string& t) {
52 }
53
54 void setBCX(const std::string& bc) {
56 }
57 void setBCY(const std::string& bc) {
59 }
60 void setBCZ(const std::string& bc) {
62 }
63
64 void setBinsName(const std::string& binsName) {
65 Attributes::setString(this->itsAttr[FIELDSOLVER::BINS], binsName);
66 }
67 };
68
69 class TestableBinningCmd : public BinningCmd {
70 public:
71 void setMaxBins(int value) {
72 Attributes::setReal(itsAttr[BINNING::MAXBINS], static_cast<double>(value));
73 }
74
75 void setAdaptiveBinning(bool value) {
77 }
78
79 void setTablePrintFrequency(double value) {
81 }
82
83 void setParameterString(const std::string& value) {
85 }
86 };
87
91 using CoordinateSelector_t = typename PartBunch_t::CoordinateSelector_t;
92
93 constexpr size_t kDefaultNParticles = 64;
94
95 class BinnedFieldSolverSmokeTest : public ::testing::Test {
96 protected:
97 static void SetUpTestSuite() {
98 int argc = 0;
99 char** argv = nullptr;
100 ippl::initialize(argc, argv);
101
102 // DataSink requires a basename to create *.stat / *.lbal writers.
103 OpalData::getInstance()->storeInputFn("unit_test.opal");
104
105 // Many OPAL writers assume `gmsg` is initialized (see SDDSWriter/StatWriter).
106 // Unit tests normally don't set this up via Main().
107 gmsg = new Inform(nullptr, -1);
108
109 // DataSink::DataSink() constructs HDF5 writers when enabled, but the unit
110 // test doesn't have an H5PartWrapper. Disable HDF5 for this smoke test.
111 Options::enableHDF5 = false;
112 }
113
114 static void TearDownTestSuite() {
115 delete gmsg;
116 gmsg = nullptr;
117 ippl::finalize();
118 }
119
120 void SetUp() override {
121 // Keep mesh small so scatter/solve/gather are quick.
122 constexpr double nx = 8;
123 constexpr double ny = 8;
124 constexpr double nz = 8;
125
126 fsCmd = std::make_shared<TestableFieldSolverCmd>();
127 fsCmd->setType("NONE");
128 fsCmd->setNX(nx);
129 fsCmd->setNY(ny);
130 fsCmd->setNZ(nz);
131 fsCmd->setBCX("PERIODIC");
132 fsCmd->setBCY("PERIODIC");
133 fsCmd->setBCZ("PERIODIC");
134
135 // Keep the concrete solver command alive; PartBunch borrows it.
136 fsCmdBase = fsCmd;
137
138 dataSink = std::make_shared<DataSink>();
139 beam = std::make_shared<Beam>();
140 Beam* testBeam = Beam::find("UNNAMED_BEAM");
141
142 // qi/mi/lbt are used by rho scaling; but with NullSolver we mostly validate
143 // "no-throw" and deterministic zero E behavior.
144 bunch = std::make_shared<PartBunch_t>(
145 /*qi=*/std::vector<double>{1.0},
146 /*mi=*/std::vector<double>{1.0},
147 /*beams=*/std::vector<Beam*>{testBeam},
148 /*totalParticlesPerBeam=*/std::vector<size_t>{kDefaultNParticles},
149 /*lbt=*/1.0,
150 /*integration_method=*/"LF2", fsCmdBase.get(), dataSink.get());
151 pc = bunch->getParticleContainer();
152 }
153
154 void TearDown() override {
155 // Ensure device allocations can be freed between tests.
156 bunch.reset();
157 dataSink.reset();
158 fsCmd.reset();
159 fsCmdBase.reset();
160 pc.reset();
161 }
162
163 void createParticles(size_t nPart, double pzMin, double pzMax) {
164 pc->createParticles(nPart);
165
166 std::mt19937_64 eng(42 + ippl::Comm->rank());
167
168 auto R_host = pc->R.getHostMirror();
169 auto P_host = pc->P.getHostMirror();
170 auto dt_host = pc->dt.getHostMirror();
171 auto E_host = pc->E.getHostMirror();
172
173 // Match mesh extents from the bunch' field container.
174 const auto rmin = bunch->rmin_m;
175 const auto rmax = bunch->rmax_m;
176
177 std::uniform_real_distribution<double> unifR_x(rmin[0] + 0.1, rmax[0] - 0.1);
178 std::uniform_real_distribution<double> unifR_y(rmin[1] + 0.1, rmax[1] - 0.1);
179 std::uniform_real_distribution<double> unifR_z(rmin[2] + 0.1, rmax[2] - 0.1);
180 std::uniform_real_distribution<double> unifP_z(pzMin, pzMax);
181
182 const double dt = bunch->getdT();
183 const double qi = pc->getChargePerParticle();
184
185 for (size_t i = 0; i < nPart; ++i) {
186 R_host(i)[0] = unifR_x(eng);
187 R_host(i)[1] = unifR_y(eng);
188 R_host(i)[2] = unifR_z(eng);
189
190 P_host(i)[0] = 0.0;
191 P_host(i)[1] = 0.0;
192 P_host(i)[2] = unifP_z(eng);
193
194 dt_host(i) = dt;
195
196 // Initialize particle E to zero; solver should leave it zero in NONE mode.
197 E_host(i)[0] = 0.0;
198 E_host(i)[1] = 0.0;
199 E_host(i)[2] = 0.0;
200 }
201
202 Kokkos::deep_copy(pc->R.getView(), R_host);
203 Kokkos::deep_copy(pc->P.getView(), P_host);
204 Kokkos::deep_copy(pc->dt.getView(), dt_host);
205 Kokkos::deep_copy(pc->E.getView(), E_host);
206 pc->setQ(qi);
207
208 ippl::Comm->barrier();
209 Kokkos::fence();
210 pc->update();
211 }
212
213 void rebuildBunch() {
214 fsCmdBase = fsCmd;
215 Beam* testBeam = Beam::find("UNNAMED_BEAM");
216 bunch = std::make_shared<PartBunch_t>(
217 /*qi=*/std::vector<double>{1.0},
218 /*mi=*/std::vector<double>{1.0},
219 /*beams=*/std::vector<Beam*>{testBeam},
220 /*totalParticlesPerBeam=*/std::vector<size_t>{kDefaultNParticles},
221 /*lbt=*/1.0,
222 /*integration_method=*/"LF2", fsCmdBase.get(), dataSink.get());
223 pc = bunch->getParticleContainer();
224 }
225
226 std::shared_ptr<AdaptBins_t> attachBins(
227 typename AdaptBins_t::bin_index_type maxBins, double alpha, double beta,
228 double desiredWidth) {
229 using ConcreteBins_t =
231
232 CoordinateSelector_t selector(/*axis=*/2);
233 auto bins = std::make_shared<ConcreteBins_t>(
234 *pc, selector, maxBins, alpha, beta, desiredWidth,
235 /*binningCmdName=*/"TEST_BINNING_CMD");
236 bunch->setBins(bins);
237 return bins;
238 }
239
240 void defineBinningCommand(
241 const std::string& name, typename AdaptBins_t::bin_index_type maxBins,
242 bool adaptiveBinning) {
243 auto* binsCmd = new TestableBinningCmd();
244 binsCmd->setOpalName(name);
245 binsCmd->setMaxBins(maxBins);
246 binsCmd->setAdaptiveBinning(adaptiveBinning);
247 binsCmd->setTablePrintFrequency(0.0);
248 binsCmd->setParameterString("VELOCITYZ");
249 binsCmd->execute();
250 OpalData::getInstance()->define(binsCmd);
251 }
252
253 void expectAllParticleEZeroAndFinite(double tol) {
254 auto E_host = pc->E.getHostMirror();
255 Kokkos::deep_copy(E_host, pc->E.getView());
256
257 const size_t localN = pc->getLocalNum();
258 for (size_t i = 0; i < localN; ++i) {
259 for (unsigned d = 0; d < 3; ++d) {
260 const double val = E_host(i)[d];
261 EXPECT_TRUE(std::isfinite(val)) << "E[" << i << "][" << d << "]=" << val;
262 EXPECT_NEAR(val, 0.0, tol) << "E[" << i << "][" << d << "]";
263 }
264 }
265 }
266
267 std::shared_ptr<TestableFieldSolverCmd> fsCmd;
268 std::shared_ptr<FieldSolverCmd> fsCmdBase;
269 std::shared_ptr<DataSink> dataSink;
270 std::shared_ptr<Beam> beam;
271 std::shared_ptr<PartBunch_t> bunch;
272 std::shared_ptr<ParticleContainer_t> pc;
273 };
274
275 TEST_F(BinnedFieldSolverSmokeTest, LegacyPath_NoBins_NoThrowAndEZero) {
276 ASSERT_FALSE(bunch->hasBinning());
277 createParticles(kDefaultNParticles, /*pzMin=*/0.1, /*pzMax=*/0.9);
278
279 EXPECT_NO_THROW(bunch->computeSelfFields());
280 expectAllParticleEZeroAndFinite(/*tol=*/1e-8);
281 }
282
283 TEST_F(BinnedFieldSolverSmokeTest, BinnedPath_WithBins_NoThrowAndEZero) {
284 createParticles(kDefaultNParticles, /*pzMin=*/0.1, /*pzMax=*/2.0);
285
286 constexpr AdaptBins_t::bin_index_type maxBins = 6;
287 auto bins = attachBins(maxBins, /*alpha=*/1.0, /*beta=*/1.0, /*desiredWidth=*/0.3);
288 ASSERT_TRUE(bunch->hasBinning());
289
290 EXPECT_NO_THROW(bunch->computeSelfFields());
291
292 const auto currentBins = bins->getCurrentBinCount();
293 EXPECT_GE(currentBins, 1);
294 EXPECT_LE(currentBins, maxBins);
295
296 expectAllParticleEZeroAndFinite(/*tol=*/1e-8);
297 }
298
299 TEST_F(BinnedFieldSolverSmokeTest, BinnedPath_AdaptiveBinningFalseKeepsUniformMaxBins) {
300 constexpr AdaptBins_t::bin_index_type maxBins = 6;
301 const std::string binsName = "UNIT_TEST_UNIFORM_BINNING";
302 defineBinningCommand(binsName, maxBins, /*adaptiveBinning=*/false);
303
304 fsCmd->setType("OPEN");
305 fsCmd->setBinsName(binsName);
306 fsCmd->setBCX("OPEN");
307 fsCmd->setBCY("OPEN");
308 fsCmd->setBCZ("OPEN");
309 rebuildBunch();
310
311 createParticles(kDefaultNParticles, /*pzMin=*/0.1, /*pzMax=*/2.0);
312 ASSERT_TRUE(bunch->hasBinning());
313
314 EXPECT_NO_THROW(bunch->computeSelfFields());
315
316 auto bins = bunch->getBins();
317 ASSERT_NE(bins, nullptr);
318 EXPECT_EQ(bins->getCurrentBinCount(), maxBins);
319 }
320
321 TEST_F(BinnedFieldSolverSmokeTest, BunchUpdate_ImageChargeBoundsIncludeMirroredZ) {
322 constexpr double zPlane = 0.0;
323 bunch->setImageChargeConfiguration(true, zPlane);
324
325 createParticles(kDefaultNParticles, /*pzMin=*/0.1, /*pzMax=*/0.9);
326 bunch->bunchUpdate();
327
328 pc->computeMinMaxR();
329 const auto minR = pc->getMinR();
330 const auto maxR = pc->getMaxR();
331 const auto gridMin = bunch->rmin_m;
332 const auto gridMax = bunch->rmax_m;
333
334 const double mirroredMinZ = 2.0 * zPlane - maxR[2];
335 const double mirroredMaxZ = 2.0 * zPlane - minR[2];
336
337 EXPECT_LE(gridMin[2], std::min(minR[2], mirroredMinZ));
338 EXPECT_GE(gridMax[2], std::max(maxR[2], mirroredMaxZ));
339 }
340
341} // namespace
Inform * gmsg
Definition changes.cpp:7
Template PIC bunch: IPPL PicManager, shared field mesh/solver, and multiple particle containers.
TEST_F(MonitorTest, GetType)
Definition Beam.h:32
static Beam * find(const std::string &name)
Find named BEAM.
Definition Beam.cpp:290
void storeInputFn(const std::string &fn)
store opals input filename
Definition OpalData.cpp:561
static OpalData * getInstance()
Definition OpalData.cpp:193
void define(Object *newObject)
Define a new object.
Definition OpalData.cpp:400
typename ParticleBinning::CoordinateSelector< ParticleContainer_t > CoordinateSelector_t
Definition PartBunch.h:63
ParticleContainer< T, Dim > ParticleContainer_t
Definition PartBunch.h:56
typename ParticleBinning::AdaptBinsBase< ParticleContainer_t > AdaptBins_t
Definition PartBunch.h:65
A class that bins particles in energy bins and allows for adaptive runtime rebinning.
Definition AdaptBins.h:79
typename BunchType::bin_index_type bin_index_type
Definition AdaptBins.h:86
Container for all per-particle (and per-simulation) fields tracked during OPALX tracking.
void setMaxBins(int value)
void setTablePrintFrequency(double value)
void setParameterString(const std::string &value)
void setAdaptiveBinning(bool value)
void setBool(Attribute &attr, bool val)
Set logical value.
void setString(Attribute &attr, const std::string &val)
Set string value.
void setReal(Attribute &attr, double val)
Set real value.
void setPredefinedString(Attribute &attr, const std::string &val)
Set predefined string value.
@ ADAPTIVEBINNING
Definition BinningCmd.h:45
@ TABLEPRINTFREQ
Definition BinningCmd.h:48
@ PARAMETER
Definition BinningCmd.h:44
bool enableHDF5
If true HDF5 files are written.
Definition Options.cpp:83