OPAL (Object Oriented Parallel Accelerator Library)  2024.1
OPAL
Ring.cpp
Go to the documentation of this file.
1 //
2 // Class Ring
3 // Defines the abstract interface for a ring type geometry.
4 //
5 // Copyright (c) 2012 - 2023, Chris Rogers, STFC Rutherford Appleton Laboratory, Didcot, UK
6 // All rights reserved
7 //
8 // This file is part of OPAL.
9 //
10 // OPAL is free software: you can redistribute it and/or modify
11 // it under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with OPAL. If not, see <https://www.gnu.org/licenses/>.
17 //
18 #include "AbsBeamline/Ring.h"
19 
20 #include "Utility/Inform.h"
21 
25 #include "Fields/EMField.h"
26 #include "Physics/Units.h"
27 #include "Structure/LossDataSink.h"
28 
29 
30 #include <limits>
31 #include <sstream>
32 
33 // fairly generous tolerance here... maybe too generous? Maybe should be
34 // user parameter?
35 const double Ring::lengthTolerance_m = 1e-2;
36 const double Ring::angleTolerance_m = 1e-2;
37 
38 extern Inform* gmsg;
39 extern Inform* gmsgALL;
40 
41 Ring::Ring(const std::string& ring)
42  : Component(ring), planarArcGeometry_m(1, 1),
43  refPartBunch_m(nullptr),
44  lossDS_m(nullptr),
45  beamRInit_m(0.), beamPRInit_m(0.), beamPhiInit_m(0.),
46  latticeRInit_m(0.), latticePhiInit_m(0.), latticeThetaInit_m(0.),
47  isLocked_m(false), isClosed_m(true),
48  symmetry_m(0), cyclHarm_m(0), rfFreq_m(0),
49  phiStep_m(Physics::pi/100.),
50  ringSections_m(),
51  section_list_m() {
52  setRefPartBunch(nullptr);
53 }
54 
55 void Ring::accept(BeamlineVisitor& visitor) const {
56  visitor.visitRing(*this);
57 }
58 
59 Ring::Ring(const Ring& ring)
60  : Component(ring.getName()),
61  planarArcGeometry_m(ring.planarArcGeometry_m),
62  lossDS_m(nullptr),
63  beamRInit_m(ring.beamRInit_m),
64  beamPRInit_m(ring.beamPRInit_m),
65  beamPhiInit_m(ring.beamPhiInit_m),
66  beamThetaInit_m(ring.beamThetaInit_m),
67  latticeRInit_m(ring.latticeRInit_m),
68  latticePhiInit_m(ring.latticePhiInit_m),
69  latticeThetaInit_m(ring.latticeThetaInit_m),
70  willDoAperture_m(ring.willDoAperture_m),
71  minR2_m(ring.minR2_m),
72  maxR2_m(ring.maxR2_m),
73  isLocked_m(ring.isLocked_m),
74  isClosed_m(ring.isClosed_m),
75  symmetry_m(ring.symmetry_m),
76  cyclHarm_m(ring.cyclHarm_m),
77  phiStep_m(ring.phiStep_m),
78  ringSections_m(),
79  section_list_m(ring.section_list_m.size()) {
81  if (ring.lossDS_m != nullptr)
82  throw GeneralClassicException("Ring::Ring(const Ring&)",
83  "Can't copy construct LossDataSink so copy constructor fails");
84  for (size_t i = 0; i < section_list_m.size(); ++i) {
85  section_list_m[i] = new RingSection(*ring.section_list_m[i]);
86  }
88 }
89 
91  for (size_t i = 0; i < section_list_m.size(); ++i)
92  delete section_list_m[i];
93 }
94 
95 bool Ring::apply(const size_t& id, const double& t,
96  Vector_t& E, Vector_t& B) {
97 
98  bool flagNeedUpdate = apply(refPartBunch_m->R[id], refPartBunch_m->P[id], t, E, B);
99 
100  if (flagNeedUpdate) {
101  *gmsgALL << level4 << getName() << ": particle " << id
102  << " at " << refPartBunch_m->R[id]
103  << " m out of the field map boundary" << endl;
104 
106  refPartBunch_m->R[id] , refPartBunch_m->P[id],
107  t,
108  refPartBunch_m->Q[id], refPartBunch_m->M[id]));
109 
110  refPartBunch_m->Bin[id] = -1;
111  }
112 
113  return flagNeedUpdate;
114 }
115 
116 bool Ring::apply(const Vector_t& R, const Vector_t& /*P*/,
117  const double& t, Vector_t& E, Vector_t& B) {
118  B = Vector_t(0.0, 0.0, 0.0);
119  E = Vector_t(0.0, 0.0, 0.0);
120 
121  std::vector<RingSection*> sections = getSectionsAt(R); // I think this doesn't actually use R -DW
122  bool outOfBounds = true;
123  // assume field maps don't set B, E to 0...
124  if (willDoAperture_m) {
125  double rSquared = R[0] * R[0] + R[1] * R[1];
126  if (rSquared < minR2_m || rSquared > maxR2_m) {
127  return true;
128  }
129  }
130  for (size_t i = 0; i < sections.size(); ++i) {
131  Vector_t B_temp(0.0, 0.0, 0.0);
132  Vector_t E_temp(0.0, 0.0, 0.0);
133  outOfBounds &= sections[i]->getFieldValue(R, refPartBunch_m->get_centroid(), t, E_temp, B_temp);
134  B += (scale_m * B_temp);
135  E += (scale_m * E_temp);
136  }
137  return outOfBounds;
138 }
139 
141  // lossDS_m is a borrowed pointer - we never delete it
142  lossDS_m = sink;
143 }
144 
145 void Ring::getDimensions(double& /*zBegin*/, double& /*zEnd*/) const {
146  throw GeneralClassicException("Ring::getDimensions",
147  "Cannot get s-dimension of a ring");
148 }
149 
151  online_m = true;
152  setRefPartBunch(bunch);
153  setLossDataSink(new LossDataSink(getName(), false));
154 }
155 
157  double& /*startField*/, double& /*endField*/) {
158  initialise(bunch);
159 }
160 
162  lossDS_m->save();
163  online_m = false;
164  setLossDataSink(nullptr);
165 }
166 
168  RefPartBunch_m = bunch; // inherited from Component
169  refPartBunch_m = bunch; // private data (obeys style guide)
170 }
171 
172 std::vector<RingSection*> Ring::getSectionsAt(const Vector_t& /*r*/) {
173  return section_list_m;
174 }
175 
177  // rotation from start to end in local coordinates
178  // Euclid3D/Rotation3D doesnt have a "getAngle" method so we use fairly
179  // obscure technique to extract it.
180  Vector3D rotationTest(1., 0., 0.);
181  rotationTest = delta.getRotation() * rotationTest;
182  double deltaAngle = std::atan2(rotationTest(2), rotationTest(0));
183  Rotation3D elementRotation = Rotation3D::ZRotation(deltaAngle);
184  return elementRotation;
185 }
186 
187 void Ring::checkMidplane(Euclid3D delta) const {
188  if (std::abs(delta.getVector()(2)) > lengthTolerance_m ||
189  std::abs(delta.getRotation().getAxis()(0)) > angleTolerance_m ||
190  std::abs(delta.getRotation().getAxis()(1)) > angleTolerance_m) {
191  throw GeneralClassicException("Ring::checkMidplane",
192  std::string("Placement of elements out of the ")+
193  "midplane is not supported by Ring");
194  }
195 }
196 
198  Vector3D v = delta.getVector();
199  Vector3D r = delta.getRotation().getAxis();
200  // Euclid3D euclid(v(0), -v(2), v(1), r(0), -r(2), r(1));
201  Euclid3D euclid(v(2), v(0), -v(1), r(2), r(0), -r(1));
202  delta = euclid;
203 }
204 
205 
207  if (!section_list_m.empty()) {
208  return section_list_m.back()->getEndPosition();
209  } else {
210  return getStartPosition();
211  }
212 }
213 
217  0.);
218 }
219 
221  if (!section_list_m.empty()) {
222  return section_list_m.back()->getEndNormal();
223  } else {
224  return getStartNormal();
225  }
226 }
227 
231  0.);
232 }
233 
234 void Ring::appendElement(const Component& element) {
235  if (isLocked_m) {
236  throw GeneralClassicException("Ring::appendElement",
237  "Attempt to append element " + element.getName() +
238  " when ring is locked");
239  }
240 
242  Vector_t startPos = getNextPosition();
243  Vector_t startNorm = getNextNormal();
244 
245  section->setComponent(dynamic_cast<Component*>(element.clone()));
246  section->setStartPosition(startPos);
247  section->setStartNormal(startNorm);
248  section->handleOffset();
249  // delta is transform from start of bend to end with x, z as horizontal
250  // I failed to get Rotation3D to work so use rotations written by hand.
251  // Probably an error in my call to Rotation3D.
252  Euclid3D delta = section->getComponent()->getGeometry().getTotalTransform();
254  checkMidplane(delta);
255 
256  double placeF = std::atan2(startNorm(0), startNorm(1)); // angle between y axis and norm
257  Vector_t endPos = Vector_t(+delta.getVector()(0) * std::sin(placeF) + delta.getVector()(1) * std::cos(placeF),
258  +delta.getVector()(0) * std::cos(placeF) - delta.getVector()(1) * std::sin(placeF),
259  0) + startPos;
260  section->setEndPosition(endPos);
261 
262  double endF = delta.getRotation().getAxis()(2);//+
263  //atan2(delta.getVector()(1), delta.getVector()(0));
264  Vector_t endNorm = Vector_t(+startNorm(0) * std::cos(endF) - startNorm(1) * std::sin(endF),
265  +startNorm(0) * std::sin(endF) + startNorm(1) * std::cos(endF),
266  0);
267  section->setEndNormal(endNorm);
268 
269  section->setComponentPosition(startPos);
270  double orientation = std::atan2(startNorm(1), startNorm(0));
271  section->setComponentOrientation(Vector_t(0, 0, orientation));
272 
273  section_list_m.push_back(section);
274  *gmsg << "* Added " << element.getName() << " to Ring" << endl;
275  *gmsg << "* Start position ("
276  << section->getStartPosition()(0) << ", "
277  << section->getStartPosition()(1) << ", "
278  << section->getStartPosition()(2) << ") normal ("
279  << section->getStartNormal()(0) << ", "
280  << section->getStartNormal()(1) << ", "
281  << section->getStartNormal()(2) << ")" << endl;
282  *gmsg << "* End position ("
283  << section->getEndPosition()(0) << ", "
284  << section->getEndPosition()(1) << ", "
285  << section->getEndPosition()(2) << ") normal ("
286  << section->getEndNormal()(0) << ", "
287  << section->getEndNormal()(1) << ", "
288  << section->getEndNormal()(2) << ")" << endl;
289 }
290 
292  if (isLocked_m) {
293  throw GeneralClassicException("Ring::lockRing",
294  "Attempt to lock ring when it is already locked");
295  }
296  // check for any elements at all
297  size_t sectionListSize = section_list_m.size();
298  if (sectionListSize == 0) {
299  throw GeneralClassicException("Ring::lockRing",
300  "Failed to find any elements in Ring");
301  }
302  // Apply symmetry properties; I think it is fastest to just duplicate
303  // elements rather than try to do some angle manipulations when we do field
304  // lookups because we do field lookups in Cartesian coordinates in general.
305  *gmsg << "Applying symmetry to Ring" << endl;
306  for (int i = 1; i < symmetry_m; ++i) {
307  for (size_t j = 0; j < sectionListSize; ++j) {
308  appendElement(*section_list_m[j]->getComponent());
309  }
310  }
311  //resetAzimuths();
312  // Check that the ring is closed within tolerance and close exactly
313  if (isClosed_m)
314  checkAndClose();
316  isLocked_m = true;
317  for (size_t i = 0; i < section_list_m.size(); i++) {
318  }
319 }
320 
322  for (size_t i = 0; i < section_list_m.size(); ++i) {
323  Vector_t startPos = section_list_m[i]->getEndPosition();
324  Vector_t startDir = section_list_m[i]->getEndNormal();
325  Vector_t endPos = section_list_m[i]->getEndPosition();
326  Vector_t endDir = section_list_m[i]->getEndNormal();
327  if (!section_list_m[i]->isOnOrPastStartPlane(endPos)) {
328  section_list_m[i]->setEndPosition(startPos);
329  section_list_m[i]->setEndNormal(startDir);
330  section_list_m[i]->setStartPosition(endPos);
331  section_list_m[i]->setStartNormal(endDir);
332  }
333  }
334 }
335 
337  Vector_t first_pos = section_list_m[0]->getStartPosition();
338  Vector_t first_norm = section_list_m[0]->getStartNormal();
339  Vector_t last_pos = section_list_m.back()->getEndPosition();
340  Vector_t last_norm = section_list_m.back()->getEndNormal();
341  for (int i = 0; i < 3; ++i) {
342  if (std::abs(first_pos(i) - last_pos(i)) > lengthTolerance_m ||
343  std::abs(first_norm(i) - last_norm(i)) > angleTolerance_m)
344  throw GeneralClassicException("Ring::lockRing",
345  "Ring is not closed");
346  }
347  section_list_m.back()->setEndPosition(first_pos);
348  section_list_m.back()->setEndNormal(first_norm);
349 }
350 
352  size_t nSections = Physics::two_pi/phiStep_m+1;
353  ringSections_m = std::vector< std::vector<RingSection*> >(nSections);
354  for (size_t i = 0; i < ringSections_m.size(); ++i) {
355  double phi0 = i*phiStep_m;
356  double phi1 = (i+1)*phiStep_m;
357  for (size_t j = 0; j < section_list_m.size(); ++j) {
358  if (section_list_m[j]->doesOverlap(phi0, phi1))
359  ringSections_m[i].push_back(section_list_m[j]);
360  }
361  }
362 }
363 
365  if (section_list_m.empty()) {
366  return nullptr;
367  }
368  return section_list_m.back();
369 }
370 
371 bool Ring::sectionCompare(RingSection const* const sec1,
372  RingSection const* const sec2) {
373  return sec1 > sec2;
374 }
375 
376 void Ring::setRingAperture(double minR, double maxR) {
377  if (minR < 0 || maxR < 0) {
378  throw GeneralClassicException("Ring::setRingAperture",
379  "Could not parse negative or undefined aperture limit");
380  }
381 
382  willDoAperture_m = true;
383  minR2_m = minR * minR;
384  maxR2_m = maxR * maxR;
385 }
386 
388  if (i < 0 || i >= int(getNumberOfRingSections())) {
389  std::stringstream err;
390  err << "Attempt to get RingSection for element " << i
391  << ". Should be in range 0 <= i < " << getNumberOfRingSections();
392  throw GeneralClassicException("Ring::getSection(int)", err.str());
393  }
395  if (sec == nullptr) {
396  throw GeneralClassicException("Ring::getSection",
397  "Opal internal error - RingSection was null");
398  }
399  return sec;
400 }
401 
403  return section_list_m.size();
404 }
bool isClosed_m
Definition: Ring.h:398
virtual BGeometryBase & getGeometry()=0
Get geometry.
bool willDoAperture_m
Definition: Ring.h:389
Rotation in 3-dimensional space.
Definition: Rotation3D.h:46
Vector_t getStartNormal() const
Definition: Ring.cpp:228
LossDataSink * lossDS_m
Definition: Ring.h:374
double minR2_m
Definition: Ring.h:390
static const double lengthTolerance_m
Definition: Ring.h:417
constexpr double two_pi
The value of .
Definition: Physics.h:33
RingSection * getLastSectionPlaced() const
Definition: Ring.cpp:364
PETE_TUTree< FnAbs, typename T::PETE_Expr_t > abs(const PETE_Expr< T > &l)
void buildRingSections()
Definition: Ring.cpp:351
static Rotation3D ZRotation(double angle)
Make rotation.
Definition: Rotation3D.cpp:147
virtual Euclid3D getTotalTransform() const
Get transform.
Definition: Geometry.cpp:51
ParticleAttrib< Vector_t > P
void setEndPosition(Vector_t pos)
Definition: RingSection.h:146
double phiStep_m
Definition: Ring.h:412
void rotateToCyclCoordinates(Euclid3D &euclid3d) const
Definition: Ring.cpp:197
Vektor< double, 3 > Vector_t
Definition: Vektor.h:6
int symmetry_m
Definition: Ring.h:401
Vector_t get_centroid() const
void save(unsigned int numSets=1, OpalData::OpenMode openMode=OpalData::OpenMode::UNDEFINED)
virtual void getDimensions(double &zBegin, double &zEnd) const override
Definition: Ring.cpp:145
std::vector< RingSectionList > ringSections_m
Definition: Ring.h:413
clearpage section
Definition: multipole_t.tex:2
void addParticle(const OpalParticle &, const boost::optional< std::pair< int, short int >> &turnBunchNumPair=boost::none)
const Rotation3D & getRotation() const
Get rotation.
Definition: Euclid3D.cpp:64
ParticleAttrib< double > M
virtual void initialise(PartBunchBase< double, 3 > *bunch, double &startField, double &endField) override
Definition: Ring.cpp:156
void checkMidplane(Euclid3D delta) const
Definition: Ring.cpp:187
PartBunchBase< double, 3 > * RefPartBunch_m
Definition: Component.h:191
void checkAndClose()
Definition: Ring.cpp:336
virtual const std::string & getName() const
Get element name.
Inform & endl(Inform &inf)
Definition: Inform.cpp:42
void setComponentPosition(Vector_t position)
Definition: RingSection.h:158
virtual void finalise() override
Definition: Ring.cpp:161
PETE_TBTree< FnArcTan2, PETE_Scalar< Vektor< T1, Dim > >, typename T2::PETE_Expr_t > atan2(const Vektor< T1, Dim > &l, const PETE_Expr< T2 > &r)
void appendElement(const Component &element)
Definition: Ring.cpp:234
void resetAzimuths()
Definition: Ring.cpp:321
void setEndNormal(Vector_t orientation)
Definition: RingSection.h:216
Vector_t getStartPosition() const
Definition: RingSection.h:137
RingSectionList section_list_m
Definition: Ring.h:414
size_t getNumberOfRingSections() const
Definition: Ring.cpp:402
virtual void visitRing(const Ring &)=0
Apply the algorithm to a ring.
std::vector< RingSection * > getSectionsAt(const Vector_t &pos)
Definition: Ring.cpp:172
void setStartNormal(Vector_t orientation)
Definition: RingSection.h:212
Vector_t getStartNormal() const
Definition: RingSection.h:143
RingSection * getSection(int i) const
Definition: Ring.cpp:387
virtual void accept(BeamlineVisitor &visitor) const override
Definition: Ring.cpp:55
Component placement handler in ring geometry.
Definition: RingSection.h:58
void setRefPartBunch(PartBunchBase< double, 3 > *bunch)
Definition: Ring.cpp:167
bool online_m
Definition: Component.h:192
Inform & level4(Inform &inf)
Definition: Inform.cpp:48
Vector_t getStartPosition() const
Definition: Ring.cpp:214
Definition: Inform.h:42
bool isLocked_m
Definition: Ring.h:394
virtual bool apply(const size_t &id, const double &t, Vector_t &E, Vector_t &B) override
Definition: Ring.cpp:95
void setComponentOrientation(Vector_t orientation)
Definition: RingSection.h:207
double scale_m
Definition: Ring.h:403
void handleOffset()
Inform * gmsgALL
Definition: Main.cpp:71
void setRingAperture(double minR, double maxR)
Definition: Ring.cpp:376
void setLossDataSink(LossDataSink *sink)
Definition: Ring.cpp:140
ParticleAttrib< double > Q
Vector_t getEndNormal() const
Definition: RingSection.h:155
double latticePhiInit_m
Definition: Ring.h:384
Component * getComponent() const
Definition: RingSection.h:131
void setStartPosition(Vector_t pos)
Definition: RingSection.h:134
double latticeThetaInit_m
Definition: Ring.h:385
static const double angleTolerance_m
Definition: Ring.h:418
Tps< T > cos(const Tps< T > &x)
Cosine.
Definition: TpsMath.h:129
Vector_t getNextPosition() const
Definition: Ring.cpp:206
Vector_t getNextNormal() const
Definition: Ring.cpp:220
const double pi
Definition: fftpack.cpp:894
ParticlePos_t & R
void lockRing()
Definition: Ring.cpp:291
virtual ElementBase * clone() const =0
Return clone.
Displacement and rotation in space.
Definition: Euclid3D.h:68
Vector3D getAxis() const
Get axis vector.
Definition: Rotation3D.cpp:91
PartBunchBase< double, 3 > * refPartBunch_m
Definition: Ring.h:369
constexpr double e
The value of .
Definition: Physics.h:39
Tps< T > sec(const Tps< T > &x)
Secant.
Definition: TpsMath.h:153
Rotation3D getRotationStartToEnd(Euclid3D delta) const
Definition: Ring.cpp:176
ParticleAttrib< int > Bin
Ring describes a ring type geometry for tracking.
Definition: Ring.h:53
static bool sectionCompare(RingSection const *const sec1, RingSection const *const sec2)
Definition: Ring.cpp:371
void setComponent(Component *component)
Definition: RingSection.h:125
Interface for a single beam element.
Definition: Component.h:50
Tps< T > sin(const Tps< T > &x)
Sine.
Definition: TpsMath.h:111
double latticeRInit_m
Definition: Ring.h:383
double maxR2_m
Definition: Ring.h:391
Vector_t getEndPosition() const
Definition: RingSection.h:149
Inform * gmsg
Definition: Main.cpp:70
A 3-dimension vector.
Definition: Vector3D.h:31
const Vector3D & getVector() const
Get displacement.
Definition: Euclid3D.cpp:59
virtual ~Ring()
Definition: Ring.cpp:90