OPAL (Object Oriented Parallel Accelerator Library) 2024.2
OPAL
MultipoleTCurvedVarRadius.cpp
Go to the documentation of this file.
1/*
2 * Copyright (c) 2017, Titus Dascalu
3 * Copyright (c) 2018, Martin Duy Tat
4 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * 1. Redistributions of source code must retain the above copyright notice,
8 * this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright notice,
10 * this list of conditions and the following disclaimer in the documentation
11 * and/or other materials provided with the distribution.
12 * 3. Neither the name of STFC nor the names of its contributors may be used to
13 * endorse or promote products derived from this software without specific
14 * prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28#include <cmath>
29#include <vector>
30
31#include "gsl/gsl_sf_pow_int.h"
32
36
38 MultipoleTBase(element),
39 varRadiusGeometry_m(1.0, 1.0, 1.0, 1.0, 1.0) {
40}
41
43 // Record geometry information
45 2.0 * element_m->getEntryOffset());
47 auto [s0, leftFringe, rightFringe] = element_m->getFringeField();
52 // Now work out where the entry point will be in the local cartesian coordinate system
53 // whose origin is at the center of the magnet.
55 Vector_t{0.0, 0.0, element_m->getLength() / 2.0 + element_m->getEntryOffset()});
56 // The tangent to the curve at this point forms the z axis of the coordinate system
57 // opal addresses us in, so we can calculate the rotation required
58 auto secondPoint = curvilinearToLocalCartesian(
59 Vector_t{0.0, 0.0,
62 secondPoint[2] - localCartesianEntryPoint_[2]);
63}
64
66 // Rotate Opal supplied cartesian coordinates around its origin
67 auto x_rotated = R[0] * std::cos(localCartesianRotation_) -
68 R[2] * std::sin(localCartesianRotation_);
69 auto z_rotated = R[0] * std::sin(localCartesianRotation_) +
70 R[2] * std::cos(localCartesianRotation_);
71 // Offset to the center of the magnet
72 R = {x_rotated + localCartesianEntryPoint_[0], R[1],
73 z_rotated + localCartesianEntryPoint_[2]};
74 // And finally into curvilinear coordinates
76}
77
79 // Offset to the Opal origin
80 auto x_offset = r[0] - localCartesianEntryPoint_[0];
81 auto z_offset = r[2] - localCartesianEntryPoint_[2];
82 // And rotate
83 auto x_rotated = x_offset * std::cos(-localCartesianRotation_) -
84 z_offset * std::sin(-localCartesianRotation_);
85 auto z_rotated = x_offset * std::sin(-localCartesianRotation_) +
86 z_offset * std::cos(-localCartesianRotation_);
87 return {x_rotated, r[1], -z_rotated};
88}
89
91 auto [s0, leftFringe, rightFringe] = element_m->getFringeField();
92 double rho = element_m->getLength() / element_m->getBendAngle();
93 coordinatetransform::CoordinateTransform t(r[0], r[1], r[2], s0, leftFringe, rightFringe, rho);
94 std::vector<double> result = t.getTransformation();
95 return {result[0], result[1], result[2]};
96}
97
99 auto [s0, leftFringe, rightFringe] = element_m->getFringeField();
100 double rho = element_m->getLength() / element_m->getBendAngle();
101 double prefactor = rho * (std::tanh(s0 / leftFringe) + std::tanh(s0 / rightFringe));
102 double theta = leftFringe * std::log(std::cosh((R[2] + s0) / leftFringe)) -
103 rightFringe * std::log(std::cosh((R[2] - s0) / rightFringe));
104 theta /= prefactor;
105 double Bx = B[0], Bs = B[2];
106 B[0] = Bx * std::cos(theta) - Bs * std::sin(theta);
107 B[2] = Bx * std::sin(theta) + Bs * std::cos(theta);
108}
109
110void MultipoleTCurvedVarRadius::setMaxOrder(size_t orderZ, size_t orderX) {
111 std::size_t N = recursion_m.size();
112 while (orderZ >= N) {
113 polynomial::RecursionRelationTwo r(N, 2 * (N + orderX + 1));
115 r.truncate(orderX);
116 recursion_m.push_back(r);
117 N = recursion_m.size();
118 }
119}
120
122 double result = 1.0;
123 if (element_m->getFringeDeriv(0, s) > 1.0e-12) {
124 double radius = element_m->getLength() * element_m->getFringeDeriv(0, 0) /
126 result += x / radius;
127 }
128 return result;
129}
130
131double MultipoleTCurvedVarRadius::getFn(size_t n, double x, double s) {
132 double result{};
133 if (n == 0) {
134 result = element_m->getTransDeriv(0, x) * element_m->getFringeDeriv(0, s);
135 } else {
136 double rho = element_m->getLength() / element_m->getBendAngle();
137 double S_0 = element_m->getFringeDeriv(0, 0);
138 double y = element_m->getFringeDeriv(0, s) / (S_0 * rho);
139 std::vector<double> fringeDerivatives;
140 for (std::size_t j = 0; j <= recursion_m.at(n).getMaxSDerivatives(); j++) {
141 fringeDerivatives.push_back(element_m->getFringeDeriv(j, s) / (S_0 * rho));
142 }
143 for (std::size_t i = 0; i <= recursion_m.at(n).getMaxXDerivatives(); i++) {
144 double temp = 0.0;
145 for (std::size_t j = 0; j <= recursion_m.at(n).getMaxSDerivatives(); j++) {
146 temp += recursion_m.at(n).evaluatePolynomial(x, y, i, j, fringeDerivatives)
147 * fringeDerivatives.at(j);
148 }
149 result += temp * element_m->getTransDeriv(i, x);
150 }
151 result *= gsl_sf_pow_int(-1.0, static_cast<int>(n)) * S_0 * rho;
152 }
153 return result;
154}
155
157 const Vector_t& target) {
158 // Return the distance between the vector r and the target.
159 // We only consider the first and last coordinates as the height coordinate
160 // is invariant across these transforms.
161 auto c = localCartesianToCurvilinear(r);
162 double dx = c[0] - target[0];
163 double ds = c[2] - target[2];
164 return std::sqrt(dx * dx + ds * ds);
165}
166
168 // This functions uses a minimize loop and coordinate descent with backtracking
169 // to implement the inverse coordinate transform from the magnet's curvilinear
170 // system to the local cartesian system whose origins are the centre of the magnet.
171 // Note that this function is iterative and should therefore only be used occasionally.
172 Vector_t result{r};
173 double step = 1.0;
174 double best_res = reverseTransformResidual(result, r);
175 for (size_t iter = 0; iter < ReverseTransformMaxIterations; ++iter) {
176 bool improved = false;
177 for (int dim = 0; dim < 2; ++dim) {
178 for (int dir = -1; dir <= 1; dir += 2) {
179 Vector_t trial = result;
180 if (dim == 0) {
181 trial[0] += dir * step;
182 } else {
183 trial[2] += dir * step;
184 }
185 double res = reverseTransformResidual(trial, r);
186 if (res < best_res) {
187 result = trial;
188 best_res = res;
189 improved = true;
190 break;
191 }
192 }
193 if (improved) {
194 break;
195 }
196 }
197 if (!improved) {
198 step *= 0.5;
199 }
200 if (step < ReverseTransformTolerance) {
201 break;
202 }
203 }
204 return result;
205}
PETE_TBTree< FnArcTan2, PETE_Scalar< Vektor< T1, Dim > >, typename T2::PETE_Expr_t > atan2(const Vektor< T1, Dim > &l, const PETE_Expr< T2 > &r)
std::size_t getTransMaxOrder() const
Definition MultipoleT.h:151
size_t getMaxFOrder() const
Definition MultipoleT.h:143
double getFringeDeriv(const std::size_t &n, const double &s)
double getLength() const
Definition MultipoleT.h:196
double getTransDeriv(const std::size_t &n, const double &x) const
size_t getMaxXOrder() const
Definition MultipoleT.h:144
std::tuple< double, double, double > getFringeField() const
double getBendAngle() const
Definition MultipoleT.h:187
double getEntryOffset() const
Definition MultipoleT.h:181
MultipoleT * element_m
std::vector< polynomial::RecursionRelationTwo > recursion_m
void transformBField(Vector_t &, const Vector_t &) override
double getScaleFactor(double x, double s) override
double reverseTransformResidual(const Vector_t &r, const Vector_t &target)
static constexpr double TangentStep
double getFn(size_t n, double x, double s) override
MultipoleTCurvedVarRadius(MultipoleT *element)
static constexpr size_t ReverseTransformMaxIterations
void setMaxOrder(size_t orderZ, size_t orderX) override
Vector_t localCartesianToCurvilinear(const Vector_t &r)
void transformCoords(Vector_t &) override
static constexpr double ReverseTransformTolerance
Vector_t localCartesianToOpalCartesian(const Vector_t &r) override
Vector_t curvilinearToLocalCartesian(const Vector_t &r)
std::vector< double > getTransformation() const
void truncate(std::size_t highestXorder)
void resizeX(const std::size_t &xDerivatives)
void setS0(const double &s_0)
virtual void setElementLength(double length)
void setLambdaRight(const double &lambda_right)
void setLambdaLeft(const double &lambda_left)
void setRadius(const double &rho)