OPALX (Object Oriented Parallel Accelerator Library for Exascal) master (dc2a29eed580)
OPALX
Loading...
Searching...
No Matches
TestSolenoid.cpp
Go to the documentation of this file.
11#include "Beamlines/Beamline.h"
13#include "Fields/Fieldmap.h"
14#include "Physics/Units.h"
15#include "Structure/Beam.h"
16#include "Structure/DataSink.h"
19
20#include "gtest/gtest.h"
21
22#include <filesystem>
23#include <fstream>
24#include <sstream>
25#include <string>
26#include <vector>
27
28class SolenoidPlacementTest : public ::testing::Test {
29protected:
30 static void SetUpTestSuite() {
31 int argc = 0;
32 char** argv = nullptr;
33 ippl::initialize(argc, argv);
34 gmsg = new Inform(nullptr, -1);
35 Options::enableHDF5 = false;
36 std::filesystem::create_directories("data");
37 }
38
39 static void TearDownTestSuite() {
40 delete gmsg;
41 gmsg = nullptr;
42 ippl::finalize();
43 }
44
45 void SetUp() override {
46 const auto* info = ::testing::UnitTest::GetInstance()->current_test_info();
47 testStem_ = std::string("solenoid_") + info->name();
51 }
52
53 void TearDown() override {
54 Fieldmap::freeMap(fieldmapFile_.string());
56 }
57
59 public:
60 void setType(const std::string& t) {
62 }
63
64 void setBCX(const std::string& bc) {
66 }
67
68 void setBCY(const std::string& bc) {
70 }
71
72 void setBCZ(const std::string& bc) {
74 }
75 };
76
77 std::shared_ptr<PartBunch_t> makeBunch(const size_t numParticles) {
78 dataSink_m = std::make_shared<DataSink>();
79 const auto fsCmd = std::make_shared<TestableFieldSolverCmd>();
80 fsCmdBase_m = fsCmd;
81 fsCmd->setType("NONE");
82 fsCmd->setNX(8);
83 fsCmd->setNY(8);
84 fsCmd->setNZ(8);
85 fsCmd->setBCX("PERIODIC");
86 fsCmd->setBCY("PERIODIC");
87 fsCmd->setBCZ("PERIODIC");
88
89 auto beam = std::make_shared<Beam>();
90 Beam* opBeam = Beam::find("UNNAMED_BEAM");
91 EXPECT_NE(opBeam, nullptr);
92
93 auto bunch = std::make_shared<PartBunch_t>(
94 /*qi=*/std::vector{1.0}, /*mi=*/std::vector{1.0},
95 /*beams=*/std::vector<Beam*>{opBeam},
96 /*totalParticlesPerBeam=*/std::vector<size_t>{numParticles},
97 /*lbt=*/1.0, /*integration_method=*/"LF2", fsCmdBase_m.get(), dataSink_m.get());
98 bunch->getParticleContainer()->createParticles(numParticles);
99 return bunch;
100 }
101
102 std::filesystem::path writeXZFieldmap(
103 const std::string& filename, double zBeginCm, double zEndCm, int nz, double rBeginCm,
104 double rEndCm, int nr, double bzValue = 1.0, double brValue = 0.0) {
105 fieldmapFile_ = std::filesystem::path("data") / filename;
106 std::ofstream out(fieldmapFile_);
107 out << "2DMagnetoStatic XZ\n";
108 out << zBeginCm << " " << zEndCm << " " << nz - 1 << "\n";
109 out << rBeginCm << " " << rEndCm << " " << nr - 1 << "\n";
110 for (int ir = 0; ir < nr; ++ir) {
111 for (int iz = 0; iz < nz; ++iz) {
112 out << bzValue << " " << brValue << "\n";
113 }
114 }
115 return fieldmapFile_;
116 }
117
118 std::filesystem::path outputPath(const std::string& suffix) const {
119 return std::filesystem::path("data") / (testStem_ + suffix);
120 }
121
122 void cleanupOutputs() const {
123 std::filesystem::remove(outputPath("_ElementPositions.txt"));
124 std::filesystem::remove(outputPath("_ElementPositions.py"));
125 std::filesystem::remove(outputPath("_ElementPositions.sdds"));
126 std::filesystem::remove(outputPath("_ElementPositions.vtk"));
127 std::filesystem::remove(outputPath("_ElementPositions.html"));
128 std::filesystem::remove(outputPath("_ElementPositions.gpl"));
129 if (!fieldmapFile_.empty()) {
130 std::filesystem::remove(fieldmapFile_);
131 }
132 }
133
134 static std::tuple<double, double, double> parsePositionLine(const std::string& line) {
135 const auto quote = line.rfind('"');
136 EXPECT_NE(quote, std::string::npos);
137 std::istringstream values(line.substr(quote + 1));
138 double z = 0.0, x = 0.0, y = 0.0;
139 values >> z >> x >> y;
140 return {z, x, y};
141 }
142
143 static std::vector<std::string> splitWhitespace(const std::string& line) {
144 std::vector<std::string> tokens;
145 std::istringstream in(line);
146 std::string token;
147 while (in >> token) {
148 tokens.push_back(token);
149 }
150 return tokens;
151 }
152
153 std::string testStem_;
154 std::filesystem::path fieldmapFile_;
155 std::shared_ptr<FieldSolverCmd> fsCmdBase_m;
156 std::shared_ptr<DataSink> dataSink_m;
157};
158
159class DummyBeamline final : public Beamline {
160public:
161 DummyBeamline() : Beamline("dummy") {}
162
163 ElementType getType() const override { return ElementType::BEAMLINE; }
164 BGeometryBase& getGeometry() override { return geometry_; }
165 const BGeometryBase& getGeometry() const override { return geometry_; }
166 void accept(BeamlineVisitor& visitor) const override { visitor.visitBeamline(*this); }
167 ElementBase* clone() const override { return new DummyBeamline(*this); }
168 void iterate(BeamlineVisitor&, bool) const override {}
169
170private:
172};
173
174TEST_F(SolenoidPlacementTest, FieldMapEdgesAndSupportEnvelopeFollowFieldMap) {
175 const auto mapFile = writeXZFieldmap("solenoid_edges.map", -5.0, 15.0, 4, 0.0, 3.0, 3);
176
177 SolenoidRep solenoid("SOL1");
178 solenoid.setElementLength(1.0);
179 solenoid.setFieldMapFN(mapFile.string());
180 solenoid.setElementPosition(1.0);
181
182 double startField = 1.0;
183 double endField = 0.0;
184 try {
185 solenoid.initialise(nullptr, startField, endField);
186 } catch (const OpalException& ex) {
187 FAIL() << ex.where() << ": " << ex.what();
188 } catch (...) {
189 FAIL() << "non-OPAL exception";
190 }
191
192 double fieldBegin = 0.0, fieldEnd = 0.0;
193 solenoid.getFieldExtend(fieldBegin, fieldEnd);
194
195 double bodyBegin = 0.0, bodyEnd = 0.0;
196 solenoid.getElementDimensions(bodyBegin, bodyEnd);
197
198 EXPECT_NEAR(startField, 0.95, 1e-12);
199 EXPECT_NEAR(endField, 1.15, 1e-12);
200 EXPECT_NEAR(solenoid.getElementLength(), 1.0, 1e-12);
201 EXPECT_NEAR(fieldBegin, -0.05, 1e-12);
202 EXPECT_NEAR(fieldEnd, 0.15, 1e-12);
203 EXPECT_NEAR(bodyBegin, 0.0, 1e-12);
204 EXPECT_NEAR(bodyEnd, 1.0, 1e-12);
205 EXPECT_NEAR(solenoid.getEdgeToBegin().getOrigin()(2), 0.0, 1e-12);
206 EXPECT_NEAR(solenoid.getEdgeToEnd().getOrigin()(2), 1.0, 1e-12);
207 EXPECT_TRUE(solenoid.isInside({0.0, 0.0, 0.00}));
208 EXPECT_TRUE(solenoid.isInside({0.0, 0.0, 0.14}));
209 EXPECT_FALSE(solenoid.isInside({0.0, 0.0, 0.20}));
210
211 double horizontalRadius = 0.0;
212 double verticalRadius = 0.0;
213 ASSERT_NO_THROW({
214 const bool hasSupport = solenoid.getSupportEnvelope(horizontalRadius, verticalRadius);
215 ASSERT_TRUE(hasSupport);
216 });
217 EXPECT_NEAR(horizontalRadius, 0.03, 1e-12);
218 EXPECT_NEAR(verticalRadius, 0.03, 1e-12);
219}
220
221TEST_F(SolenoidPlacementTest, LatticeExportsUseFieldMapEdgesAndSolenoidMeshType) {
222 const auto mapFile = writeXZFieldmap("solenoid_lattice.map", -5.0, 15.0, 4, 0.0, 3.0, 3);
223
224 SolenoidRep solenoid("SOL1");
225 solenoid.setElementLength(1.0);
226 solenoid.setFieldMapFN(mapFile.string());
227 solenoid.setElementPosition(1.0);
228
229 auto bunch = makeBunch(0);
230 DummyBeamline beamlineForVisitor;
231 DefaultVisitor visitor(beamlineForVisitor, false, false);
232 OpalBeamline beamline;
233 try {
234 beamline.visit(solenoid, visitor, *bunch);
235 } catch (const OpalException& ex) {
236 FAIL() << ex.where() << ": " << ex.what();
237 } catch (...) {
238 FAIL() << "non-OPAL exception";
239 }
240 ASSERT_NO_THROW(beamline.compute3DLattice());
241
242 const auto elements = beamline.getElements();
243 ASSERT_EQ(elements.size(), 1u);
244 const auto& placedComponent = *elements.begin();
245 const PlacedElement placed = beamline.getPlacedElement(placedComponent);
246 EXPECT_NEAR(placed.getNominalEntryTransform().getOrigin()(2), 1.0, 1e-12);
247 EXPECT_NEAR(placed.getNominalExitTransform().getOrigin()(2), 2.0, 1e-12);
248 EXPECT_EQ(beamline.getElements(Vector_t<double, 3>(0.0, 0.0, 1.10)).size(), 1u);
249 EXPECT_TRUE(beamline.getElements(Vector_t<double, 3>(0.0, 0.0, 1.30)).empty());
250
251 ASSERT_NO_THROW(beamline.save3DLattice());
252
253 std::ifstream txt(outputPath("_ElementPositions.txt"));
254 ASSERT_TRUE(txt.is_open());
255
256 std::string textLine;
257 bool foundBegin = false;
258 bool foundEnd = false;
259 while (std::getline(txt, textLine)) {
260 if (textLine.find("\"BEGIN: SOL1\"") != std::string::npos) {
261 const auto [z, x, y] = parsePositionLine(textLine);
262 const auto entry = placed.getNominalEntryTransform().getOrigin();
263 EXPECT_NEAR(z, entry(2), 1e-12);
264 EXPECT_NEAR(x, entry(0), 1e-12);
265 EXPECT_NEAR(y, entry(1), 1e-12);
266 foundBegin = true;
267 } else if (textLine.find("\"END: SOL1\"") != std::string::npos) {
268 const auto [z, x, y] = parsePositionLine(textLine);
269 const auto exit = placed.getNominalExitTransform().getOrigin();
270 EXPECT_NEAR(z, exit(2), 1e-12);
271 EXPECT_NEAR(x, exit(0), 1e-12);
272 EXPECT_NEAR(y, exit(1), 1e-12);
273 foundEnd = true;
274 }
275 }
276 EXPECT_TRUE(foundBegin);
277 EXPECT_TRUE(foundEnd);
278
279 std::ifstream py(outputPath("_ElementPositions.py"));
280 ASSERT_TRUE(py.is_open());
281 const std::string script(
282 (std::istreambuf_iterator<char>(py)), std::istreambuf_iterator<char>());
283 EXPECT_NE(script.find("color = [5]"), std::string::npos);
284 EXPECT_NE(script.find("numVertices = [432]"), std::string::npos);
285 EXPECT_NE(
286 script.find("os.path.getmtime(script_file) > os.path.getmtime(vtk_file)"),
287 std::string::npos);
288}
289
290TEST_F(SolenoidPlacementTest, ElementPositionsSDDSMarksSolenoidColumn) {
291 auto solenoid = std::make_shared<SolenoidRep>("SOL1");
292
293 IndexMap map;
295 elements.insert(solenoid);
296 map.add(1.0, 2.0, elements);
297 map.saveSDDS(0.0);
298
299 std::ifstream sdds(outputPath("_ElementPositions.sdds"));
300 ASSERT_TRUE(sdds.is_open());
301
302 std::string line;
303 bool foundSolenoidRow = false;
304 while (std::getline(sdds, line)) {
305 if (line.find("\"SOL1\"") == std::string::npos) {
306 continue;
307 }
308
309 const auto tokens = splitWhitespace(line);
310 ASSERT_GE(tokens.size(), 12u);
311 EXPECT_EQ(tokens[7], "1");
312 EXPECT_EQ(tokens[10], "0");
313 foundSolenoidRow = true;
314 }
315 EXPECT_TRUE(foundSolenoidRow);
316}
317
318TEST_F(SolenoidPlacementTest, DriftMeshesAsBlueCylinderUsingFirstNonDriftReference) {
319 const auto mapFile = writeXZFieldmap("solenoid_drift_mesh.map", -5.0, 15.0, 4, 0.0, 3.0, 3);
320
321 DriftRep drift("DR1");
322 drift.setElementLength(0.5);
323 drift.setElementPosition(0.0);
324
325 SolenoidRep solenoid("SOL1");
326 solenoid.setElementLength(1.0);
327 solenoid.setFieldMapFN(mapFile.string());
328 solenoid.setElementPosition(0.5);
329
330 auto bunch = makeBunch(0);
331 DummyBeamline beamlineForVisitor;
332 DefaultVisitor visitor(beamlineForVisitor, false, false);
333 OpalBeamline beamline;
334 beamline.visit(drift, visitor, *bunch);
335 beamline.visit(solenoid, visitor, *bunch);
336 beamline.compute3DLattice();
337 beamline.save3DLattice();
338
339 std::ifstream py(outputPath("_ElementPositions.py"));
340 ASSERT_TRUE(py.is_open());
341 const std::string script(
342 (std::istreambuf_iterator<char>(py)), std::istreambuf_iterator<char>());
343 EXPECT_NE(script.find("color = [8, 5]"), std::string::npos);
344 EXPECT_NE(script.find("numVertices = [144, 432]"), std::string::npos);
345}
346
347TEST_F(SolenoidPlacementTest, QuadrupoleMeshesAsPoleBodyRatherThanGenericCylinder) {
348 MultipoleRep quadrupole("Q1");
349 quadrupole.setElementLength(0.4);
350 quadrupole.setElementPosition(0.0);
351 quadrupole.setAperture(ApertureType::ELLIPTICAL, std::vector<double>{0.02, 0.03, 1.0});
352 quadrupole.setNormalComponent(1, 1.0);
353
354 auto bunch = makeBunch(0);
355 DummyBeamline beamlineForVisitor;
356 DefaultVisitor visitor(beamlineForVisitor, false, false);
357 OpalBeamline beamline;
358 beamline.visit(quadrupole, visitor, *bunch);
359 beamline.compute3DLattice();
360 beamline.save3DLattice();
361
362 std::ifstream py(outputPath("_ElementPositions.py"));
363 ASSERT_TRUE(py.is_open());
364 const std::string script(
365 (std::istreambuf_iterator<char>(py)), std::istreambuf_iterator<char>());
366 EXPECT_NE(script.find("color = [2]"), std::string::npos);
367 EXPECT_NE(script.find("numVertices = [32]"), std::string::npos);
368}
369
370TEST_F(SolenoidPlacementTest, RFCavityMeshesAsBulgedCellStructure) {
371 RFCavityRep cavity("RFC1");
372 cavity.setElementLength(0.7);
373 cavity.setElementPosition(0.0);
374 cavity.setAperture(ApertureType::ELLIPTICAL, std::vector<double>{0.02, 0.03, 1.0});
375
376 MeshGenerator mesh;
377 mesh.add(cavity);
378 mesh.write(testStem_);
379
380 std::ifstream py(outputPath("_ElementPositions.py"));
381 ASSERT_TRUE(py.is_open());
382 const std::string script(
383 (std::istreambuf_iterator<char>(py)), std::istreambuf_iterator<char>());
384 EXPECT_NE(script.find("color = [6]"), std::string::npos);
385 EXPECT_NE(script.find("numVertices = [1008]"), std::string::npos);
386}
387
388TEST_F(SolenoidPlacementTest, TravelingWaveMeshesAsPeriodicStructure) {
389 TravelingWaveRep travelingWave("TW1");
390 travelingWave.setElementLength(0.8);
391 travelingWave.setElementPosition(0.0);
392 travelingWave.setAperture(ApertureType::ELLIPTICAL, std::vector<double>{0.02, 0.03, 1.0});
393
394 MeshGenerator mesh;
395 mesh.add(travelingWave);
396 mesh.write(testStem_);
397
398 std::ifstream py(outputPath("_ElementPositions.py"));
399 ASSERT_TRUE(py.is_open());
400 const std::string script(
401 (std::istreambuf_iterator<char>(py)), std::istreambuf_iterator<char>());
402 EXPECT_NE(script.find("color = [7]"), std::string::npos);
403 EXPECT_NE(script.find("numVertices = [1152]"), std::string::npos);
404}
Inform * gmsg
Definition changes.cpp:7
ippl::Vector< T, Dim > Vector_t
ElementType
Definition ElementBase.h:94
const int nr
elements
Definition IndexMap.cpp:168
TEST_F(SolenoidPlacementTest, FieldMapEdgesAndSupportEnvelopeFollowFieldMap)
Abstract base class for accelerator geometry classes.
Definition Geometry.h:42
Definition Beam.h:32
static Beam * find(const std::string &name)
Find named BEAM.
Definition Beam.cpp:290
virtual void visitBeamline(const Beamline &)=0
Apply the algorithm to a beam line.
An abstract sequence of beam line components.
Definition Beamline.h:34
ippl::Vector< double, 3 > getOrigin() const
ElementType getType() const override
Get element type std::string.
ElementBase * clone() const override
Return clone.
const BGeometryBase & getGeometry() const override
Get geometry.
BGeometryBase & getGeometry() override
Get geometry.
void accept(BeamlineVisitor &visitor) const override
Apply visitor.
void iterate(BeamlineVisitor &, bool) const override
Apply visitor to all elements of the line.
NullGeometry geometry_
void setElementPosition(double elemedge)
Access to ELEMEDGE attribute.
void setAperture(const ApertureType &type, const std::vector< double > &args)
virtual double getElementLength() const
Get design length.
virtual void setElementLength(double length)
Set design length.
void add(key_t::first_type initialStep, key_t::second_type finalStep, const value_t &val)
Definition IndexMap.cpp:111
std::set< std::shared_ptr< Component > > value_t
Definition IndexMap.h:46
void saveSDDS(double startS) const
Definition IndexMap.cpp:182
void add(const ElementBase &element)
void write(const std::string &fname)
void setNormalComponent(int n, double)
Definition Multipole.h:214
Geometry representing an identity transform.
std::vector< Attribute > itsAttr
The object attributes.
Definition Object.h:210
PlacedElement getPlacedElement(const std::shared_ptr< Component > &comp) const
Return the placed-element view used by the bridge stage.
void compute3DLattice()
std::set< std::shared_ptr< Component > > getElements(const Vector_t< double, 3 > &x)
void visit(const T &, BeamlineVisitor &, PartBunch_t &)
void storeInputFn(const std::string &fn)
store opals input filename
Definition OpalData.cpp:561
static OpalData * getInstance()
Definition OpalData.cpp:193
void setOpenMode(OpenMode openMode)
Definition OpalData.cpp:298
virtual const std::string & what() const
Return the message string for the exception.
virtual const std::string & where() const
Return the name of the method or function which detected the exception.
Geometric placement record for an element instance.
CoordinateSystemTrafo getNominalEntryTransform() const
CoordinateSystemTrafo getNominalExitTransform() const
void cleanupOutputs() const
static void TearDownTestSuite()
std::shared_ptr< DataSink > dataSink_m
static void SetUpTestSuite()
static std::tuple< double, double, double > parsePositionLine(const std::string &line)
std::shared_ptr< PartBunch_t > makeBunch(const size_t numParticles)
void TearDown() override
static std::vector< std::string > splitWhitespace(const std::string &line)
void SetUp() override
std::filesystem::path writeXZFieldmap(const std::string &filename, double zBeginCm, double zEndCm, int nz, double rBeginCm, double rEndCm, int nr, double bzValue=1.0, double brValue=0.0)
std::filesystem::path outputPath(const std::string &suffix) const
std::filesystem::path fieldmapFile_
std::shared_ptr< FieldSolverCmd > fsCmdBase_m
virtual CoordinateSystemTrafo getEdgeToBegin() const override
Get the coordinate transformation to the begin of the element.
Definition Solenoid.h:214
virtual void initialise(PartBunch_t *bunch, double &startField, double &endField) override
initialise the solenoid element
Definition Solenoid.cpp:169
virtual void getElementDimensions(double &zBegin, double &zEnd) const override
Return the nominal body extent of the solenoid.
Definition Solenoid.cpp:243
virtual void getFieldExtend(double &zBegin, double &zEnd) const override
Return the local field-support interval of the solenoid.
Definition Solenoid.cpp:224
virtual CoordinateSystemTrafo getEdgeToEnd() const override
Get the coordinate transformation to the end of the element.
Definition Solenoid.h:224
virtual bool isInside(const Vector_t< double, 3 > &r) const override
Check if position r is inside the field map.
Definition Solenoid.cpp:236
bool getSupportEnvelope(double &horizontalRadius, double &verticalRadius) const
Get a finite transverse support envelope for placement/export.
Definition Solenoid.cpp:248
void setFieldMapFN(std::string fn)
Assign the field filename.
Definition Solenoid.cpp:213
void setPredefinedString(Attribute &attr, const std::string &val)
Set predefined string value.
bool enableHDF5
If true HDF5 files are written.
Definition Options.cpp:83