OPALX (Object Oriented Parallel Accelerator Library for Exascal) master (dc2a29eed580)
OPALX
Loading...
Searching...
No Matches
TestAstra1DMagnetoStatic.cpp
Go to the documentation of this file.
1
39#include <gtest/gtest.h>
40
42#include "Fields/Fieldmap.h"
43#include "Ippl.h"
44#include "Physics/Physics.h"
46
47#include <cmath>
48#include <filesystem>
49#include <fstream>
50#include <iomanip>
51#include <string>
52#include <vector>
53
54namespace {
55
56 constexpr const char* kAstra1DMagnetoStaticType = "AstraMagnetoStatic";
57
58 std::string writeAstra1DMagnetoStaticFieldmap(
59 const std::string& path, const std::vector<double>& z_m, const std::vector<double>& bz,
60 int accuracy = 8, bool normalize = true) {
61 EXPECT_EQ(z_m.size(), bz.size());
62
63 std::ofstream f(path);
64
65 f << kAstra1DMagnetoStaticType << " " << accuracy << " " << (normalize ? "TRUE" : "FALSE")
66 << "\n";
67
68 for (std::size_t i = 0; i < z_m.size(); ++i) {
69 f << std::setprecision(16) << z_m[i] << " " << bz[i] << "\n";
70 }
71
72 return path;
73 }
74
75 std::string writeConstantAstra1DMagnetoStaticFieldmap(
76 const std::string& path, double zbegin_m, double zend_m, int nz, double bz,
77 int accuracy = 8, bool normalize = true) {
78 std::vector<double> z(nz);
79 std::vector<double> b(nz, bz);
80
81 const double dz = (zend_m - zbegin_m) / double(nz - 1);
82
83 for (int i = 0; i < nz; ++i) {
84 z[i] = zbegin_m + i * dz;
85 }
86
87 return writeAstra1DMagnetoStaticFieldmap(path, z, b, accuracy, normalize);
88 }
89
90 std::string writeRampAstra1DMagnetoStaticFieldmap(
91 const std::string& path, double zbegin_m, double zend_m, int nz, int accuracy = 8,
92 bool normalize = false) {
93 std::vector<double> z(nz);
94 std::vector<double> b(nz);
95
96 const double dz = (zend_m - zbegin_m) / double(nz - 1);
97
98 for (int i = 0; i < nz; ++i) {
99 z[i] = zbegin_m + i * dz;
100 b[i] = 1.0 + z[i] / zend_m;
101 }
102
103 return writeAstra1DMagnetoStaticFieldmap(path, z, b, accuracy, normalize);
104 }
105
106} // namespace
107
108class Astra1DMagnetoStaticTest : public ::testing::Test {
109protected:
110 static void SetUpTestSuite() {
111 int argc = 0;
112 char** argv = nullptr;
113 ippl::initialize(argc, argv);
114 }
115
116 static void TearDownTestSuite() {
117 Fieldmap::clearDictionary();
118 ippl::finalize();
119 }
120
121 void SetUp() override {
122 tmpDir_ = std::filesystem::temp_directory_path() / "opalx_astra1dmagnetostatic_test";
123
124 std::filesystem::create_directories(tmpDir_);
125 }
126
127 void TearDown() override {
128 Fieldmap::clearDictionary();
129 std::filesystem::remove_all(tmpDir_);
130 }
131
132 std::string tmpFile(const std::string& name) const { return (tmpDir_ / name).string(); }
133
134 std::filesystem::path tmpDir_;
135};
136
137// ===========================================================================
138// Test: Parse fieldmap and verify field dimensions
139// ===========================================================================
140TEST_F(Astra1DMagnetoStaticTest, ParseAndDimensions) {
141 const double zb = 0.0;
142 const double ze = 0.10;
143 const int nz = 5;
144
145 std::string fname =
146 writeConstantAstra1DMagnetoStaticFieldmap(tmpFile("parse.map"), zb, ze, nz, 1.0);
147
148 Fieldmap* fm = Fieldmap::getFieldmap(fname);
149
150 ASSERT_NE(fm, nullptr);
151 EXPECT_EQ(fm->getType(), TAstraMagnetoStatic);
152
153 Fieldmap::readMap(fname);
154
155 double zBegin = 0.0;
156 double zEnd = 0.0;
157
158 fm->getFieldDimensions(zBegin, zEnd);
159
160 EXPECT_NEAR(zBegin, zb, 1e-12);
161 EXPECT_NEAR(zEnd, ze, 1e-12);
162}
163
164// ===========================================================================
165// Test: isInside() helper
166// ===========================================================================
168 std::string fname =
169 writeConstantAstra1DMagnetoStaticFieldmap(tmpFile("inside.map"), 0.0, 0.10, 5, 1.0);
170
171 auto* fm = dynamic_cast<Astra1DMagnetoStatic*>(Fieldmap::getFieldmap(fname));
172
173 ASSERT_NE(fm, nullptr);
174
175 EXPECT_TRUE(fm->isInside({0.0, 0.0, 0.00}));
176 EXPECT_TRUE(fm->isInside({0.0, 0.0, 0.05}));
177 EXPECT_FALSE(fm->isInside({0.0, 0.0, 0.10}));
178 EXPECT_FALSE(fm->isInside({0.0, 0.0, -0.01}));
179}
180
181// ===========================================================================
182// Test: Uniform field with normalization, on-axis Bz
183// ===========================================================================
184TEST_F(Astra1DMagnetoStaticTest, UniformFieldStrengthOnAxis) {
185 const int nz = 9;
186
187 std::string fname = writeConstantAstra1DMagnetoStaticFieldmap(
188 tmpFile("uniform.map"), 0.0, 0.10, nz, 3.0, 8, true);
189
190 Fieldmap* fm = Fieldmap::getFieldmap(fname);
191
192 ASSERT_NE(fm, nullptr);
193
194 Fieldmap::readMap(fname);
195
196 Vector_t<double, 3> R = {0.0, 0.0, 0.05};
197 Vector_t<double, 3> E = {1.0, 2.0, 3.0};
198 Vector_t<double, 3> B = {0.0, 0.0, 0.0};
199
200 bool outside = fm->getFieldstrength(R, E, B);
201
202 EXPECT_FALSE(outside);
203
204 EXPECT_NEAR(E[0], 1.0, 1e-15);
205 EXPECT_NEAR(E[1], 2.0, 1e-15);
206 EXPECT_NEAR(E[2], 3.0, 1e-15);
207
208 EXPECT_NEAR(B[0], 0.0, 1e-10);
209 EXPECT_NEAR(B[1], 0.0, 1e-10);
210 EXPECT_NEAR(B[2], 1.0, 1e-10);
211}
212
213// ===========================================================================
214// Test: Uniform field without normalization
215// ===========================================================================
217 const int nz = 9;
218
219 std::string fname = writeConstantAstra1DMagnetoStaticFieldmap(
220 tmpFile("nonorm.map"), 0.0, 0.10, nz, 3.0, 8, false);
221
222 Fieldmap* fm = Fieldmap::getFieldmap(fname);
223
224 ASSERT_NE(fm, nullptr);
225
226 Fieldmap::readMap(fname);
227
228 Vector_t<double, 3> R = {0.0, 0.0, 0.05};
229 Vector_t<double, 3> E = {0.0, 0.0, 0.0};
230 Vector_t<double, 3> B = {0.0, 0.0, 0.0};
231
232 bool outside = fm->getFieldstrength(R, E, B);
233
234 EXPECT_FALSE(outside);
235
236 EXPECT_NEAR(B[0], 0.0, 1e-10);
237 EXPECT_NEAR(B[1], 0.0, 1e-10);
238 EXPECT_NEAR(B[2], 3.0, 1e-10);
239}
240
241// ===========================================================================
242// Test: Outside z range returns outside flag and does not modify fields
243// ===========================================================================
245 std::string fname =
246 writeConstantAstra1DMagnetoStaticFieldmap(tmpFile("outside.map"), 0.0, 0.10, 9, 1.0);
247
248 Fieldmap* fm = Fieldmap::getFieldmap(fname);
249
250 ASSERT_NE(fm, nullptr);
251
252 Fieldmap::readMap(fname);
253
254 {
255 Vector_t<double, 3> R = {0.0, 0.0, -0.01};
256 Vector_t<double, 3> E = {1.0, 2.0, 3.0};
257 Vector_t<double, 3> B = {4.0, 5.0, 6.0};
258
259 bool outside = fm->getFieldstrength(R, E, B);
260
261 EXPECT_TRUE(outside);
262
263 EXPECT_NEAR(E[0], 1.0, 1e-15);
264 EXPECT_NEAR(E[1], 2.0, 1e-15);
265 EXPECT_NEAR(E[2], 3.0, 1e-15);
266
267 EXPECT_NEAR(B[0], 4.0, 1e-15);
268 EXPECT_NEAR(B[1], 5.0, 1e-15);
269 EXPECT_NEAR(B[2], 6.0, 1e-15);
270 }
271
272 {
273 Vector_t<double, 3> R = {0.0, 0.0, 0.10};
274 Vector_t<double, 3> E = {1.0, 2.0, 3.0};
275 Vector_t<double, 3> B = {4.0, 5.0, 6.0};
276
277 bool outside = fm->getFieldstrength(R, E, B);
278
279 EXPECT_TRUE(outside);
280
281 EXPECT_NEAR(E[0], 1.0, 1e-15);
282 EXPECT_NEAR(E[1], 2.0, 1e-15);
283 EXPECT_NEAR(E[2], 3.0, 1e-15);
284
285 EXPECT_NEAR(B[0], 4.0, 1e-15);
286 EXPECT_NEAR(B[1], 5.0, 1e-15);
287 EXPECT_NEAR(B[2], 6.0, 1e-15);
288 }
289}
290
291// ===========================================================================
292// Test: Accumulation semantics — B is accumulated, not overwritten
293// ===========================================================================
294TEST_F(Astra1DMagnetoStaticTest, FieldAccumulation) {
295 std::string fname =
296 writeConstantAstra1DMagnetoStaticFieldmap(tmpFile("accum.map"), 0.0, 0.10, 9, 1.0);
297
298 Fieldmap* fm = Fieldmap::getFieldmap(fname);
299
300 ASSERT_NE(fm, nullptr);
301
302 Fieldmap::readMap(fname);
303
304 Vector_t<double, 3> R = {0.0, 0.0, 0.05};
305 Vector_t<double, 3> E = {0.0, 0.0, 0.0};
306 Vector_t<double, 3> B = {4.0, 5.0, 6.0};
307
308 fm->getFieldstrength(R, E, B);
309
310 EXPECT_NEAR(B[0], 4.0, 1e-10);
311 EXPECT_NEAR(B[1], 5.0, 1e-10);
312 EXPECT_NEAR(B[2], 7.0, 1e-10);
313}
314
315// ===========================================================================
316// Test: computeField static function directly with a constant coefficient
317// ===========================================================================
318TEST_F(Astra1DMagnetoStaticTest, ComputeFieldStaticConstantCoefficient) {
319 Kokkos::View<double*, Kokkos::HostSpace> coefs("coefs", 3);
320
321 coefs(0) = 1.0;
322 coefs(1) = 0.0;
323 coefs(2) = 0.0;
324
325 Vector_t<double, 3> R = {0.0, 0.0, 0.05};
326 Vector_t<double, 3> E = {0.0, 0.0, 0.0};
327 Vector_t<double, 3> B = {0.0, 0.0, 0.0};
328
329 Astra1DMagnetoStatic::computeField(R, E, B, coefs, 0.0, 0.20, 2);
330
331 EXPECT_NEAR(E[0], 0.0, 1e-12);
332 EXPECT_NEAR(E[1], 0.0, 1e-12);
333 EXPECT_NEAR(E[2], 0.0, 1e-12);
334
335 EXPECT_NEAR(B[0], 0.0, 1e-12);
336 EXPECT_NEAR(B[1], 0.0, 1e-12);
337 EXPECT_NEAR(B[2], 1.0, 1e-12);
338}
339
340// ===========================================================================
341// Test: computeField off-axis with a constant coefficient
342// ===========================================================================
343TEST_F(Astra1DMagnetoStaticTest, ComputeFieldStaticOffAxisConstantCoefficient) {
344 Kokkos::View<double*, Kokkos::HostSpace> coefs("coefs", 3);
345
346 coefs(0) = 2.0;
347 coefs(1) = 0.0;
348 coefs(2) = 0.0;
349
350 Vector_t<double, 3> R = {0.01, 0.02, 0.05};
351 Vector_t<double, 3> E = {0.0, 0.0, 0.0};
352 Vector_t<double, 3> B = {0.0, 0.0, 0.0};
353
354 Astra1DMagnetoStatic::computeField(R, E, B, coefs, 0.0, 0.20, 2);
355
356 EXPECT_NEAR(B[0], 0.0, 1e-12);
357 EXPECT_NEAR(B[1], 0.0, 1e-12);
358 EXPECT_NEAR(B[2], 2.0, 1e-12);
359}
360
361// ===========================================================================
362// Test: computeField off-axis with one Fourier mode
363// ===========================================================================
364TEST_F(Astra1DMagnetoStaticTest, ComputeFieldStaticSingleModeOffAxis) {
365 Kokkos::View<double*, Kokkos::HostSpace> coefs("coefs", 3);
366
367 coefs(0) = 0.0;
368 coefs(1) = 1.0;
369 coefs(2) = 0.0;
370
371 const double zbegin = 0.0;
372 const double length = 0.20;
373 const int accuracy = 2;
374 const double base = Physics::two_pi / length;
375
376 Vector_t<double, 3> R = {0.01, 0.0, 0.05};
377 Vector_t<double, 3> E = {0.0, 0.0, 0.0};
378 Vector_t<double, 3> B = {0.0, 0.0, 0.0};
379
380 Astra1DMagnetoStatic::computeField(R, E, B, coefs, zbegin, length, accuracy);
381
382 const double rr2 = R(0) * R(0) + R(1) * R(1);
383 const double bzp = base;
384 const double bzpp = 0.0;
385 const double bzppp = -base * base * base;
386
387 const double expectedBfieldR = -bzp / 2.0 + bzppp * rr2 / 16.0;
388
389 EXPECT_NEAR(B[0], expectedBfieldR * R(0), 1e-12);
390 EXPECT_NEAR(B[1], 0.0, 1e-12);
391 EXPECT_NEAR(B[2], 0.0 - bzpp * rr2 / 4.0, 1e-12);
392}
393
394// ===========================================================================
395// Test: Non-uniform fieldmap produces different Bz values at different z
396// ===========================================================================
397TEST_F(Astra1DMagnetoStaticTest, NonUniformMapProducesNonUniformField) {
398 std::string fname =
399 writeRampAstra1DMagnetoStaticFieldmap(tmpFile("ramp.map"), 0.0, 0.10, 17, 12, false);
400
401 Fieldmap* fm = Fieldmap::getFieldmap(fname);
402
403 ASSERT_NE(fm, nullptr);
404
405 Fieldmap::readMap(fname);
406
407 Vector_t<double, 3> E1 = {0.0, 0.0, 0.0};
408 Vector_t<double, 3> B1 = {0.0, 0.0, 0.0};
409 Vector_t<double, 3> E2 = {0.0, 0.0, 0.0};
410 Vector_t<double, 3> B2 = {0.0, 0.0, 0.0};
411
412 fm->getFieldstrength({0.0, 0.0, 0.025}, E1, B1);
413 fm->getFieldstrength({0.0, 0.0, 0.075}, E2, B2);
414
415 EXPECT_NE(B1[2], B2[2]);
416}
417
418// ===========================================================================
419// Test: getFieldDerivative current no-op behaviour
420// ===========================================================================
421TEST_F(Astra1DMagnetoStaticTest, FieldDerivativeNoOp) {
422 std::string fname =
423 writeConstantAstra1DMagnetoStaticFieldmap(tmpFile("deriv.map"), 0.0, 0.10, 9, 2.0);
424
425 auto* fm = dynamic_cast<Astra1DMagnetoStatic*>(Fieldmap::getFieldmap(fname));
426
427 ASSERT_NE(fm, nullptr);
428
429 Fieldmap::readMap(fname);
430
431 Vector_t<double, 3> R = {0.0, 0.0, 0.05};
432 Vector_t<double, 3> E = {1.0, 2.0, 3.0};
433 Vector_t<double, 3> B = {4.0, 5.0, 6.0};
434
435 bool outside = fm->getFieldDerivative(R, E, B, DZ);
436
437 EXPECT_FALSE(outside);
438
439 EXPECT_NEAR(E[0], 1.0, 1e-15);
440 EXPECT_NEAR(E[1], 2.0, 1e-15);
441 EXPECT_NEAR(E[2], 3.0, 1e-15);
442
443 EXPECT_NEAR(B[0], 4.0, 1e-15);
444 EXPECT_NEAR(B[1], 5.0, 1e-15);
445 EXPECT_NEAR(B[2], 6.0, 1e-15);
446}
447
448// ===========================================================================
449// Test: getFrequency / setFrequency throw for magnetostatic fieldmap
450// ===========================================================================
452 std::string fname =
453 writeConstantAstra1DMagnetoStaticFieldmap(tmpFile("freq.map"), 0.0, 0.10, 5, 1.0);
454
455 Fieldmap* fm = Fieldmap::getFieldmap(fname);
456
457 ASSERT_NE(fm, nullptr);
458
459 EXPECT_THROW(fm->getFrequency(), GeneralOpalException);
460 EXPECT_THROW(fm->setFrequency(100.0), GeneralOpalException);
461}
462
463// ===========================================================================
464// Test: getFieldDimensions 6-arg overload throws
465// ===========================================================================
466TEST_F(Astra1DMagnetoStaticTest, GetFieldDimensions6ArgThrows) {
467 std::string fname =
468 writeConstantAstra1DMagnetoStaticFieldmap(tmpFile("dim6.map"), 0.0, 0.10, 5, 1.0);
469
470 Fieldmap* fm = Fieldmap::getFieldmap(fname);
471
472 ASSERT_NE(fm, nullptr);
473
474 double xIni = 0.0;
475 double xFinal = 0.0;
476 double yIni = 0.0;
477 double yFinal = 0.0;
478 double zIni = 0.0;
479 double zFinal = 0.0;
480
481 EXPECT_THROW(
482 fm->getFieldDimensions(xIni, xFinal, yIni, yFinal, zIni, zFinal), GeneralOpalException);
483}
484
485// ===========================================================================
486// Test: swap() does not crash
487// ===========================================================================
489 std::string fname =
490 writeConstantAstra1DMagnetoStaticFieldmap(tmpFile("swap.map"), 0.0, 0.10, 5, 1.0);
491
492 auto* fm = dynamic_cast<Astra1DMagnetoStatic*>(Fieldmap::getFieldmap(fname));
493
494 ASSERT_NE(fm, nullptr);
495
496 EXPECT_NO_THROW(fm->swap());
497}
498
499// ===========================================================================
500// Test: getInfo() does not crash
501// ===========================================================================
503 std::string fname =
504 writeConstantAstra1DMagnetoStaticFieldmap(tmpFile("info.map"), 0.0, 0.10, 5, 1.0);
505
506 Fieldmap* fm = Fieldmap::getFieldmap(fname);
507
508 ASSERT_NE(fm, nullptr);
509
510 Inform msg("test");
511
512 EXPECT_NO_THROW(fm->getInfo(&msg));
513}
514
515// ===========================================================================
516// Test: Missing file
517// ===========================================================================
519 std::string fname = tmpFile("nonexistent.map");
520
521 EXPECT_THROW(Fieldmap::getFieldmap(fname), GeneralOpalException);
522}
523
524// ===========================================================================
525// Test: Fieldmap dictionary caching
526// ===========================================================================
527TEST_F(Astra1DMagnetoStaticTest, DictionaryCaching) {
528 std::string fname =
529 writeConstantAstra1DMagnetoStaticFieldmap(tmpFile("cache.map"), 0.0, 0.10, 5, 1.0);
530
531 Fieldmap* fm1 = Fieldmap::getFieldmap(fname);
532 Fieldmap* fm2 = Fieldmap::getFieldmap(fname);
533
534 EXPECT_EQ(fm1, fm2);
535}
536
537// ===========================================================================
538// Test: readMap / freeMap cycle
539// ===========================================================================
541 std::string fname =
542 writeConstantAstra1DMagnetoStaticFieldmap(tmpFile("cycle.map"), 0.0, 0.10, 9, 1.0);
543
544 Fieldmap* fm = Fieldmap::getFieldmap(fname);
545
546 ASSERT_NE(fm, nullptr);
547
548 fm->readMap();
549
550 Vector_t<double, 3> R = {0.0, 0.0, 0.05};
551 Vector_t<double, 3> E = {0.0, 0.0, 0.0};
552 Vector_t<double, 3> B = {0.0, 0.0, 0.0};
553
554 fm->getFieldstrength(R, E, B);
555
556 EXPECT_NEAR(B[2], 1.0, 1e-10);
557
558 fm->freeMap();
559 fm->readMap();
560
561 B = {0.0, 0.0, 0.0};
562
563 fm->getFieldstrength(R, E, B);
564
565 EXPECT_NEAR(B[2], 1.0, 1e-10);
566}
567
568// ===========================================================================
569// Test: Invalid fieldmap with fewer than two valid samples
570// ===========================================================================
571TEST_F(Astra1DMagnetoStaticTest, RejectsTooFewSamples) {
572 const std::string fname = tmpFile("too_few.map");
573
574 {
575 std::ofstream f(fname);
576 f << kAstra1DMagnetoStaticType << " 8 TRUE\n";
577 f << "0.0 1.0\n";
578 }
579
580 EXPECT_THROW(Fieldmap::getFieldmap(fname), GeneralOpalException);
581}
582
583// ===========================================================================
584// Test: Zero-amplitude map is rejected during readMap()
585// ===========================================================================
586TEST_F(Astra1DMagnetoStaticTest, RejectsZeroAmplitudeMap) {
587 std::string fname =
588 writeConstantAstra1DMagnetoStaticFieldmap(tmpFile("zero.map"), 0.0, 0.10, 5, 0.0);
589
590 Fieldmap* fm = Fieldmap::getFieldmap(fname);
591
592 ASSERT_NE(fm, nullptr);
593
594 EXPECT_THROW(Fieldmap::readMap(fname), GeneralOpalException);
595}
ippl::Vector< T, Dim > Vector_t
@ TAstraMagnetoStatic
Definition Fieldmap.h:21
@ DZ
Definition Fieldmap.h:54
TEST_F(Astra1DMagnetoStaticTest, ParseAndDimensions)
std::string tmpFile(const std::string &name) const
Abstract base class for all field maps. It acts as a factory for creating specific field map types ba...
Definition Fieldmap.h:62
virtual bool getFieldstrength(const Vector_t< double, 3 > &R, Vector_t< double, 3 > &E, Vector_t< double, 3 > &B) const =0
Get the field strength at a given point.
virtual void getInfo(Inform *msg)=0
Print info about the field map.
virtual void getFieldDimensions(double &zBegin, double &zEnd) const =0
Get the longitudinal dimensions of the field.
MapType getType()
Definition Fieldmap.h:206
virtual void setFrequency(double freq)=0
Set the frequency.
virtual double getFrequency() const =0
Get the frequency.
static void freeMap(std::string Filename)
Decrease reference count or delete field map if unused.
Definition Fieldmap.cpp:311
static void readMap(std::string Filename)
Trigger the actual reading of the field map data.
Definition Fieldmap.cpp:301
constexpr double two_pi
The value of.
Definition Physics.h:40