OPAL (Object Oriented Parallel Accelerator Library) 2024.2
OPAL
ScalingFFAMagnet.cpp
Go to the documentation of this file.
1//
2// Class ScalingFFAMagnet
3// Defines the abstract interface for a sector FFA magnet
4// with radially scaling fringe fields.
5//
6// Copyright (c) 2017 - 2023, Chris Rogers, STFC Rutherford Appleton Laboratory, Didcot, UK
7// All rights reserved
8//
9// This file is part of OPAL.
10//
11// OPAL is free software: you can redistribute it and/or modify
12// it under the terms of the GNU General Public License as published by
13// the Free Software Foundation, either version 3 of the License, or
14// (at your option) any later version.
15//
16// You should have received a copy of the GNU General Public License
17// along with OPAL. If not, see <https://www.gnu.org/licenses/>.
18//
20
22
25 planarArcGeometry_m(1., 1.),
26 dummy(),
27 endField_m(nullptr) {
28}
29
31 Component(right),
32 planarArcGeometry_m(right.planarArcGeometry_m),
33 dummy(), maxOrder_m(right.maxOrder_m), tanDelta_m(right.tanDelta_m),
34 k_m(right.k_m), Bz_m(right.Bz_m), r0_m(right.r0_m),
35 rMin_m(right.rMin_m), rMax_m(right.rMax_m), phiStart_m(right.phiStart_m),
36 phiEnd_m(right.phiEnd_m), azimuthalExtent_m(right.azimuthalExtent_m),
37 verticalExtent_m(right.verticalExtent_m), centre_m(right.centre_m),
38 endField_m(nullptr), endFieldName_m(right.endFieldName_m),
39 dfCoefficients_m(right.dfCoefficients_m)
40{
41 endField_m = right.endField_m->clone();
43 Bz_m = right.Bz_m;
44 r0_m = right.r0_m;
45 r0Sign_m = right.r0Sign_m;
46}
47
49 delete endField_m;
50}
51
53 ScalingFFAMagnet* magnet = new ScalingFFAMagnet(*this);
54 magnet->initialise();
55 return magnet;
56}
57
59 return dummy;
60}
61
63 return dummy;
64}
65
68}
69
70void ScalingFFAMagnet::initialise(PartBunchBase<double, 3>* bunch, double& /*startField*/, double& /*endField*/) {
71 RefPartBunch_m = bunch;
72 initialise();
73}
74
76 RefPartBunch_m = nullptr;
77}
78
80 return true;
81}
82
85}
86
89}
90
92 visitor.visitScalingFFAMagnet(*this);
93}
94
96 double x = r0Sign_m * (r0_m + R[0]);
97 double r = std::sqrt(x * x + R[2] * R[2]);
98 double phi = std::atan2(R[2], x); // angle between y-axis and position vector in anticlockwise direction
99 Vector_t posCyl(r, R[1], phi);
100 //Vector_t posCyl(r, pos[1], phi);
101 Vector_t bCyl(0., 0., 0.); //br bz bphi
102 bool outOfBounds = getFieldValueCylindrical(posCyl, bCyl);
103
104 // this is cartesian coordinates
105 B[1] += bCyl[1];
106 B[0] += -r0Sign_m * (-bCyl[0] * std::cos(phi) + bCyl[2] * std::sin(phi));
107 B[2] += bCyl[0] * std::sin(phi) + bCyl[2] * std::cos(phi);
108 return outOfBounds;
109}
110
112 double r = pos[0];
113 double z = pos[1];
114 double phi = pos[2];
115 if (r < rMin_m || r > rMax_m) {
116 return true;
117 }
118 if (z < -verticalExtent_m || z > verticalExtent_m) {
119 return true;
120 }
121 double normRadius = r/std::abs(r0_m);
122 double g = tanDelta_m*std::log(normRadius);
123 double phiSpiral = phi - g - phiStart_m;
124 if (phiSpiral < -azimuthalExtent_m || phiSpiral > azimuthalExtent_m) {
125 return true;
126 }
127
128 double h = std::pow(normRadius, k_m)*Bz_m;
129 std::vector<double> fringeDerivatives(maxOrder_m+1, 0.);
130 for (size_t i = 0; i < fringeDerivatives.size(); ++i) {
131 fringeDerivatives[i] = endField_m->function(phiSpiral, i); // d^i_phi f
132 }
133
134 double zOverR = r0Sign_m * z/r;
135 std::vector<double> zOverRVec(dfCoefficients_m.size()+1); // zOverR^n
136 zOverRVec[0] = 1.0;
137 for (size_t n = 1; n < zOverRVec.size(); ++n) {
138 zOverRVec[n] = zOverRVec[n-1] * zOverR;
139 }
140 for (size_t n = 0; n < dfCoefficients_m.size(); n += 2) {
141 double f2n = 0;
142 Vector_t deltaB;
143 for (size_t i = 0; i < dfCoefficients_m[n].size(); ++i) {
144 f2n += dfCoefficients_m[n][i]*fringeDerivatives[i];
145 }
146 deltaB[1] = f2n * h * zOverRVec[n]; // Bz = sum(f_2n * h * (z/r)^2n
147 if (maxOrder_m >= n+1) {
148 double f2nplus1 = 0;
149 for (size_t i = 0; i < dfCoefficients_m[n+1].size() && n+1 < dfCoefficients_m.size(); ++i) {
150 f2nplus1 += dfCoefficients_m[n+1][i]*fringeDerivatives[i];
151 }
152 deltaB[0] = r0Sign_m * (f2n * (k_m-n) / (n+1) - tanDelta_m * f2nplus1) * h * zOverRVec[n+1]; // Br
153 deltaB[2] = r0Sign_m * f2nplus1 * h * zOverRVec[n+1]; // Bphi = sum(f_2n+1 * h * (z/r)^2n+1
154 }
155 B += deltaB;
156 }
157 return false;
158}
159
161 dfCoefficients_m = std::vector<std::vector<double> >(maxOrder_m+1);
162 dfCoefficients_m[0] = std::vector<double>(1, 1.); // f_0 = 1.*0th derivative
163 for (size_t n = 0; n < maxOrder_m; n += 2) { // n indexes the power in z
164 dfCoefficients_m[n+1] = std::vector<double>(dfCoefficients_m[n].size()+1, 0);
165 for (size_t i = 0; i < dfCoefficients_m[n].size(); ++i) { // i indexes the derivative
166 dfCoefficients_m[n+1][i+1] = dfCoefficients_m[n][i]/(n+1);
167 }
168 if (n+1 == maxOrder_m) {
169 break;
170 }
171 dfCoefficients_m[n+2] = std::vector<double>(dfCoefficients_m[n].size()+2, 0);
172 for (size_t i = 0; i < dfCoefficients_m[n].size(); ++i) { // i indexes the derivative
173 dfCoefficients_m[n+2][i] = -(k_m-n)*(k_m-n)/(n+1)*dfCoefficients_m[n][i]/(n+2);
174 }
175 for (size_t i = 0; i < dfCoefficients_m[n+1].size(); ++i) { // i indexes the derivative
176 dfCoefficients_m[n+2][i] += 2*(k_m-n)*tanDelta_m*dfCoefficients_m[n+1][i]/(n+2);
177 dfCoefficients_m[n+2][i+1] -= (1+tanDelta_m*tanDelta_m)*dfCoefficients_m[n+1][i]/(n+2);
178 }
179 }
180
181}
182
184 if (endField_m != nullptr) {
185 delete endField_m;
186 }
187 endField_m = endField;
188}
189
190// Note this is tested in OpalScalingFFAMagnetTest.*
192 if (endFieldName_m.empty()) { // no end field is defined
193 return;
194 }
195
196 std::shared_ptr<endfieldmodel::EndFieldModel> efm
198
199 endfieldmodel::EndFieldModel* newEFM = efm->clone();
200 newEFM->rescale(1.0/std::abs(r0_m));
201 setEndField(newEFM);
203
204 double defaultExtent = (newEFM->getEndLength()*4.+
205 newEFM->getCentreLength());
206 if (phiStart_m < 0.0) {
207 setPhiStart(defaultExtent/2.0);
208 } else {
209 setPhiStart(getPhiStart()+newEFM->getCentreLength()*0.5);
210 }
211 if (phiEnd_m < 0.0) {
212 setPhiEnd(defaultExtent);
213 }
214 if (azimuthalExtent_m < 0.0) {
215 setAzimuthalExtent(newEFM->getEndLength()*5.+
216 newEFM->getCentreLength()/2.0);
217 }
220}
Tps< T > log(const Tps< T > &x)
Natural logarithm.
Definition: TpsMath.h:182
Tps< T > cos(const Tps< T > &x)
Cosine.
Definition: TpsMath.h:129
Tps< T > pow(const Tps< T > &x, int y)
Integer power.
Definition: TpsMath.h:76
Tps< T > sin(const Tps< T > &x)
Sine.
Definition: TpsMath.h:111
Tps< T > sqrt(const Tps< T > &x)
Square root.
Definition: TpsMath.h:91
PETE_TUTree< FnAbs, typename T::PETE_Expr_t > abs(const PETE_Expr< T > &l)
PETE_TBTree< FnArcTan2, PETE_Scalar< Vektor< T1, Dim > >, typename T2::PETE_Expr_t > atan2(const Vektor< T1, Dim > &l, const PETE_Expr< T2 > &r)
const std::string name
virtual void visitScalingFFAMagnet(const ScalingFFAMagnet &)=0
Apply the algorithm to a scaling FFA magnet.
Interface for a single beam element.
Definition: Component.h:50
PartBunchBase< double, 3 > * RefPartBunch_m
Definition: Component.h:191
static std::shared_ptr< EndFieldModel > getEndFieldModel(std::string name)
Look up the EndFieldModel that has a given name.
virtual void rescale(double scaleFactor)=0
Rescale the end field lengths and offsets by a factor x0.
virtual double getCentreLength() const =0
Return the nominal flat top length of the magnet.
virtual double getEndLength() const =0
Return the nominal end field length of the magnet.
virtual void setMaximumDerivative(size_t n)=0
Set the maximum derivative that will be required to be calculated.
virtual double function(double x, int n) const =0
Return the value of the function or its n^th derivative.
virtual EndFieldModel * clone() const =0
Inheritable copy constructor - returns a deep copy of the EndFieldModel.
Sector bending magnet with an FFA-style field index and spiral end shape.
bool getFieldValue(const Vector_t &R, Vector_t &B) const
Calculate the field at some arbitrary position in cartesian coordinates.
std::string endFieldName_m
void setupEndField()
setupEndField does some end field and geometry set-up
void accept(BeamlineVisitor &visitor) const override
Accept a beamline visitor.
void setAzimuthalExtent(double azimuthalExtent)
Set the maximum azimuthal displacement from \psi=0.
void finalise() override
Finalise the ScalingFFAMagnet - sets bunch to nullptr.
endfieldmodel::EndFieldModel * endField_m
void initialise()
Initialise the ScalingFFAMagnet.
ScalingFFAMagnet * clone() const override
Inheritable copy constructor.
ScalingFFAMagnet(const std::string &name)
Construct a new ScalingFFAMagnet.
bool getFieldValueCylindrical(const Vector_t &R, Vector_t &B) const
Calculate the field at some arbitrary position in cylindrical coordinates.
void calculateDfCoefficients()
Calculate the df coefficients, ready for field generation.
void setPhiStart(double phiStart)
Set the offset of the magnet centre from the start.
EMField & getField() override
Return a dummy (0.) field value (what is this for?)
~ScalingFFAMagnet()
Destructor - deletes map.
void setEndField(endfieldmodel::EndFieldModel *endField)
Set the fringe field.
BMultipoleField dummy
void setPhiEnd(double phiEnd)
Set the offset of the magnet end from the start.
std::vector< std::vector< double > > dfCoefficients_m
void initialise(PartBunchBase< double, 3 > *bunch, double &startField, double &endField) override
Initialise the ScalingFFAMagnet.
double getPhiStart() const
Get the offset of the magnet centre from the start.
BGeometryBase & getGeometry() override
Return the cell geometry.
bool bends() const override
Return true - ScalingFFAMagnet always bends the reference particle.
PlanarArcGeometry planarArcGeometry_m
Abstract base class for accelerator geometry classes.
Definition: Geometry.h:43
void setCurvature(double)
Set curvature.
virtual void setElementLength(double)
Set length.
Abstract base class for electromagnetic fields.
Definition: EMField.h:188