OPALX (Object Oriented Parallel Accelerator Library for Exascal) MINIorX
OPALX
MultipoleT.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
32
33#include <gsl/gsl_math.h>
34#include "MultipoleT.h"
39
40using namespace endfieldmodel;
41
42MultipoleT::MultipoleT(const std::string& name)
43 : Component(name),
46 maxOrder_m(5),
47 maxOrderX_m(10),
49 planarArcGeometry_m(1., 1.),
50 length_m(1.0),
51 angle_m(0.0),
52 entranceAngle_m(0.0),
53 rotation_m(0.0),
54 variableRadius_m(false),
56 verticalApert_m(0.5),
57 horizApert_m(0.5) {
58}
59
82
85
87 MultipoleT* newMultipole = new MultipoleT(*this);
88 newMultipole->initialise();
89 return newMultipole;
90}
91
93 RefPartBunch_m = nullptr;
94}
95
97 const Vector_t<double, 3>& R, const Vector_t<double, 3>& /*P*/, const double& /*t*/,
100 Vector_t<double, 3> R_prime = rotateFrame(R);
102 // R_prime[2] *= -1; //OPAL uses different sign convention...
103 Vector_t<double, 3> X = R_prime;
104 R_prime = transformCoords(X);
105 if (insideAperture(R_prime)) {
107 double theta;
108 double prefactor = (length_m / angle_m)
109 * (tanh((fringeField_l.getX0()) / fringeField_l.getLambda())
110 + tanh((fringeField_r.getX0()) / fringeField_r.getLambda()));
111 if (angle_m == 0.0) {
112 theta = 0.0;
113 } else if (!variableRadius_m) {
114 theta = R_prime[2] * angle_m / length_m;
115 } else { // variableRadius_m == true
116 theta =
117 fringeField_l.getLambda()
118 * log(cosh((R_prime[2] + fringeField_l.getX0()) / fringeField_l.getLambda()))
119 - fringeField_r.getLambda()
120 * log(cosh((R_prime[2] - fringeField_r.getX0()) / fringeField_r.getLambda()));
121 theta /= prefactor;
122 }
123 double Bx = getBx(R_prime);
124 double Bs = getBs(R_prime);
125 B[0] = Bx * cos(theta) - Bs * sin(theta);
126 B[2] = Bx * sin(theta) + Bs * cos(theta);
127 B[1] = getBz(R_prime);
128 // B[2] *= -1; //OPAL uses different sign convention
129 return false;
130 } else {
131 for (int i = 0; i < 3; i++) {
132 B[i] = 0.0;
133 }
135 }
136}
137
139 const size_t& i, const double& t, Vector_t<double, 3>& E, Vector_t<double, 3>& B) {
140 std::shared_ptr<ParticleContainer_t> pc = RefPartBunch_m->getParticleContainer();
141 auto Rview = pc->R.getView();
142 auto Pview = pc->P.getView();
143
144 const Vector_t<double, 3> R = Rview(i);
145 const Vector_t<double, 3> P = Pview(i);
146
147 return apply(R(i), P(i), t, E, B);
148}
149
151 Vector_t<double, 3> R_prime(3), R_pprime(3);
156 // 1st rotation
157 R_prime[0] = R[0] * cos(rotation_m) + R[1] * sin(rotation_m);
158 R_prime[1] = -R[0] * sin(rotation_m) + R[1] * cos(rotation_m);
159 R_prime[2] = R[2];
160 // 2nd rotation
161 R_pprime[0] = R_prime[2] * sin(entranceAngle_m) + R_prime[0] * cos(entranceAngle_m);
162 R_pprime[1] = R_prime[1];
163 R_pprime[2] = R_prime[2] * cos(entranceAngle_m) - R_prime[0] * sin(entranceAngle_m);
164 return R_pprime;
165}
166
172 Vector_t<double, 3> B_prime(3);
173 B_prime[0] = B[0] * cos(rotation_m) + B[1] * sin(rotation_m);
174 B_prime[1] = -B[0] * sin(rotation_m) + B[1] * cos(rotation_m);
175 B_prime[2] = B[2];
176 return B_prime;
177}
178
180 if (std::abs(R[1]) <= verticalApert_m / 2. && std::abs(R[0]) <= horizApert_m / 2.) {
181 return true;
182 } else {
183 return false;
184 }
185}
186
189 // If radius is constant
190 if (!variableRadius_m) {
191 double radius = length_m / angle_m;
192 // Transform from Cartesian to Frenet-Serret along the magnet
193 double alpha = atan(R[2] / (R[0] + radius));
194 if (alpha != 0.0 && angle_m != 0.0) {
195 X[0] = R[2] / sin(alpha) - radius;
196 X[1] = R[1];
197 X[2] = radius * alpha; // + boundingBoxLength_m;
198 } else {
199 X[0] = R[0];
200 X[1] = R[1];
201 X[2] = R[2]; // + boundingBoxLength_m;
202 }
203 } else {
204 // If radius is variable
206 R[0], R[1], R[2], fringeField_l.getX0(), fringeField_l.getLambda(),
207 fringeField_r.getLambda(), (length_m / angle_m));
208 std::vector<double> r = t.getTransformation();
209 X[0] = r[0];
210 X[1] = r[1];
211 X[2] = r[2];
212 }
213 return X;
214}
215
216void MultipoleT::setMaxOrder(std::size_t maxOrder) {
217 if (variableRadius_m && angle_m != 0.0) {
218 std::size_t N = recursion_VarRadius_m.size();
219 while (maxOrder >= N) {
223 recursion_VarRadius_m.push_back(r);
224 N = recursion_VarRadius_m.size();
225 }
226 } else if (!variableRadius_m && angle_m != 0.0) {
227 std::size_t N = recursion_ConstRadius_m.size();
228 while (maxOrder >= N) {
229 polynomial::RecursionRelation r(N, 2 * (N + maxOrderX_m + 1));
232 recursion_ConstRadius_m.push_back(r);
233 N = recursion_ConstRadius_m.size();
234 }
235 }
236 maxOrder_m = maxOrder;
237}
238
239void MultipoleT::setTransProfile(std::size_t n, double dTn) {
240 if (n > transMaxOrder_m) {
241 transMaxOrder_m = n;
242 transProfile_m.resize(n + 1, 0.0);
243 }
244 transProfile_m[n] = dTn;
245}
246
247bool MultipoleT::setFringeField(double s0, double lambda_l, double lambda_r) {
248 fringeField_l.Tanh::setLambda(lambda_l);
249 fringeField_l.Tanh::setX0(s0);
250 fringeField_l.Tanh::setTanhDiffIndices(2 * maxOrder_m + 1);
251
252 fringeField_r.Tanh::setLambda(lambda_r);
253 fringeField_r.Tanh::setX0(s0);
254 fringeField_r.Tanh::setTanhDiffIndices(2 * maxOrder_m + 1);
255
256 return true;
257}
258
263 double Bz = 0.0;
264 if (angle_m == 0.0) {
265 // Straight geometry -> Use corresponding field expansion directly
266 for (std::size_t n = 0; n <= maxOrder_m; n++) {
267 double f_n = 0.0;
268 for (std::size_t i = 0; i <= n; i++) {
269 f_n += gsl_sf_choose(n, i) * getTransDeriv(2 * i, R[0])
270 * getFringeDeriv(2 * n - 2 * i, R[2]);
271 }
272 f_n *= gsl_sf_pow_int(-1.0, n);
273 Bz += gsl_sf_pow_int(R[1], 2 * n) / gsl_sf_fact(2 * n) * f_n;
274 }
275 } else {
276 if (variableRadius_m == true and getFringeDeriv(0, R[2]) < 1.0e-12) {
277 // Return 0 if end of fringe field is reached
278 // This is to avoid functions being called at infinite radius
279 return 0.0;
280 }
281 // Curved geometry -> Use full machinery of differential operators
282 for (std::size_t n = 0; n <= maxOrder_m; n++) {
283 double f_n = getFn(n, R[0], R[2]);
284 Bz += gsl_sf_pow_int(R[1], 2 * n) / gsl_sf_fact(2 * n) * f_n;
285 }
286 }
287 return Bz;
288}
289
294 double Bx = 0.0;
295 if (angle_m == 0.0) {
296 // Straight geometry -> Use corresponding field expansion directly
297 for (std::size_t n = 0; n <= maxOrder_m; n++) {
298 double f_n = 0.0;
299 for (std::size_t i = 0; i <= n; i++) {
300 f_n += gsl_sf_choose(n, i) * getTransDeriv(2 * i + 1, R[0])
301 * getFringeDeriv(2 * n - 2 * i, R[2]);
302 }
303 f_n *= gsl_sf_pow_int(-1.0, n);
304 Bx += gsl_sf_pow_int(R[1], 2 * n + 1) / gsl_sf_fact(2 * n + 1) * f_n;
305 }
306 } else {
307 if (variableRadius_m == true and getFringeDeriv(0, R[2]) < 1.0e-12) {
308 // Return 0 if end of fringe field is reached
309 // This is to avoid functions being called at infinite radius
310 return 0.0;
311 }
312 // Curved geometry -> Use full machinery of differential operators
313 for (std::size_t n = 0; n <= maxOrder_m; n++) {
314 double partialX_fn = getFnDerivX(n, R[0], R[2]);
315 Bx += partialX_fn * gsl_sf_pow_int(R[1], 2 * n + 1) / gsl_sf_fact(2 * n + 1);
316 }
317 }
318 return Bx;
319}
320
325 double Bs = 0.0;
326 if (angle_m == 0.0) {
327 // Straight geometry -> Use corresponding field expansion directly
328 for (std::size_t n = 0; n <= maxOrder_m; n++) {
329 double f_n = 0.0;
330 for (std::size_t i = 0; i <= n; i++) {
331 f_n += gsl_sf_choose(n, i) * getTransDeriv(2 * i, R[0])
332 * getFringeDeriv(2 * n - 2 * i + 1, R[2]);
333 }
334 f_n *= gsl_sf_pow_int(-1.0, n);
335 Bs += gsl_sf_pow_int(R[1], 2 * n + 1) / gsl_sf_fact(2 * n + 1) * f_n;
336 }
337 } else {
338 if (variableRadius_m == true and getFringeDeriv(0, R[2]) < 1.0e-12) {
339 // Return 0 if end of fringe field is reached
340 // This is to avoid functions being called at infinite radius
341 return 0.0;
342 }
343 // Curved geometry -> Use full machinery of differential operators
344 for (std::size_t n = 0; n <= maxOrder_m; n++) {
345 double partialS_fn = getFnDerivS(n, R[0], R[2]);
346 Bs += partialS_fn * gsl_sf_pow_int(R[1], 2 * n + 1) / gsl_sf_fact(2 * n + 1);
347 }
348 Bs /= getScaleFactor(R[0], R[2]);
349 }
350 return Bs;
351}
352
353double MultipoleT::getFringeDeriv(int n, double s) {
354 if (n <= 10) {
355 return (fringeField_l.getTanh(s, n) - fringeField_r.getNegTanh(s, n)) / 2;
356 } else {
358 s, fringeField_l.Tanh::getX0(), fringeField_l.Tanh::getLambda(),
359 fringeField_r.Tanh::getLambda(), n);
360 }
361}
362
363double MultipoleT::getTransDeriv(std::size_t n, double x) {
369 double func = 0;
370 std::vector<double> temp = transProfile_m;
371 if (n <= transMaxOrder_m) {
372 if (n != 0) {
373 for (std::size_t i = 1; i <= n; i++) {
374 for (std::size_t j = 0; j <= transMaxOrder_m; j++) {
375 if (j <= transMaxOrder_m - i) {
376 // Move terms to the left and multiply by power
377 temp[j] = temp[j + 1] * (j + 1);
378 } else {
379 // Put 0 at the end for missing higher powers
380 temp[j] = 0.0;
381 }
382 }
383 }
384 }
385 // Now use the vector to calculate value of the function
386 for (std::size_t k = 0; k <= transMaxOrder_m; k++) {
387 func += temp[k] * gsl_sf_pow_int(x, k);
388 }
389 }
390 return func;
391}
392
394 if (transMaxOrder_m < 1) {
395 transProfile_m.resize(1, 0.);
396 }
397 transProfile_m[0] = B0;
398}
399
401 visitor.visitMultipoleT(*this);
402}
403
404void MultipoleT::getDimensions(double& /*zBegin*/, double& /*zEnd*/) const {
405}
406
407void MultipoleT::setAperture(double vertAp, double horizAp) {
408 verticalApert_m = vertAp;
409 horizApert_m = horizAp;
410}
411
412std::vector<double> MultipoleT::getAperture() const {
413 std::vector<double> temp(2, 0.0);
414 temp[0] = verticalApert_m;
415 temp[1] = horizApert_m;
416 return temp;
417}
418
419std::vector<double> MultipoleT::getFringeLength() const {
420 std::vector<double> temp(2, 0.0);
421 temp[0] = fringeField_l.getLambda();
422 temp[1] = fringeField_r.getLambda();
423 return temp;
424}
425
426double MultipoleT::getRadius(double s) {
427 double centralRadius = length_m / angle_m;
428 if (!variableRadius_m) {
429 return centralRadius;
430 }
431 if (getFringeDeriv(0, s) != 0.0) {
432 return centralRadius * getFringeDeriv(0, 0) / getFringeDeriv(0, s);
433 } else {
434 return -1; // Return -1 if radius is infinite
435 }
436}
437
438double MultipoleT::getScaleFactor(double x, double s) {
439 if (!variableRadius_m) {
440 return 1 + x * angle_m / length_m;
441 } else {
442 return 1 + x / getRadius(s);
443 }
444}
445
446double MultipoleT::getFnDerivX(std::size_t n, double x, double s) {
447 if (n == 0) {
448 return getTransDeriv(1, x) * getFringeDeriv(0, s);
449 }
450 double deriv = 0.0;
451 double stepSize = 1e-3;
452 deriv += 1. * getFn(n, x - 2. * stepSize, s);
453 deriv += -8. * getFn(n, x - stepSize, s);
454 deriv += 8. * getFn(n, x + stepSize, s);
455 deriv += -1. * getFn(n, x + 2. * stepSize, s);
456 deriv /= 12 * stepSize;
457 return deriv;
458}
459
460double MultipoleT::getFnDerivS(std::size_t n, double x, double s) {
461 if (n == 0) {
462 return getTransDeriv(0, x) * getFringeDeriv(1, s);
463 }
464 double deriv = 0.0;
465 double stepSize = 1e-3;
466 deriv += 1. * getFn(n, x, s - 2. * stepSize);
467 deriv += -8. * getFn(n, x, s - stepSize);
468 deriv += 8. * getFn(n, x, s + stepSize);
469 deriv += -1. * getFn(n, x, s + 2. * stepSize);
470 deriv /= 12 * stepSize;
471 return deriv;
472}
473
474double MultipoleT::getFn(std::size_t n, double x, double s) {
475 if (n == 0) {
476 return getTransDeriv(0, x) * getFringeDeriv(0, s);
477 }
478 if (!variableRadius_m) {
479 double rho = length_m / angle_m;
480 double func = 0.0;
481
482 for (std::size_t j = 0; j <= recursion_ConstRadius_m.at(n).getMaxSDerivatives(); j++) {
483 double FringeDerivj = getFringeDeriv(2 * j, s);
484 for (std::size_t i = 0; i <= recursion_ConstRadius_m.at(n).getMaxXDerivatives(); i++) {
485 if (recursion_ConstRadius_m.at(n).isPolynomialZero(i, j)) {
486 continue;
487 }
488 func += (recursion_ConstRadius_m.at(n).evaluatePolynomial(x / rho, i, j)
489 * getTransDeriv(i, x) * FringeDerivj)
490 / gsl_sf_pow_int(rho, 2 * n - i - 2 * j);
491 }
492 }
493 func *= gsl_sf_pow_int(-1.0, n);
494 return func;
495 } else {
496 double rho = length_m / angle_m;
497 double S_0 = getFringeDeriv(0, 0);
498 double y = getFringeDeriv(0, s) / (S_0 * rho);
499 double func = 0.0;
500 std::vector<double> fringeDerivatives;
501 for (std::size_t j = 0; j <= recursion_VarRadius_m.at(n).getMaxSDerivatives(); j++) {
502 fringeDerivatives.push_back(getFringeDeriv(j, s) / (S_0 * rho));
503 }
504 for (std::size_t i = 0; i <= recursion_VarRadius_m.at(n).getMaxXDerivatives(); i++) {
505 double temp = 0.0;
506 for (std::size_t j = 0; j <= recursion_VarRadius_m.at(n).getMaxSDerivatives(); j++) {
507 temp +=
508 recursion_VarRadius_m.at(n).evaluatePolynomial(x, y, i, j, fringeDerivatives)
509 * fringeDerivatives.at(j);
510 }
511 func += temp * getTransDeriv(i, x);
512 }
513 func *= gsl_sf_pow_int(-1.0, n) * S_0 * rho;
514 return func;
515 }
516}
517
519 planarArcGeometry_m.setElementLength(2 * boundingBoxLength_m);
520 planarArcGeometry_m.setCurvature(angle_m / length_m);
521}
522
523void MultipoleT::initialise(PartBunch_t* bunch, double& /*startField*/, double& /*endField*/) {
524 RefPartBunch_m = bunch;
525 initialise();
526}
527
528bool MultipoleT::bends() const {
529 return (transProfile_m[0] != 0);
530}
531
535
539
541 return dummy;
542}
543
545 return dummy;
546}
PartBunch< PLayout_t< double, 3 >, double, 3 > PartBunch_t
ippl::Vector< T, Dim > Vector_t
double integrate(const double &a, const double &s0, const double &lambdaleft, const double &lambdaright, const int &n)
Definition tanhDeriv.cpp:66
virtual void visitMultipoleT(const MultipoleT &)=0
Apply the algorithm to an arbitrary multipole.
Component(const std::string &name)
Constructor with given name.
Definition Component.cpp:44
PartBunch_t * RefPartBunch_m
Definition Component.h:185
bool getFlagDeleteOnTransverseExit() const
ElementBase(const std::string &name)
Constructor with given name.
std::vector< double > transProfile_m
Definition MultipoleT.h:264
void setDipoleConstant(double B0)
std::size_t transMaxOrder_m
Definition MultipoleT.h:262
double horizApert_m
Definition MultipoleT.h:296
void setAperture(double vertAp, double horizAp)
BMultipoleField dummy
Definition MultipoleT.h:298
Vector_t< double, 3 > rotateFrameInverse(Vector_t< double, 3 > &B)
Vector_t< double, 3 > transformCoords(const Vector_t< double, 3 > &R)
double getTransDeriv(std::size_t n, double x)
double entranceAngle_m
Definition MultipoleT.h:283
void setMaxOrder(std::size_t maxOrder)
std::vector< polynomial::RecursionRelationTwo > recursion_VarRadius_m
Definition MultipoleT.h:259
void initialise()
bool apply(const Vector_t< double, 3 > &R, const Vector_t< double, 3 > &P, const double &t, Vector_t< double, 3 > &E, Vector_t< double, 3 > &B) override
endfieldmodel::Tanh fringeField_r
Definition MultipoleT.h:251
endfieldmodel::Tanh fringeField_l
Definition MultipoleT.h:250
double getBz(const Vector_t< double, 3 > &R)
ElementBase * clone() const override
bool insideAperture(const Vector_t< double, 3 > &R)
EMField & getField() override
double getScaleFactor(double x, double s)
double getFnDerivX(std::size_t n, double x, double s)
void accept(BeamlineVisitor &visitor) const override
double verticalApert_m
Definition MultipoleT.h:295
double rotation_m
Definition MultipoleT.h:284
void getDimensions(double &zBegin, double &zEnd) const override
double angle_m
Definition MultipoleT.h:282
double getFnDerivS(std::size_t n, double x, double s)
std::size_t maxOrderX_m
Definition MultipoleT.h:257
double getRadius(double s)
std::vector< polynomial::RecursionRelation > recursion_ConstRadius_m
Definition MultipoleT.h:260
double getBx(const Vector_t< double, 3 > &R)
double length_m
Definition MultipoleT.h:281
double getFn(std::size_t n, double x, double s)
bool setFringeField(double s0, double lambda_left, double lambda_right)
void initialise(PartBunch_t *, double &startField, double &endField) override
void finalise() override
double getFringeDeriv(int n, double s)
PlanarArcGeometry planarArcGeometry_m
Definition MultipoleT.h:266
bool bends() const override
std::vector< double > getAperture() const
void setTransProfile(std::size_t n, double Bn)
std::size_t maxOrder_m
Definition MultipoleT.h:255
bool variableRadius_m
Definition MultipoleT.h:286
double boundingBoxLength_m
Definition MultipoleT.h:288
MultipoleT(const std::string &name)
Vector_t< double, 3 > rotateFrame(const Vector_t< double, 3 > &R)
double getBs(const Vector_t< double, 3 > &R)
PlanarArcGeometry & getGeometry() override
std::vector< double > getFringeLength() const
std::vector< double > getTransformation() const
void resizeX(const std::size_t &xDerivatives)
void truncate(std::size_t highestXorder)
void truncate(std::size_t highestXorder)
void resizeX(const std::size_t &xDerivatives)
A simple arc in the XZ plane.
Abstract base class for electromagnetic fields.
Definition EMField.h:188