OPALX (Object Oriented Parallel Accelerator Library for Exascal) master (dc2a29eed580)
OPALX
Loading...
Searching...
No Matches
TestMultiContainerPartBunch.cpp
Go to the documentation of this file.
1
7#include <gtest/gtest.h>
8
9#include <cstdio>
10#include <memory>
11#include <random>
12#include <stdexcept>
13#include <string>
14#include <vector>
15
18#include "Ippl.h"
19#include "PartBunch/PartBunch.h"
20#include "Structure/Beam.h"
21#include "Structure/DataSink.h"
25#include "Utilities/Options.h"
26#include "Utility/Inform.h"
27
28namespace {
29
30 class TestableFieldSolverCmd : public FieldSolverCmd {
31 public:
32 void setType(const std::string& t) {
34 }
35
36 void setBCX(const std::string& bc) {
38 }
39 void setBCY(const std::string& bc) {
41 }
42 void setBCZ(const std::string& bc) {
44 }
45 };
46
49
50 constexpr size_t kParticlesPerBeam = 32;
51
52 class MultiContainerPartBunchTest : public ::testing::Test {
53 protected:
54 static void SetUpTestSuite() {
55 int argc = 0;
56 char** argv = nullptr;
57 ippl::initialize(argc, argv);
58
59 OpalData::getInstance()->storeInputFn("unit_test.opal");
60 gmsg = new Inform(nullptr, -1);
61 Options::enableHDF5 = false;
62 }
63
64 static void TearDownTestSuite() {
65 delete gmsg;
66 gmsg = nullptr;
67 ippl::finalize();
68 }
69
70 void SetUp() override {
71 fsCmd = std::make_shared<TestableFieldSolverCmd>();
72 fsCmd->setType("NONE");
73 fsCmd->setNX(8);
74 fsCmd->setNY(8);
75 fsCmd->setNZ(8);
76 fsCmd->setBCX("PERIODIC");
77 fsCmd->setBCY("PERIODIC");
78 fsCmd->setBCZ("PERIODIC");
79 fsCmdBase = fsCmd;
80
81 dataSink = std::make_shared<DataSink>();
82 beam = std::make_shared<Beam>();
83 testBeam = Beam::find("UNNAMED_BEAM");
84 ASSERT_NE(testBeam, nullptr);
85
86 const double q0 = 1.0e-9;
87 const double q1 = 2.0e-9;
88 const double m0 = 0.511e-3;
89 const double m1 = 0.938;
90
91 bunch = std::make_shared<PartBunch_t>(
92 std::vector<double>{q0, q1}, std::vector<double>{m0, m1},
93 std::vector<Beam*>{testBeam, testBeam},
94 std::vector<size_t>{kParticlesPerBeam, kParticlesPerBeam},
95 /*lbt=*/1.0,
96 /*integration_method=*/"LF2", fsCmdBase.get(), dataSink.get());
97 q0_m = q0;
98 q1_m = q1;
99 m0_m = m0;
100 m1_m = m1;
101 }
102
103 void TearDown() override {
104 bunch.reset();
105 dataSink.reset();
106 fsCmd.reset();
107 fsCmdBase.reset();
108 beam.reset();
109 }
110
111 void createParticlesInContainer(
112 size_t containerIndex, size_t nPart, double pzMin, double pzMax) {
113 auto pc = bunch->getParticleContainer(containerIndex);
114 const int mpiRank = ippl::Comm->rank();
115 std::mt19937_64 eng(1000u + containerIndex * 97u + static_cast<unsigned>(mpiRank));
116
117 pc->createParticles(nPart);
118
119 auto R_host = pc->R.getHostMirror();
120 auto P_host = pc->P.getHostMirror();
121 auto dt_host = pc->dt.getHostMirror();
122 auto E_host = pc->E.getHostMirror();
123
124 const auto rmin = bunch->rmin_m;
125 const auto rmax = bunch->rmax_m;
126
127 std::uniform_real_distribution<double> unifR_x(rmin[0] + 0.1, rmax[0] - 0.1);
128 std::uniform_real_distribution<double> unifR_y(rmin[1] + 0.1, rmax[1] - 0.1);
129 std::uniform_real_distribution<double> unifR_z(rmin[2] + 0.1, rmax[2] - 0.1);
130 std::uniform_real_distribution<double> unifP_z(pzMin, pzMax);
131
132 const double dt = bunch->getdT();
133 const double qi = (containerIndex == 0) ? q0_m : q1_m;
134
135 for (size_t i = 0; i < nPart; ++i) {
136 R_host(i)[0] = unifR_x(eng);
137 R_host(i)[1] = unifR_y(eng);
138 R_host(i)[2] = unifR_z(eng);
139
140 P_host(i)[0] = 0.0;
141 P_host(i)[1] = 0.0;
142 P_host(i)[2] = unifP_z(eng);
143
144 dt_host(i) = dt;
145
146 E_host(i)[0] = 0.0;
147 E_host(i)[1] = 0.0;
148 E_host(i)[2] = 0.0;
149 }
150
151 Kokkos::deep_copy(pc->R.getView(), R_host);
152 Kokkos::deep_copy(pc->P.getView(), P_host);
153 Kokkos::deep_copy(pc->dt.getView(), dt_host);
154 Kokkos::deep_copy(pc->E.getView(), E_host);
155 pc->setQ(qi);
156
157 ippl::Comm->barrier();
158 Kokkos::fence();
159 pc->update();
160 }
161
162 static std::array<Vector_t<double, 3>, 2> zeroFdPair() {
163 std::array<Vector_t<double, 3>, 2> fd{};
164 fd[0] = Vector_t<double, 3>(0.0);
165 fd[1] = Vector_t<double, 3>(0.0);
166 return fd;
167 }
168
169 std::shared_ptr<TestableFieldSolverCmd> fsCmd;
170 std::shared_ptr<FieldSolverCmd> fsCmdBase;
171 std::shared_ptr<DataSink> dataSink;
172 std::shared_ptr<Beam> beam;
173 Beam* testBeam = nullptr;
174 std::shared_ptr<PartBunch_t> bunch;
175 double q0_m = 0.0;
176 double q1_m = 0.0;
177 double m0_m = 0.0;
178 double m1_m = 0.0;
179 };
180
181 // --- PartBunch / container access ---
182
183 TEST_F(MultiContainerPartBunchTest, TwoContainers_CountsAndDistinctPointers) {
184 ASSERT_EQ(bunch->getNumParticleContainers(), 2u);
185 ASSERT_EQ(bunch->getParticleContainers().size(), 2u);
186
187 auto pc0 = bunch->getParticleContainer(0);
188 auto pc1 = bunch->getParticleContainer(1);
189 ASSERT_NE(pc0, nullptr);
190 ASSERT_NE(pc1, nullptr);
191 EXPECT_NE(pc0.get(), pc1.get());
192
193 EXPECT_EQ(bunch->getParticleContainer(0).get(), bunch->getParticleContainer().get());
194 }
195
196 TEST_F(MultiContainerPartBunchTest, TwoContainers_PerContainerChargeAndMass) {
197 auto pc0 = bunch->getParticleContainer(0);
198 auto pc1 = bunch->getParticleContainer(1);
199
200 EXPECT_DOUBLE_EQ(pc0->getChargePerParticle(), q0_m);
201 EXPECT_DOUBLE_EQ(pc1->getChargePerParticle(), q1_m);
202 EXPECT_DOUBLE_EQ(pc0->getMassPerParticle(), m0_m);
203 EXPECT_DOUBLE_EQ(pc1->getMassPerParticle(), m1_m);
204 }
205
206 TEST_F(MultiContainerPartBunchTest, GetParticleContainer_IndexOutOfRange) {
207 EXPECT_THROW(static_cast<void>(bunch->getParticleContainer(2)), std::out_of_range);
208 }
209
210 TEST_F(MultiContainerPartBunchTest, Constructor_ThrowsOnQiSizeMismatch) {
211 EXPECT_THROW(
212 static_cast<void>(std::make_shared<PartBunch_t>(
213 std::vector<double>{1.0}, std::vector<double>{1.0, 1.0},
214 std::vector<Beam*>{testBeam, testBeam},
215 std::vector<size_t>{kParticlesPerBeam, kParticlesPerBeam}, 1.0, "LF2",
216 fsCmdBase.get(), dataSink.get())),
218 }
219
220 TEST_F(MultiContainerPartBunchTest, Constructor_ThrowsOnNullBeam) {
221 EXPECT_THROW(
222 static_cast<void>(std::make_shared<PartBunch_t>(
223 std::vector<double>{1.0, 1.0}, std::vector<double>{1.0, 1.0},
224 std::vector<Beam*>{testBeam, nullptr},
225 std::vector<size_t>{kParticlesPerBeam, kParticlesPerBeam}, 1.0, "LF2",
226 fsCmdBase.get(), dataSink.get())),
228 }
229
230 TEST_F(MultiContainerPartBunchTest, Constructor_ThrowsOnNullDataSink) {
231 EXPECT_THROW(
232 static_cast<void>(std::make_shared<PartBunch_t>(
233 std::vector<double>{1.0, 1.0}, std::vector<double>{1.0, 1.0},
234 std::vector<Beam*>{testBeam, testBeam},
235 std::vector<size_t>{kParticlesPerBeam, kParticlesPerBeam}, 1.0, "LF2",
236 fsCmdBase.get(), nullptr)),
238 }
239
240 TEST_F(MultiContainerPartBunchTest, TotalNumAllContainers_SumsBoth) {
241 const size_t n0 = 11u;
242 const size_t n1 = 7u;
243 createParticlesInContainer(0, n0, 0.1, 0.5);
244 createParticlesInContainer(1, n1, 0.2, 0.6);
245
246 EXPECT_EQ(bunch->getTotalNumAllContainers(), n0 + n1);
247 }
248
249 // --- DataSink stems and writers ---
250
251 TEST_F(MultiContainerPartBunchTest, DiagnosticStemForContainer_SingleVsMulti) {
252 EXPECT_EQ(DataSink::diagnosticStemForContainer("run", 1, 0), "run");
253 EXPECT_EQ(DataSink::diagnosticStemForContainer("run", 1, 99), "run");
254
255 EXPECT_EQ(DataSink::diagnosticStemForContainer("run", 2, 0), "run_c0");
256 EXPECT_EQ(DataSink::diagnosticStemForContainer("run", 2, 1), "run_c1");
257 }
258
259 TEST_F(MultiContainerPartBunchTest, DataSink_MultiContainerInit_DoesNotThrow) {
260 EXPECT_NO_THROW(DataSink(std::vector<H5PartWrapper*>{}, false, 2));
261 std::remove((OpalData::getInstance()->getInputBasename() + ".lbal").c_str());
262 }
263
264 TEST_F(MultiContainerPartBunchTest, DataSink_dumpSDDS_ThrowsWhenFdextTooSmall) {
265 createParticlesInContainer(0, 8u, 0.1, 0.3);
266 createParticlesInContainer(1, 6u, 0.1, 0.3);
267
268 DataSink ds(std::vector<H5PartWrapper*>{}, false, 2);
269 std::vector<std::array<Vector_t<double, 3>, 2>> fdTooSmall;
270 fdTooSmall.push_back(zeroFdPair());
271
272 const double azimuth = 0.0;
273 EXPECT_THROW(ds.dumpSDDS(*bunch, fdTooSmall, azimuth), OpalException);
274
275 std::remove((DataSink::diagnosticStemForContainer("unit_test", 2, 0) + ".stat").c_str());
276 std::remove((DataSink::diagnosticStemForContainer("unit_test", 2, 1) + ".stat").c_str());
277 std::remove((OpalData::getInstance()->getInputBasename() + ".lbal").c_str());
278 }
279
280 TEST_F(MultiContainerPartBunchTest, DataSink_dumpSDDS_NoThrowWithMatchingFdextAndHdf5Off) {
281 createParticlesInContainer(0, 8u, 0.1, 0.3);
282 createParticlesInContainer(1, 6u, 0.1, 0.3);
283
284 DataSink ds(std::vector<H5PartWrapper*>{}, false, 2);
285 std::vector<std::array<Vector_t<double, 3>, 2>> fd(2);
286 fd[0] = zeroFdPair();
287 fd[1] = zeroFdPair();
288
289 const double azimuth = 0.0;
290 EXPECT_NO_THROW(ds.dumpSDDS(*bunch, fd, azimuth));
291
292 std::remove((DataSink::diagnosticStemForContainer("unit_test", 2, 0) + ".stat").c_str());
293 std::remove((DataSink::diagnosticStemForContainer("unit_test", 2, 1) + ".stat").c_str());
294 std::remove((OpalData::getInstance()->getInputBasename() + ".lbal").c_str());
295 }
296
297 // When HDF5 is disabled, dumpH5 returns before validating fdext size.
298 TEST_F(MultiContainerPartBunchTest, DataSink_dumpH5_NoThrowWhenHdf5OffEvenIfFdextTooSmall) {
299 ASSERT_FALSE(Options::enableHDF5);
300
301 createParticlesInContainer(0, 4u, 0.1, 0.2);
302
303 DataSink ds(std::vector<H5PartWrapper*>{}, false, 2);
304 std::vector<std::array<Vector_t<double, 3>, 2>> fdTooSmall;
305 fdTooSmall.push_back(zeroFdPair());
306
307 EXPECT_NO_THROW(ds.dumpH5(*bunch, fdTooSmall));
308
309 std::remove((DataSink::diagnosticStemForContainer("unit_test", 2, 0) + ".stat").c_str());
310 std::remove((DataSink::diagnosticStemForContainer("unit_test", 2, 1) + ".stat").c_str());
311 std::remove((OpalData::getInstance()->getInputBasename() + ".lbal").c_str());
312 }
313
314 TEST_F(MultiContainerPartBunchTest, DataSink_dumpH5_ThrowsWhenFdextTooSmallAndHdf5On) {
315 createParticlesInContainer(0, 4u, 0.1, 0.2);
316
317 const bool savedH5 = Options::enableHDF5;
318 Options::enableHDF5 = true;
319
320 static int h5FileCounter = 0;
321 const int tag = ++h5FileCounter;
322 const std::string f0 = std::string("test_mc_h5_c0_") + std::to_string(tag) + ".h5";
323 const std::string f1 = std::string("test_mc_h5_c1_") + std::to_string(tag) + ".h5";
324
325 std::vector<H5PartWrapper*> wrappers;
326 wrappers.push_back(new H5PartWrapperForPT(f0, H5_O_WRONLY));
327 wrappers.push_back(new H5PartWrapperForPT(f1, H5_O_WRONLY));
328
329 {
330 DataSink ds(wrappers, false, 2);
331 std::vector<std::array<Vector_t<double, 3>, 2>> fdTooSmall;
332 fdTooSmall.push_back(zeroFdPair());
333 EXPECT_THROW(ds.dumpH5(*bunch, fdTooSmall), OpalException);
334 }
335
336 for (H5PartWrapper* w : wrappers) {
337 delete w;
338 }
339
340 std::remove(f0.c_str());
341 std::remove(f1.c_str());
342 std::remove((DataSink::diagnosticStemForContainer("unit_test", 2, 0) + ".stat").c_str());
343 std::remove((DataSink::diagnosticStemForContainer("unit_test", 2, 1) + ".stat").c_str());
344 std::remove((OpalData::getInstance()->getInputBasename() + ".lbal").c_str());
345
346 Options::enableHDF5 = savedH5;
347 }
348
349} // namespace
Inform * gmsg
Definition changes.cpp:7
ippl::Vector< T, Dim > Vector_t
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
static std::string diagnosticStemForContainer(const std::string &inputBasename, size_t numContainers, size_t index)
Definition DataSink.cpp:43
void storeInputFn(const std::string &fn)
store opals input filename
Definition OpalData.cpp:561
static OpalData * getInstance()
Definition OpalData.cpp:193
ParticleContainer< T, Dim > ParticleContainer_t
Definition PartBunch.h:56
Container for all per-particle (and per-simulation) fields tracked during OPALX tracking.
void setPredefinedString(Attribute &attr, const std::string &val)
Set predefined string value.
bool enableHDF5
If true HDF5 files are written.
Definition Options.cpp:83