OPAL (Object Oriented Parallel Accelerator Library) 2024.2
OPAL
OutputPlane.cpp
Go to the documentation of this file.
1// Copyright (c) 2023, Chris Rogers
2// All rights reserved
3//
4// This file is part of OPAL.
5//
6// OPAL is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10//
11// You should have received a copy of the GNU General Public License
12// along with OPAL. If not, see <https://www.gnu.org/licenses/>.
13//
14
16
19#include "Physics/Physics.h"
21#include "Physics/Units.h"
23
24extern Inform *gmsg;
25
28
29OutputPlane::OutputPlane(const std::string& name):
32 algorithm_m(algorithm::INTERPOLATION),
33 verbose_m(0) {
34}
35
54
57
59 ElementBase* element = dynamic_cast<ElementBase*>(new OutputPlane(*this));
60 return element;
61}
62
64 visitor.visitOutputPlane(*this);
65}
66
68 //void OutputPlane::doInitialise() {
69 *gmsg << "* Initialize OutputPlane at " << centre_m << " with normal " << normal_m << "\n*";
70 if (radialExtent_m >= 0) {
71 *gmsg << " Radial extent " << radialExtent_m;
72 }
73 if (horizontalExtent_m >= 0) {
74 *gmsg << " Horizontal extent " << horizontalExtent_m;
75 }
76 if (verticalExtent_m >= 0) {
77 *gmsg << " Vertical extent " << verticalExtent_m;
78 }
79 if (field_m != nullptr) {
80 *gmsg << " Using field map " << field_m->getName();
81 } else {
82 *gmsg << " Using empty field map";
83 }
84 if (recentre_m >= 0) {
85 *gmsg << " Recentre event " << recentre_m;
86 }
87 *gmsg << endl;
88}
89
91 *gmsg << "* OutputPlane goes offline " << getName() << endl;
92}
93
95 const double& tStep,
96 const double& chargeToMass,
97 const double& t,
98 Vector_t& R1,
99 Vector_t& P1) const {
100 double thalfStep = tStep / 2.;
101 double tPlusHalf = t + thalfStep;
102 double tPlusStep = t + tStep;
103 // f = dy/dt
104 // f1 = f(x,t)
105 double deriv1[6];
106
107 getDerivatives(R1, P1, t, chargeToMass, deriv1);
108 // f2 = f(x+dt*f1/2, t+dt/2 )
109 double deriv2[6];
110 Vector_t R2(R1[0] + thalfStep * deriv1[0], R1[1] + thalfStep * deriv1[1], R1[2] + thalfStep * deriv1[2]);
111 Vector_t P2(P1[0] + thalfStep * deriv1[3], P1[1] + thalfStep * deriv1[4], P1[2] + thalfStep * deriv1[5]);
112 getDerivatives(R2, P2, tPlusHalf, chargeToMass, deriv2);
113
114 // f3 = f( x+dt*f2/2, t+dt/2 )
115 double deriv3[6];
116 Vector_t R3(R1[0] + thalfStep * deriv2[0], R1[1] + thalfStep * deriv2[1], R1[2] + thalfStep * deriv2[2]);
117 Vector_t P3(P1[0] + thalfStep * deriv2[3], P1[1] + thalfStep * deriv2[4], P1[2] + thalfStep * deriv2[5]);
118 getDerivatives(R3, P3, tPlusHalf, chargeToMass, deriv3);
119
120 // f4 = f(x+dt*f3, t+dt ).
121 double deriv4[6];
122 Vector_t R4(R1[0] + tStep * deriv3[0], R1[1] + tStep * deriv3[1], R1[2] + tStep * deriv3[2]);
123 Vector_t P4(P1[0] + tStep * deriv3[3], P1[1] + tStep * deriv3[4], P1[2] + tStep * deriv3[5]);
124 getDerivatives(R4, P4, tPlusStep, chargeToMass, deriv4);
125
126 // Return x(t+dt) computed from fourth-order R-K.
127 R1[0] += (deriv1[0] + deriv4[0] + 2. * (deriv2[0] + deriv3[0])) * tStep / 6.;
128 R1[1] += (deriv1[1] + deriv4[1] + 2. * (deriv2[1] + deriv3[1])) * tStep / 6.;
129 R1[2] += (deriv1[2] + deriv4[2] + 2. * (deriv2[2] + deriv3[2])) * tStep / 6.;
130 P1[0] += (deriv1[3] + deriv4[3] + 2. * (deriv2[3] + deriv3[3])) * tStep / 6.;
131 P1[1] += (deriv1[4] + deriv4[4] + 2. * (deriv2[4] + deriv3[4])) * tStep / 6.;
132 P1[2] += (deriv1[5] + deriv4[5] + 2. * (deriv2[5] + deriv3[5])) * tStep / 6.;
133}
134
136 const Vector_t& P,
137 const double& t,
138 const double& chargeToMass,
139 double* yp) const {
140
141 double gamma = std::sqrt(1 + (P[0] * P[0] + P[1] * P[1] + P[2] * P[2]));
142
143 Vector_t beta = P / gamma;
144 double betax = beta[0];
145 double betay = beta[1];
146 double betaz = beta[2];
147
148 yp[0] = Physics::c * betax;
149 yp[1] = Physics::c * betay;
150 yp[2] = Physics::c * betaz;
151
152 if (field_m == nullptr) {
153 throw GeneralClassicException("OutputPlane::getDerivatives",
154 "Field was null");
155 }
156 Vector_t externalB, externalE;
157 field_m->apply(R, P, t, externalE, externalB);
158 //double kiloGaussToTesla = 0.1;
159 externalB *= Units::kG2T;
160 externalE *= Units::kV2V / Units::mm2m / Physics::c;
161
162 yp[3] = chargeToMass * (externalB(2) * betay - externalB(1) * betaz+externalE(0));
163 yp[4] = chargeToMass * (externalB(0) * betaz - externalB(2) * betax + externalE(1));
164 yp[5] = chargeToMass * (externalB(1) * betax - externalB(0) * betay + externalE(2));
165}
166
167bool OutputPlane::checkOne(const int index, const double tstep, double chargeToMass,
168 double& t, Vector_t& R, Vector_t& P) {
169
170 // distance from particle to the output plane
171 // time units are ns
172 Vector_t delta = R-centre_m;
173 double distance = dot(normal_m, delta);
174
175 // maximum step, assuming no curvature, rough guess for relativistic beta
176 double betaEstimate = euclidean_norm(P);
177 if (betaEstimate > 1) {
178 betaEstimate = 1.0;
179 }
180 double sStep = tstep*betaEstimate*Physics::c;
181 if (verbose_m > 3) {
182 *gmsg << "* First check crossing of plane " << getName() << " at " << centre_m << " with normal " << normal_m << endl;
183 *gmsg << " Particle " << index << " with R " << R << " P " << P << " t0 " << t << " tstep " << tstep << endl;
184 *gmsg << " Distance prestep " << distance << " compared to s step length " << sStep << endl;
185 }
186 if (std::abs(distance) > sStep) {
187 // we can't cross the plane
188 return false;
189 }
190
191 Vector_t rTest(R);
192 Vector_t pTest(P);
193 RK4Step(tstep, chargeToMass, t, rTest, pTest);
194 double distanceTest = dot(normal_m, (rTest-centre_m));
195 if (verbose_m > 2) {
196 *gmsg << "* Second check crossing of plane " << getName() << " at " << centre_m << " with normal " << normal_m << endl;
197 *gmsg << " Particle " << index << " with R " << R << " P " << P << " tstep " << tstep << endl;
198 *gmsg << " After RK4 " << rTest << " " << pTest << endl;
199 *gmsg << " Step distance " << distanceTest << " compared to " << distance << endl;
200 }
201
202 if (distance != 0 && distanceTest/distance > 0) {
203 // step does not cross the plane - give up
204 // note that particle could cross and cross back the plane in a single
205 // time-step; in this case the particle is not registered
206 return false;
207 }
208
210 rk4Test(tstep, chargeToMass, t, R, P);
212 interpolation(t, R, P);
213 }
214
215 delta = R-centre_m;
216 if (verbose_m > 1) {
217 *gmsg << "* Track estimate RK4? " << bool(algorithm_m == algorithm::RK4STEP)
218 << " INTERPOLATION? " << bool(algorithm_m == algorithm::INTERPOLATION)
219 << " R " << R << " P " << P << " t " << t << endl
220 << " delta " << delta << endl;
221 }
222 if (horizontalExtent_m > 0 && delta[0] * delta[0] + delta[1] * delta[1] > horizontalExtent_m*horizontalExtent_m) {
223 // out of horizontal extent. Again, defined in global coordinates
224 return false;
225 }
226 if (verticalExtent_m > 0 && abs(delta[2]) > verticalExtent_m) {
227 // out of vertical extent; note that this is defined in global coordinates
228 // not in the coordinates of the plane
229 return false;
230 }
231 if (radialExtent_m > 0 &&
232 delta[0] * delta[0] + delta[1] * delta[1] + delta[2] * delta[2] > radialExtent_m * radialExtent_m) {
233 // out of radial extent
234 return false;
235 }
236 if (recentre_m == index) {
237 recentre(R, P);
238 *gmsg << "* Recentred output plane to " << centre_m
239 << " with normal " << normal_m << " by event " << index << endl;
240 }
241 if (verbose_m > 0) {
242 *gmsg << "* Found track" << endl;
243 }
244 return true;
245}
246
247void OutputPlane::rk4Test(double tstep, double chargeToMass,
248 double& t, Vector_t& R, Vector_t& P) {
249 double preStepDistance = 0.0;
250 double postStepDistance = dot(normal_m, (R-centre_m));
251 size_t iteration = 0;
252 while (std::abs(postStepDistance) > tolerance_m && iteration < maxIterations_m) {
253 preStepDistance = postStepDistance;
254 RK4Step(tstep, chargeToMass, t, R, P); // this updates R and P
255 if (verbose_m > 2) {
256 *gmsg << " RK4 iteration " << iteration << " step distance " << preStepDistance << " R " << R << " P " << P
257 << " centre " << centre_m << " d: " << R-centre_m << " t: " << t << " dt: " << tstep << endl;
258 Vector_t externalB, externalE;
259 field_m->apply(R, P, t, externalE, externalB);
260 *gmsg << " B " << externalB << " [kG] E " << externalE << " [MV/m] " << endl;
261 }
262 t += tstep;
263 postStepDistance = dot(normal_m, (R - centre_m));
264 if (postStepDistance/preStepDistance < 0) { // straddling the plane
265 // we stepped too far; step in opposite direction
266 // step length in ratio of distance to plane
267 tstep *= -abs(postStepDistance)/abs(postStepDistance - preStepDistance);
268 } else {
269 // we didn't step far enough; step in same direction
270 tstep *= abs(postStepDistance)/abs(postStepDistance - preStepDistance);
271 }
272 iteration++;
273 }
274}
275
277 // trajectory R = R0 + V dt
278 // plane = (X-X0).n
279 // intersection time t0 = (X0-R0).N / (V.N)
280 // intersection position = R0 + V dt
281 // relativistic gamma:
282 double gamma = std::sqrt(1 + P[0] * P[0] + P[1] * P[1] + P[2] * P[2]);
283 Vector_t velocity = P/gamma*Physics::c; // m/ns
284 double dt = dot((centre_m-R), normal_m) / dot(velocity, normal_m);
285 R += velocity*dt;
286 t += dt;
287}
288
289
290bool OutputPlane::doCheck(PartBunchBase<double, 3> *bunch, const int turnnumber,
291 const double t, const double tstep) {
292 size_t tempnum = bunch->getLocalNum();
293 for(unsigned int i = 0; i < tempnum; ++i) {
294 if (verbose_m > 2) {
295 *gmsg << "OutputPlane checking at time " << t
296 << " turn number " << turnnumber << " track id " << i << endl;
297 }
298 Vector_t R(bunch->R[i]);
299 Vector_t P(bunch->P[i]);
300 double t0(t);
301 double chargeToMass = bunch->Q[i] / Physics::q_e * Physics::c * Physics::c
302 / bunch->M[i]/Units::GeV2eV; // electron charge cancels
303 bool crossing = checkOne(i, tstep, chargeToMass, t0, R, P);
304 if (crossing && lossDs_m) {
305 nHits_m += 1;
306 lossDs_m->addParticle(OpalParticle(bunch->ID[i], R, P,
307 t0, bunch->Q[i], bunch->M[i]),
308 std::make_pair(turnnumber, bunch->bunchNum[i]));
309 }
310 }
311 return false;
312}
313
315 setCentre(R);
316 setNormal(P);
317 recentre_m = -1; // don't recentre again
318}
319
double dot(const Vector3D &lhs, const Vector3D &rhs)
Vector dot product.
Definition Vector3D.cpp:118
ElementType
Definition ElementBase.h:88
T euclidean_norm(const Vector< T > &)
Euclidean norm.
Definition Vector.h:243
Inform * gmsg
Definition Main.cpp:70
PETE_TUTree< FnAbs, typename T::PETE_Expr_t > abs(const PETE_Expr< T > &l)
Inform & endl(Inform &inf)
Definition Inform.cpp:42
const std::string name
constexpr double q_e
The elementary charge in As.
Definition Physics.h:69
constexpr double c
The velocity of light in m/s.
Definition Physics.h:45
constexpr double mm2m
Definition Units.h:29
constexpr double kV2V
Definition Units.h:62
constexpr double GeV2eV
Definition Units.h:68
constexpr double kG2T
Definition Units.h:59
ParticlePos_t & R
size_t getLocalNum() const
ParticleAttrib< double > M
ParticleAttrib< Vector_t > P
ParticleAttrib< double > Q
ParticleAttrib< short > bunchNum
ParticleIndex_t & ID
virtual void visitOutputPlane(const OutputPlane &)=0
Apply the algorithm to an outputplane.
virtual const std::string & getName() const
Get element name.
ElementBase(const std::string &name)
Constructor with given name.
Vector_t centre_m
virtual ~OutputPlane()
void setCentre(Vector_t centre)
double tolerance_m
void RK4Step(const double &tstep, const double &chargeToMass, const double &t, Vector_t &R, Vector_t &P) const
void rk4Test(double tstep, double chargeToMass, double &t, Vector_t &R, Vector_t &P)
OutputPlane(const std::string &name)
void setNormal(Vector_t normal)
void getDerivatives(const Vector_t &R, const Vector_t &P, const double &t, const double &chargeToMass, double *yp) const
bool checkOne(const int index, const double tstep, double chargeToMass, double &t, Vector_t &R, Vector_t &P)
NullField nullfield_m
virtual void doInitialise(PartBunchBase< double, 3 > *) override
Initialise peakfinder file.
ElementType getType() const override
Get element type std::string.
double radialExtent_m
StraightGeometry geom_m
ElementBase * clone() const override
size_t maxIterations_m
virtual void doGoOffline() override
Hook for goOffline.
void recentre(Vector_t R, Vector_t P)
algorithm algorithm_m
Vector_t normal_m
double horizontalExtent_m
virtual bool doCheck(PartBunchBase< double, 3 > *bunch, const int turnnumber, const double t, const double tstep) override
Record probe hits when bunch particles pass.
double verticalExtent_m
void interpolation(double &t, Vector_t &R, Vector_t &P)
Component * field_m
virtual void accept(BeamlineVisitor &) const override
std::unique_ptr< LossDataSink > lossDs_m
Pointer to Loss instance.
PluginElement(const std::string &name)
Constructor with given name.
Vektor< double, 3 > Vector_t