OPALX (Object Oriented Parallel Accelerator Library for Exascal) master (dc2a29eed580)
OPALX
Loading...
Searching...
No Matches
TestOpalDrift.cpp
Go to the documentation of this file.
1//
2// Copyright (c) 2026, Paul Scherrer Institute, Villigen PSI, Switzerland
3// All rights reserved
4//
5// This file is part of OPALX.
6//
7// OPALX is free software: you can redistribute it and/or modify
8// it under the terms of the GNU General Public License as published by
9// the Free Software Foundation, either version 3 of the License, or
10// (at your option) any later version.
11//
12// You should have received a copy of the GNU General Public License
13// along with OPALX. If not, see <https://www.gnu.org/licenses/>.
14//
17#include "Elements/OpalDrift.h"
19#include "gtest/gtest.h"
20
21#include <memory>
22#include <optional>
23#include <string>
24#include <vector>
25
26namespace {
27
28 class TestOpalDrift : public testing::Test {
29 public:
30 TestOpalDrift() = default;
31 };
32
33 std::unique_ptr<OpalDrift> makeDrift(const std::optional<std::string>& aperture) {
34 auto drift = std::make_unique<OpalDrift>();
35 Attributes::setReal(*drift->findAttribute("L"), 2.0);
36 Attributes::setReal(*drift->findAttribute("ELEMEDGE"), 0.0);
37 if (aperture.has_value()) {
38 Attributes::setString(*drift->findAttribute("APERTURE"), aperture.value());
39 }
40 drift->update();
41 return drift;
42 }
43
44} // namespace
45
46TEST_F(TestOpalDrift, CircleDefaultsMatchDefaultApertureBehaviour) {
47 auto noAperture = makeDrift(std::nullopt);
48 auto circle = makeDrift("CIRCLE(1)");
49 auto conicCircle = makeDrift("CIRCLE(1,1)");
50
51 ElementBase* noApertureElement = noAperture->getElement();
52 ElementBase* circleElement = circle->getElement();
53 ElementBase* conicCircleElement = conicCircle->getElement();
54
55 ASSERT_NE(noApertureElement, nullptr);
56 ASSERT_NE(circleElement, nullptr);
57 ASSERT_NE(conicCircleElement, nullptr);
58
59 const std::vector<Vector_t<double, 3>> probes = {
60 {0.00, 0.00, 0.20}, {0.20, 0.10, 1.00}, {0.49, 0.00, 0.20}, {0.49, 0.00, 1.00},
61 {0.49, 0.00, 1.80}, {0.50, 0.00, 1.00}, {0.00, 0.50, 1.00}, {0.36, 0.36, 1.00},
62 {0.40, 0.40, 1.00}, {0.00, 0.00, -0.10}, {0.00, 0.00, 2.00}};
63
64 for (const Vector_t<double, 3>& r : probes) {
65 SCOPED_TRACE(
66 ::testing::Message()
67 << "Probe point (" << r[0] << ", " << r[1] << ", " << r[2] << ")");
68 EXPECT_EQ(noApertureElement->isInside(r), circleElement->isInside(r));
69 EXPECT_EQ(noApertureElement->isInside(r), conicCircleElement->isInside(r));
70 }
71}
72
73TEST_F(TestOpalDrift, SquareAndRectangleEquivalentBehaviour) {
74 auto rectangle = makeDrift("RECTANGLE(1,1)");
75 auto conicRectangle = makeDrift("RECTANGLE(1,1,1)");
76 auto square = makeDrift("SQUARE(1)");
77 auto conicSquare = makeDrift("SQUARE(1,1)");
78
79 ElementBase* rectangleElement = rectangle->getElement();
80 ElementBase* conicRectangleElement = conicRectangle->getElement();
81 ElementBase* squareElement = square->getElement();
82 ElementBase* conicSquareElement = conicSquare->getElement();
83
84 ASSERT_NE(rectangleElement, nullptr);
85 ASSERT_NE(conicRectangleElement, nullptr);
86 ASSERT_NE(squareElement, nullptr);
87 ASSERT_NE(conicSquareElement, nullptr);
88
89 const std::vector<Vector_t<double, 3>> probes = {
90 {0.00, 0.00, 0.20}, {0.49, 0.49, 1.00}, {0.49, 0.10, 1.80}, {0.10, 0.49, 0.20},
91 {0.50, 0.10, 1.00}, {0.10, 0.50, 1.00}, {0.60, 0.40, 1.00}, {0.40, 0.60, 1.00},
92 {0.00, 0.00, -0.10}, {0.00, 0.00, 2.00}};
93
94 for (const Vector_t<double, 3>& r : probes) {
95 SCOPED_TRACE(
96 ::testing::Message()
97 << "Probe point (" << r[0] << ", " << r[1] << ", " << r[2] << ")");
98 const bool expected = rectangleElement->isInside(r);
99 EXPECT_EQ(expected, conicRectangleElement->isInside(r));
100 EXPECT_EQ(expected, squareElement->isInside(r));
101 EXPECT_EQ(expected, conicSquareElement->isInside(r));
102 }
103}
104
105TEST_F(TestOpalDrift, ConicCircleOpeningBehaviour) {
106 auto conicCircle = makeDrift("CIRCLE(1,2)");
107 ElementBase* conicCircleElement = conicCircle->getElement();
108
109 ASSERT_NE(conicCircleElement, nullptr);
110
111 const Vector_t<double, 3> centerline = {0.00, 0.00, 1.00};
112 EXPECT_TRUE(conicCircleElement->isInside(centerline));
113
114 const Vector_t<double, 3> startProbe = {0.75, 0.00, 0.10};
115 const Vector_t<double, 3> middleProbe = {0.75, 0.00, 1.00};
116 const Vector_t<double, 3> endProbe = {0.75, 0.00, 1.90};
117
118 EXPECT_FALSE(conicCircleElement->isInside(startProbe));
119 EXPECT_FALSE(conicCircleElement->isInside(middleProbe));
120 EXPECT_TRUE(conicCircleElement->isInside(endProbe));
121}
122
123TEST_F(TestOpalDrift, ConicCircleClosingBehaviour) {
124 auto conicCircle = makeDrift("CIRCLE(1,0.5)");
125 ElementBase* conicCircleElement = conicCircle->getElement();
126
127 ASSERT_NE(conicCircleElement, nullptr);
128
129 const Vector_t<double, 3> startProbe = {0.40, 0.00, 0.10};
130 const Vector_t<double, 3> middleProbe = {0.40, 0.00, 1.00};
131 const Vector_t<double, 3> endProbe = {0.40, 0.00, 1.90};
132
133 EXPECT_TRUE(conicCircleElement->isInside(startProbe));
134 EXPECT_FALSE(conicCircleElement->isInside(middleProbe));
135 EXPECT_FALSE(conicCircleElement->isInside(endProbe));
136}
137
138TEST_F(TestOpalDrift, CircleConstantAlongLengthAndLongitudinalBounds) {
139 auto circle = makeDrift("CIRCLE(1)");
140 ElementBase* circleElement = circle->getElement();
141
142 ASSERT_NE(circleElement, nullptr);
143
144 const std::vector<Vector_t<double, 3>> insideTransverseInsideLength = {
145 {0.49, 0.00, 0.10}, {0.49, 0.00, 1.00}, {0.49, 0.00, 1.90}};
146 for (const Vector_t<double, 3>& r : insideTransverseInsideLength) {
147 EXPECT_TRUE(circleElement->isInside(r));
148 }
149
150 const std::vector<Vector_t<double, 3>> outsideTransverseInsideLength = {
151 {0.51, 0.00, 0.10}, {0.51, 0.00, 1.00}, {0.51, 0.00, 1.90}};
152 for (const Vector_t<double, 3>& r : outsideTransverseInsideLength) {
153 EXPECT_FALSE(circleElement->isInside(r));
154 }
155
156 EXPECT_FALSE(circleElement->isInside(Vector_t<double, 3>({0.00, 0.00, -0.10})));
157 EXPECT_FALSE(circleElement->isInside(Vector_t<double, 3>({0.00, 0.00, 2.00})));
158}
159
160TEST_F(TestOpalDrift, InvalidConicScaleThrowsOpalException) {
161 EXPECT_THROW(makeDrift("RECTANGLE(1,1,-1)"), OpalException);
162 EXPECT_THROW(makeDrift("CIRCLE(1,0)"), OpalException);
163 EXPECT_THROW(makeDrift("ELLIPSE(1,1,nan)"), OpalException);
164}
165
166TEST_F(TestOpalDrift, MalformedRectangleArgumentsThrow) {
167 EXPECT_THROW(makeDrift("RECTANGLE(1,)"), OpalException);
168 EXPECT_THROW(makeDrift("RECTANGLE(,1)"), OpalException);
169}
170
171TEST_F(TestOpalDrift, ConicEllipseOpeningBehaviour) {
172 auto conicEllipse = makeDrift("ELLIPSE(1,1,2)");
173 ElementBase* conicEllipseElement = conicEllipse->getElement();
174
175 ASSERT_NE(conicEllipseElement, nullptr);
176
177 const Vector_t<double, 3> startProbe = {0.75, 0.00, 0.10};
178 const Vector_t<double, 3> middleProbe = {0.75, 0.00, 1.00};
179 const Vector_t<double, 3> endProbe = {0.75, 0.00, 1.90};
180
181 EXPECT_FALSE(conicEllipseElement->isInside(startProbe));
182 EXPECT_FALSE(conicEllipseElement->isInside(middleProbe));
183 EXPECT_TRUE(conicEllipseElement->isInside(endProbe));
184}
ippl::Vector< T, Dim > Vector_t
TEST_F(TestOpalDrift, CircleDefaultsMatchDefaultApertureBehaviour)
virtual bool isInside(const Vector_t< double, 3 > &r) const
void setString(Attribute &attr, const std::string &val)
Set string value.
void setReal(Attribute &attr, double val)
Set real value.