OPAL (Object Oriented Parallel Accelerator Library)  2024.1
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"
20 #include "Structure/LossDataSink.h"
21 #include "Physics/Units.h"
23 
24 extern Inform *gmsg;
25 
27 {}
28 
29 OutputPlane::OutputPlane(const std::string& name):
30  PluginElement(name),
31  maxIterations_m(10),
32  algorithm_m(algorithm::INTERPOLATION),
33  verbose_m(0) {
34 }
35 
37  PluginElement(right),
38  maxIterations_m(10) {
39 
40  field_m = right.field_m;
41  normal_m = right.normal_m;
42  centre_m = right.centre_m;
47  tolerance_m = right.tolerance_m;
48  nullfield_m = right.nullfield_m;
49  geom_m = right.geom_m;
50  recentre_m = right.recentre_m;
51  algorithm_m = right.algorithm_m;
52  verbose_m = right.verbose_m;
53 }
54 
56 {}
57 
59  ElementBase* element = dynamic_cast<ElementBase*>(new OutputPlane(*this));
60  return element;
61 }
62 
63 void OutputPlane::accept(BeamlineVisitor& visitor) const {
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 
167 bool 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);
211  } else if (algorithm_m == algorithm::INTERPOLATION) {
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 
247 void 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 
290 bool 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 
322 }
algorithm algorithm_m
Definition: OutputPlane.h:251
Tps< T > sqrt(const Tps< T > &x)
Square root.
Definition: TpsMath.h:91
constexpr double c
The velocity of light in m/s.
Definition: Physics.h:45
void rk4Test(double tstep, double chargeToMass, double &t, Vector_t &R, Vector_t &P)
virtual void visitOutputPlane(const OutputPlane &)=0
Apply the algorithm to an outputplane.
void setCentre(Vector_t centre)
Definition: OutputPlane.h:284
PETE_TUTree< FnAbs, typename T::PETE_Expr_t > abs(const PETE_Expr< T > &l)
ParticleAttrib< Vector_t > P
ElementBase * clone() const override
Definition: OutputPlane.cpp:58
ParticleAttrib< short > bunchNum
bool checkOne(const int index, const double tstep, double chargeToMass, double &t, Vector_t &R, Vector_t &P)
virtual void doGoOffline() override
Hook for goOffline.
Definition: OutputPlane.cpp:90
ParticleAttrib< double > M
Vector_t centre_m
Definition: OutputPlane.h:243
virtual void accept(BeamlineVisitor &) const override
Definition: OutputPlane.cpp:63
double horizontalExtent_m
Definition: OutputPlane.h:246
virtual const std::string & getName() const
Get element name.
Inform & endl(Inform &inf)
Definition: Inform.cpp:42
std::unique_ptr< LossDataSink > lossDs_m
Pointer to Loss instance.
double tolerance_m
Definition: OutputPlane.h:248
virtual bool apply(const size_t &i, const double &t, Vector_t &E, Vector_t &B)
Definition: Component.cpp:99
T euclidean_norm(const Vector< T > &)
Euclidean norm.
Definition: Vector.h:243
constexpr double mm2m
Definition: Units.h:29
size_t getLocalNum() const
void recentre(Vector_t R, Vector_t P)
Definition: Inform.h:42
double verticalExtent_m
Definition: OutputPlane.h:245
ElementType
Definition: ElementBase.h:88
ParticleAttrib< double > Q
constexpr double q_e
The elementary charge in As.
Definition: Physics.h:69
virtual bool doCheck(PartBunchBase< double, 3 > *bunch, const int turnnumber, const double t, const double tstep) override
Record probe hits when bunch particles pass.
void getDerivatives(const Vector_t &R, const Vector_t &P, const double &t, const double &chargeToMass, double *yp) const
const std::string name
ParticlePos_t & R
size_t maxIterations_m
Definition: OutputPlane.h:247
Vector_t normal_m
Definition: OutputPlane.h:242
ElementType getType() const override
Get element type std::string.
ParticleIndex_t & ID
void RK4Step(const double &tstep, const double &chargeToMass, const double &t, Vector_t &R, Vector_t &P) const
Definition: OutputPlane.cpp:94
constexpr double GeV2eV
Definition: Units.h:68
virtual void doInitialise(PartBunchBase< double, 3 > *) override
Initialise peakfinder file.
Definition: OutputPlane.cpp:67
virtual ~OutputPlane()
Definition: OutputPlane.cpp:55
StraightGeometry geom_m
Definition: OutputPlane.h:250
void interpolation(double &t, Vector_t &R, Vector_t &P)
constexpr double kG2T
Definition: Units.h:59
constexpr double kV2V
Definition: Units.h:62
Inform * gmsg
Definition: Main.cpp:70
NullField nullfield_m
Definition: OutputPlane.h:249
double dot(const Vector3D &lhs, const Vector3D &rhs)
Vector dot product.
Definition: Vector3D.cpp:118
void setNormal(Vector_t normal)
Definition: OutputPlane.h:275
Component * field_m
Definition: OutputPlane.h:241
double radialExtent_m
Definition: OutputPlane.h:244