OPALX (Object Oriented Parallel Accelerator Library for Exascal) master (dc2a29eed580)
OPALX
Loading...
Searching...
No Matches
TestMultipoleT.cpp
Go to the documentation of this file.
1//
2// Tests for MultipoleTBase
3//
4// Copyright (c) 2025, Jon Thompson, STFC Rutherford Appleton Laboratory, Didcot, UK
5//
6// This file is part of OPAL.
7//
8// OPAL is free software: you can redistribute it and/or modify
9// it under the terms of the GNU General Public License as published by
10// the Free Software Foundation, either version 3 of the License, or
11// (at your option) any later version.
12//
13// You should have received a copy of the GNU General Public License
14// along with OPAL. If not, see <https://www.gnu.org/licenses/>.
15//
16
17#include <csignal>
23#include "Structure/Beam.h"
24#include "Structure/DataSink.h"
27#include "Utilities/Options.h"
28#include "Utility/Inform.h"
29#include "gtest/gtest.h"
30
31class TestMultipoleT : public testing::Test, public MultipoleT, public BeamlineVisitor {
32public:
33 TestMultipoleT() : MultipoleT("Magnet") {}
34
36 public:
37 void setType(const std::string& t) {
39 }
40
41 void setBCX(const std::string& bc) {
43 }
44 void setBCY(const std::string& bc) {
46 }
47 void setBCZ(const std::string& bc) {
49 }
50 };
51
52 static std::shared_ptr<FieldSolverCmd> makeNoFieldSolver() {
53 const auto fsCmd = std::make_shared<TestableFieldSolverCmd>();
54 fsCmd->setType("NONE");
55 fsCmd->setNX(8);
56 fsCmd->setNY(8);
57 fsCmd->setNZ(8);
58 fsCmd->setBCX("PERIODIC");
59 fsCmd->setBCY("PERIODIC");
60 fsCmd->setBCZ("PERIODIC");
61 return fsCmd;
62 }
63
64 static void SetUpTestSuite() {
65 int argc = 0;
66 char** argv = nullptr;
67 ippl::initialize(argc, argv);
68 OpalData::getInstance()->storeInputFn("unit_test.opal");
69 if (gmsg == nullptr) {
70 gmsg = new Inform(nullptr, -1);
71 }
72 Options::enableHDF5 = false;
73 }
74 static void TearDownTestSuite() {
75 if (gmsg != nullptr) {
76 delete gmsg;
77 gmsg = nullptr;
78 }
79 ippl::finalize();
80 }
81
82 // Overrides of BeamlineVisitor
83 void execute() override {}
84 void visitBeamline(const Beamline&) override {}
85 void visitComponent(const Component&) override {}
87 void visitDrift(const Drift&) override {}
88 void visitFlaggedElmPtr(const FlaggedElmPtr&) override {}
89 void visitLaser(const Laser&) override {}
90 void visitMarker(const Marker&) override {}
91 void visitMonitor(const Monitor&) override {}
92 void visitMultipole(const Multipole&) override {}
93 void visitMultipoleT(const MultipoleT&) override {}
94 void visitRBend(const RBend&) override {}
95 void visitRFCavity(const RFCavity&) override {}
96 void visitScalingFFAMagnet(const ScalingFFAMagnet&) override {}
97 void visitRing(const Ring&) override {}
98 void visitSBend(const SBend&) override {}
99 void visitSolenoid(const Solenoid&) override {}
100 void visitTravelingWave(const TravelingWave&) override {}
102 void visitProbe(const Probe&) override {}
104
105 // Test helper functions
106 double fieldAtT(const Vector_t<double, 3>& pos, const double t) {
107 Vector_t<double, 3> E{}, B{};
108 apply(pos, {}, t, E, B);
109 return std::hypot(B[0], B[1], B[2]);
110 }
111};
112
113// Does the clone API return an object with the same configuration
115 // Set up the magnet
116 setBendAngle(1, false);
117 setElementLength(4);
118 setAperture(3, 2);
119 setFringeField(2, 1, 2);
120 setRotation(0.4);
121 setEntranceAngle(1);
122 setMaxOrder(5, 10);
123 setTransProfile({1, 2, 3});
124 setBoundingBoxLength(7);
125 setEntryOffset(3);
126 setScalingName("Scaling");
127 // Does the clone have the same configuration
128 std::unique_ptr<MultipoleT> theClone;
129 theClone.reset(dynamic_cast<MultipoleT*>(clone()));
130 EXPECT_NE(theClone.get(), nullptr);
131 EXPECT_EQ(theClone->getMaxFOrder(), getMaxFOrder());
132 EXPECT_EQ(theClone->getMaxXOrder(), getMaxXOrder());
133 EXPECT_EQ(theClone->getTransMaxOrder(), getTransMaxOrder());
134 EXPECT_EQ(theClone->getTransProfile(), getTransProfile());
135 EXPECT_EQ(theClone->getFringeField(), getFringeField());
136 EXPECT_EQ(theClone->getEntryOffset(), getEntryOffset());
137 EXPECT_EQ(theClone->getVariableRadius(), getVariableRadius());
138 EXPECT_EQ(theClone->getBendAngle(), getBendAngle());
139 EXPECT_EQ(theClone->getEntranceAngle(), getEntranceAngle());
140 EXPECT_EQ(theClone->getLength(), getLength());
141 EXPECT_EQ(theClone->getAperture(), getAperture());
142 EXPECT_EQ(theClone->getRotation(), getRotation());
143 EXPECT_EQ(theClone->getBoundingBoxLength(), getBoundingBoxLength());
144 EXPECT_EQ(theClone->getScalingName(), getScalingName());
145 EXPECT_EQ(theClone->getEntryOffset(), getEntryOffset());
146}
147
148// Attach a time dependency object and check it affects the field output appropriately
149TEST_F(TestMultipoleT, TimeDependency) {
150 // Set up a time dependency
151 const auto timeDependence = std::make_shared<SplineTimeDependence>(
152 1, std::vector<double>{0, 1, 1.01, 2}, std::vector<double>{1, 1, 2, 2});
153 AbstractTimeDependence::setTimeDependence("STEPFUNCTION", timeDependence);
154 // Set up the magnet
155 setBendAngle(0, false);
156 setElementLength(4);
157 setAperture(0.3, 0.3);
158 setFringeField(2, 0.2, 0.2);
159 setMaxOrder(5, 10);
160 setTransProfile({1});
161 setScalingName("STEPFUNCTION");
162 // This initialises the time dependency
163 accept(*this);
164 // Test the field at the magnet center at various times
165 EXPECT_NEAR(fieldAtT({0.0, 0.0, 2.0}, 0.0), 1.0, 1e-6);
166 EXPECT_NEAR(fieldAtT({0.0, 0.0, 2.0}, 1.0), 1.0, 1e-6);
167 EXPECT_NEAR(fieldAtT({0.0, 0.0, 2.0}, 1.2), 2.0, 1e-6);
168 EXPECT_NEAR(fieldAtT({0.0, 0.0, 2.0}, 2.0), 2.0, 1e-6);
169 // Now clear the time dependence
170 setScalingName("");
171 // This initialises the time dependency
172 accept(*this);
173 // Test the field at the magnet center at various times
174 EXPECT_NEAR(fieldAtT({0.0, 0.0, 2.0}, 0.0), 1.0, 1e-6);
175 EXPECT_NEAR(fieldAtT({0.0, 0.0, 2.0}, 1.0), 1.0, 1e-6);
176 EXPECT_NEAR(fieldAtT({0.0, 0.0, 2.0}, 1.2), 1.0, 1e-6);
177 EXPECT_NEAR(fieldAtT({0.0, 0.0, 2.0}, 2.0), 1.0, 1e-6);
178}
179
180// Does the bends API return the correct value
182 setBendAngle(0, false);
183 EXPECT_FALSE(bends());
184 setBendAngle(1, false);
185 EXPECT_TRUE(bends());
186}
187
188// Check that an exception if thrown for configuration that is not supported.
189TEST_F(TestMultipoleT, ConfigurationValidation) {
190 // Set up the magnet
191 setBendAngle(0, false);
192 setElementLength(4);
193 setAperture(0.3, 0.3);
194 setFringeField(2, 0.2, 0.2);
195 setTransProfile({1});
196 // Illegal F order
197 setMaxOrder(10, 10);
198 EXPECT_THROW(fieldAtT({0.0, 0.0, 2.0}, 0.0), OpalException);
199}
200
201// Tests for the few remaining API functions are collected here
203 // The field object API which currently always returns a dummy object
204 MultipoleT* magnet = this;
205 auto* field = &magnet->getField();
206 EXPECT_NE(field, nullptr);
207 auto* constField = &const_cast<const MultipoleT*>(magnet)->getField();
208 EXPECT_NE(constField, nullptr);
209 EXPECT_EQ(field, constField);
210 // The field-support interval follows the body length.
211 EXPECT_NO_THROW(finalise());
212 setElementLength(4.0);
213 double a = -1.0, b = -1.0;
214 EXPECT_NO_THROW(getFieldExtend(a, b));
215 EXPECT_DOUBLE_EQ(a, 0.0);
216 EXPECT_DOUBLE_EQ(b, 4.0);
217}
218
219TEST_F(TestMultipoleT, ApplySingleParticleThrowsForMultiContainerBunch) {
220 // Configure a simple straight multipole.
221 setBendAngle(0.0, false);
222 setElementLength(1.0);
223 setAperture(1.0, 1.0);
224 setFringeField(0.5, 0.1, 0.1);
225 setTransProfile({1.0});
226
227 // Build a multi-container bunch.
228 const auto dataSink = std::make_shared<DataSink>();
229 const auto fsCmd = makeNoFieldSolver();
230 const auto beam = std::make_shared<Beam>();
231 Beam* testBeam = Beam::find("UNNAMED_BEAM");
232 ASSERT_NE(testBeam, nullptr);
233 const auto bunch = std::make_shared<PartBunch_t>(
234 std::vector<double>{1.0e-9, 2.0e-9}, std::vector<double>{0.511e-3, 0.938},
235 std::vector<Beam*>{testBeam, testBeam}, std::vector<size_t>{1, 1}, 1.0, "LF2",
236 fsCmd.get(), dataSink.get());
237 ASSERT_EQ(bunch->getNumParticleContainers(), 2u);
238
239 // Register bunch and verify per-particle apply() rejects ambiguous container context.
240 double startField = 0.0;
241 double endField = 0.0;
242 initialise(bunch.get(), startField, endField);
243 Vector_t<double, 3> E{}, B{};
244 EXPECT_THROW(apply(0, 0.0, E, B), OpalException);
245}
Inform * gmsg
Definition changes.cpp:7
ippl::Vector< T, Dim > Vector_t
TEST_F(TestMultipoleT, Cloning)
static void setTimeDependence(const std::string &name, std::shared_ptr< AbstractTimeDependence > time_dep)
Definition Beam.h:32
static Beam * find(const std::string &name)
Find named BEAM.
Definition Beam.cpp:290
An abstract sequence of beam line components.
Definition Beamline.h:34
Component applying a constant accelerating electric field (Ex,Ey,Ez).
Interface for drift space.
Definition Drift.h:31
A section of a beam line.
Passive OPALX laser element.
Definition Laser.h:25
Interface for a marker.
Definition Marker.h:31
EMField & getField() override
Definition MultipoleT.h:91
bool apply(const std::shared_ptr< ParticleContainer_t > &pc) override
Interface for general multipole.
Definition Multipole.h:30
std::vector< Attribute > itsAttr
The object attributes.
Definition Object.h:210
void storeInputFn(const std::string &fn)
store opals input filename
Definition OpalData.cpp:561
static OpalData * getInstance()
Definition OpalData.cpp:193
Definition Probe.h:28
Abstract rectangular bend with straight body and curved reference path.
Definition RBend.h:16
Interface for standing wave cavities.
Definition RFCavity.h:34
Ring describes a ring type geometry for tracking.
Definition Ring.h:62
Abstract sector bend with planar-arc body geometry.
Definition SBend.h:15
Abstract class for a solenoid magnet.
Definition Solenoid.h:33
void setBCY(const std::string &bc)
void setBCZ(const std::string &bc)
void setBCX(const std::string &bc)
void visitRBend(const RBend &) override
Apply the algorithm to a rectangular bend.
void visitProbe(const Probe &) override
Apply the algorithm to a Probe.
void visitMultipole(const Multipole &) override
Apply the algorithm to a multipole.
void visitRFCavity(const RFCavity &) override
Apply the algorithm to a RF cavity.
void visitSBend(const SBend &) override
Apply the algorithm to a sector bend.
double fieldAtT(const Vector_t< double, 3 > &pos, const double t)
static void TearDownTestSuite()
static void SetUpTestSuite()
void visitRing(const Ring &) override
Apply the algorithm to a Ring element.
void visitMarker(const Marker &) override
Apply the algorithm to a marker.
void visitComponent(const Component &) override
Apply the algorithm to an arbitrary component.
void visitMonitor(const Monitor &) override
Apply the algorithm to a beam position monitor.
void visitDrift(const Drift &) override
Apply the algorithm to a drift space.
void visitSolenoid(const Solenoid &) override
Apply the algorithm to a Solenoid element.
void visitConstantEFieldCavity(const ConstantEFieldCavity &) override
Apply the algorithm to a constant E-field cavity element.
void visitLaser(const Laser &) override
Apply the algorithm to a laser element.
void visitScalingFFAMagnet(const ScalingFFAMagnet &) override
static std::shared_ptr< FieldSolverCmd > makeNoFieldSolver()
void visitVariableRFCavity(const VariableRFCavity &) override
Apply the algorithm to a variable RF cavity.
void visitTravelingWave(const TravelingWave &) override
Apply the algorithm to a traveling wave.
void execute() override
Execute the algorithm on the attached beam line.
void visitBeamline(const Beamline &) override
Apply the algorithm to a beam line.
void visitFlaggedElmPtr(const FlaggedElmPtr &) override
Apply the algorithm to a FlaggedElmPtr.
void visitMultipoleT(const MultipoleT &) override
Apply the algorithm to an arbitrary multipole.
void visitVerticalFFAMagnet(const VerticalFFAMagnet &) override
Apply the algorithm to a vertical FFA magnet.
Interface for traveling wave cavities.
void setPredefinedString(Attribute &attr, const std::string &val)
Set predefined string value.
bool enableHDF5
If true HDF5 files are written.
Definition Options.cpp:83