OPAL (Object Oriented Parallel Accelerator Library) 2024.2
OPAL
OrbitThreader.cpp
Go to the documentation of this file.
1//
2// Class OrbitThreader
3//
4// This class determines the design path by tracking the reference particle through
5// the 3D lattice.
6//
7// Copyright (c) 2016, Christof Metzger-Kraus, Helmholtz-Zentrum Berlin, Germany
8// 2017 - 2022 Christof Metzger-Kraus
9//
10// All rights reserved
11//
12// This file is part of OPAL.
13//
14// OPAL is free software: you can redistribute it and/or modify
15// it under the terms of the GNU General Public License as published by
16// the Free Software Foundation, either version 3 of the License, or
17// (at your option) any later version.
18//
19// You should have received a copy of the GNU General Public License
20// along with OPAL. If not, see <https://www.gnu.org/licenses/>.
21//
22
24
30#include "BasicActions/Option.h"
32#include "Physics/Units.h"
34#include "Utilities/Options.h"
35#include "Utilities/Util.h"
36
37#include <cmath>
38#include <filesystem>
39#include <iostream>
40#include <limits>
41
42#define HITMATERIAL 0x80000000
43#define EOL 0x40000000
44#define EVERYTHINGFINE 0x00000000
45extern Inform *gmsg;
46
48 const Vector_t &r,
49 const Vector_t &p,
50 double s,
51 double maxDiffZBunch,
52 double t,
53 double dt,
54 StepSizeConfig stepSizes,
55 OpalBeamline &bl) :
56 r_m(r),
57 p_m(p),
58 pathLength_m(s),
59 time_m(t),
60 dt_m(dt),
61 stepSizes_m(stepSizes),
62 zstop_m(stepSizes.getFinalZStop() + std::copysign(1.0, dt) * 2 * maxDiffZBunch),
64 errorFlag_m(0),
65 integrator_m(ref),
66 reference_m(ref)
67{
68 auto opal = OpalData::getInstance();
69 if (Ippl::myNode() == 0 && !opal->isOptimizerRun()) {
70 std::string fileName = Util::combineFilePath({
71 opal->getAuxiliaryOutputDirectory(),
72 opal->getInputBasename() + "_DesignPath.dat"
73 });
74 if (opal->getOpenMode() == OpalData::OpenMode::WRITE ||
75 !std::filesystem::exists(fileName)) {
76 logger_m.open(fileName);
77 logger_m << "#"
78 << std::setw(17) << "1 - s"
79 << std::setw(18) << "2 - Rx"
80 << std::setw(18) << "3 - Ry"
81 << std::setw(18) << "4 - Rz"
82 << std::setw(18) << "5 - Px"
83 << std::setw(18) << "6 - Py"
84 << std::setw(18) << "7 - Pz"
85 << std::setw(18) << "8 - Efx"
86 << std::setw(18) << "9 - Efy"
87 << std::setw(18) << "10 - Efz"
88 << std::setw(18) << "11 - Bfx"
89 << std::setw(18) << "12 - Bfy"
90 << std::setw(18) << "13 - Bfz"
91 << std::setw(18) << "14 - Ekin"
92 << std::setw(18) << "15 - t"
93 << std::endl;
94 } else {
95 logger_m.open(fileName, std::ios_base::app);
96 }
97
98 loggingFrequency_m = std::max(1.0, std::round(1e-11 / std::abs(dt_m)));
99 } else {
100 loggingFrequency_m = std::numeric_limits<size_t>::max();
101 }
102 pathLengthRange_m = stepSizes_m.getPathLengthRange();
103 pathLengthRange_m.enlargeIfOutside(pathLength_m);
104 pathLengthRange_m.enlargeIfOutside(zstop_m);
105 stepRange_m.enlargeIfOutside(0);
106 stepRange_m.enlargeIfOutside(stepSizes_m.getNumStepsFinestResolution());
107 distTrackBack_m = std::min(pathLength_m, std::max(0.0, maxDiffZBunch));
109}
110
111void OrbitThreader::checkElementLengths(const std::set<std::shared_ptr<Component>>& fields) {
112 while (!stepSizes_m.reachedEnd() && pathLength_m > stepSizes_m.getZStop()) {
113 ++ stepSizes_m;
114 }
115 if (stepSizes_m.reachedEnd()) {
116 return;
117 }
118 double driftLength = Physics::c * std::abs(stepSizes_m.getdT()) * euclidean_norm(p_m) / Util::getGamma(p_m);
119 for (const std::shared_ptr<Component>& field : fields) {
120 double length = field->getElementLength();
121 int numSteps = field->getRequiredNumberOfTimeSteps();
122
123 if (length < numSteps * driftLength) {
124 throw OpalException("OrbitThreader::checkElementLengths",
125 "The time step is too long compared to the length of the\n"
126 "element '" + field->getName() + "'\n" +
127 "The length of the element is: " + std::to_string(length) + "\n"
128 "The distance the particles drift in " + std::to_string(numSteps) +
129 " time step(s) is: " + std::to_string(numSteps * driftLength));
130 }
131 }
132}
133
135 double initialPathLength = pathLength_m;
136
137 auto allElements = itsOpalBeamline_m.getElementByType(ElementType::ANY);
138 std::set<std::string> visitedElements;
139
140 trackBack();
142 pathLengthRange_m.enlargeIfOutside(pathLength_m);
143
144 Vector_t nextR = r_m / (Physics::c * dt_m);
145 integrator_m.push(nextR, p_m, dt_m);
146 nextR *= Physics::c * dt_m;
147
148 setDesignEnergy(allElements, visitedElements);
149
150 auto elementSet = itsOpalBeamline_m.getElements(nextR);
151 std::set<std::shared_ptr<Component>> intersection, currentSet;
153 do {
154 checkElementLengths(elementSet);
155 if (containsCavity(elementSet)) {
156 autophaseCavities(elementSet, visitedElements);
157 }
158
159 double initialS = pathLength_m;
160 Vector_t initialR = r_m;
161 Vector_t initialP = p_m;
162 double maxDistance = computeDriftLengthToBoundingBox(elementSet, r_m, p_m);
163 integrate(elementSet, maxDistance);
164
165 registerElement(elementSet, initialS, initialR, initialP);
166
167 if (errorFlag_m == HITMATERIAL) {
168 // Shouldn't be reached because reference particle
169 // isn't stopped by collimators
170 pathLength_m += std::copysign(1.0, dt_m);
171 }
172
173 imap_m.add(initialS, pathLength_m, elementSet);
174
175 IndexMap::value_t::const_iterator it = elementSet.begin();
176 const IndexMap::value_t::const_iterator end = elementSet.end();
177 for (; it != end; ++ it) {
178 visitedElements.insert((*it)->getName());
179 }
180
181 setDesignEnergy(allElements, visitedElements);
182
183 currentSet = elementSet;
185 nextR = r_m / (Physics::c * dt_m);
186 integrator_m.push(nextR, p_m, dt_m);
187 nextR *= Physics::c * dt_m;
188
189 elementSet = itsOpalBeamline_m.getElements(nextR);
190 }
191 intersection.clear();
192 std::set_intersection(currentSet.begin(), currentSet.end(),
193 elementSet.begin(), elementSet.end(),
194 std::inserter(intersection, intersection.begin()));
195 } while (errorFlag_m != HITMATERIAL &&
196 errorFlag_m != EOL &&
197 stepRange_m.isInside(currentStep_m) &&
198 !(pathLengthRange_m.isOutside(pathLength_m) &&
199 intersection.empty() && !(elementSet.empty() || currentSet.empty())));
200
201 imap_m.tidyUp(zstop_m);
202 *gmsg << level1 << "\n" << imap_m << endl;
203 imap_m.saveSDDS(initialPathLength);
204
206}
207
208void OrbitThreader::integrate(const IndexMap::value_t &activeSet, double /*maxDrift*/) {
209 CoordinateSystemTrafo labToBeamline = itsOpalBeamline_m.getCSTrafoLab2Local();
210 Vector_t nextR;
211 do {
213
214 IndexMap::value_t::const_iterator it = activeSet.begin();
215 const IndexMap::value_t::const_iterator end = activeSet.end();
216 Vector_t oldR = r_m;
217
218 r_m /= Physics::c * dt_m;
219 integrator_m.push(r_m, p_m, dt_m);
220 r_m *= Physics::c * dt_m;
221
222 Vector_t Ef(0.0), Bf(0.0);
223 std::string names("\t");
224 for (; it != end; ++ it) {
225 Vector_t localR = itsOpalBeamline_m.transformToLocalCS(*it, r_m);
226 Vector_t localP = itsOpalBeamline_m.rotateToLocalCS(*it, p_m);
227 Vector_t localE(0.0), localB(0.0);
228
229 if ((*it)->applyToReferenceParticle(localR, localP, time_m + 0.5 * dt_m, localE, localB)) {
231 return;
232 }
233 names += (*it)->getName() + ", ";
234
235 Ef += itsOpalBeamline_m.rotateFromLocalCS(*it, localE);
236 Bf += itsOpalBeamline_m.rotateFromLocalCS(*it, localB);
237 }
238
239 if (((pathLength_m > 0.0 &&
240 pathLength_m < zstop_m) || dt_m < 0.0) &&
242 !OpalData::getInstance()->isOptimizerRun()) {
243 logger_m << std::setw(18) << std::setprecision(8) << pathLength_m + std::copysign(euclidean_norm(r_m - oldR), dt_m)
244 << std::setw(18) << std::setprecision(8) << r_m(0)
245 << std::setw(18) << std::setprecision(8) << r_m(1)
246 << std::setw(18) << std::setprecision(8) << r_m(2)
247 << std::setw(18) << std::setprecision(8) << p_m(0)
248 << std::setw(18) << std::setprecision(8) << p_m(1)
249 << std::setw(18) << std::setprecision(8) << p_m(2)
250 << std::setw(18) << std::setprecision(8) << Ef(0)
251 << std::setw(18) << std::setprecision(8) << Ef(1)
252 << std::setw(18) << std::setprecision(8) << Ef(2)
253 << std::setw(18) << std::setprecision(8) << Bf(0)
254 << std::setw(18) << std::setprecision(8) << Bf(1)
255 << std::setw(18) << std::setprecision(8) << Bf(2)
256 << std::setw(18) << std::setprecision(8) << reference_m.getM() * (sqrt(dot(p_m, p_m) + 1) - 1) * Units::eV2MeV
257 << std::setw(18) << std::setprecision(8) << (time_m + 0.5 * dt_m) * Units::s2ns
258 << names
259 << std::endl;
260 }
261
262 r_m /= Physics::c * dt_m;
263 integrator_m.kick(r_m, p_m, Ef, Bf, dt_m);
264 integrator_m.push(r_m, p_m, dt_m);
265 r_m *= Physics::c * dt_m;
266
267 pathLength_m += std::copysign(euclidean_norm(r_m - oldR), dt_m);
268 ++ currentStep_m;
269 time_m += dt_m;
270
271 nextR = r_m / (Physics::c * dt_m);
272 integrator_m.push(nextR, p_m, dt_m);
273 nextR *= Physics::c * dt_m;
274
275 if (activeSet.empty() &&
276 (pathLengthRange_m.isOutside(pathLength_m) ||
277 stepRange_m.isOutside(currentStep_m))) {
279 globalBoundingBox_m.enlargeToContainPosition(r_m);
280 return;
281 }
282
283 } while (activeSet == itsOpalBeamline_m.getElements(nextR));
284}
285
287 IndexMap::value_t::const_iterator it = activeSet.begin();
288 const IndexMap::value_t::const_iterator end = activeSet.end();
289
290 for (; it != end; ++ it) {
291 if ((*it)->getType() == ElementType::TRAVELINGWAVE ||
292 (*it)->getType() == ElementType::RFCAVITY) {
293 return true;
294 }
295 }
296
297 return false;
298}
299
301 const std::set<std::string> &visitedElements) {
302 if (Options::autoPhase == 0) return;
303
304 IndexMap::value_t::const_iterator it = activeSet.begin();
305 const IndexMap::value_t::const_iterator end = activeSet.end();
306
307 for (; it != end; ++ it) {
308 if (((*it)->getType() == ElementType::TRAVELINGWAVE ||
309 (*it)->getType() == ElementType::RFCAVITY) &&
310 visitedElements.find((*it)->getName()) == visitedElements.end()) {
311
312 Vector_t initialR = itsOpalBeamline_m.transformToLocalCS(*it, r_m);
313 Vector_t initialP = itsOpalBeamline_m.rotateToLocalCS(*it, p_m);
314
316 ap.getPhaseAtMaxEnergy(initialR,
317 initialP,
318 time_m,
319 dt_m);
320 }
321 }
322}
323
324
326{
327 IndexMap::value_t::const_iterator it = elementSet.begin();
328 const IndexMap::value_t::const_iterator end = elementSet.end();
329
330 double designEnergy = 0.0;
331 for (; it != end; ++ it) {
332 if ((*it)->getType() == ElementType::TRAVELINGWAVE ||
333 (*it)->getType() == ElementType::RFCAVITY) {
334 const RFCavity *element = static_cast<const RFCavity *>((*it).get());
335 designEnergy = std::max(designEnergy, element->getDesignEnergy());
336 }
337 }
338
339 return designEnergy;
340}
341
343 dt_m *= -1;
344 ValueRange<double> tmpRange;
345 std::swap(tmpRange, pathLengthRange_m);
346 double initialPathLength = pathLength_m;
347
348 Vector_t nextR = r_m / (Physics::c * dt_m);
349 integrator_m.push(nextR, p_m, dt_m);
350 nextR *= Physics::c * dt_m;
351
352 while (std::abs(initialPathLength - pathLength_m) < distTrackBack_m) {
353 auto elementSet = itsOpalBeamline_m.getElements(nextR);
354
355 double maxDrift = computeDriftLengthToBoundingBox(elementSet, r_m, -p_m);
356 maxDrift = std::min(maxDrift, distTrackBack_m);
357 integrate(elementSet, maxDrift);
358
359 nextR = r_m / (Physics::c * dt_m);
360 integrator_m.push(nextR, p_m, dt_m);
361 nextR *= Physics::c * dt_m;
362 }
363 std::swap(tmpRange, pathLengthRange_m);
364 currentStep_m *= -1;
365 dt_m *= -1;
366
367 stepRange_m.enlargeIfOutside(currentStep_m);
368}
369
371 double start,
372 const Vector_t &R,
373 const Vector_t &P) {
374
375 IndexMap::value_t::const_iterator it = elementSet.begin();
376 const IndexMap::value_t::const_iterator end = elementSet.end();
377
378 for (; it != end; ++ it) {
379 bool found = false;
380 std::string name = (*it)->getName();
381 auto prior = elementRegistry_m.equal_range(name);
382
383 for (auto pit = prior.first; pit != prior.second; ++ pit) {
384 if (std::abs((*pit).second.endField_m - start) < 1e-10) {
385 found = true;
386 (*pit).second.endField_m = pathLength_m;
387 break;
388 }
389 }
390
391 if (found) continue;
392
393 Vector_t initialR = itsOpalBeamline_m.transformToLocalCS(*it, R);
394 Vector_t initialP = itsOpalBeamline_m.rotateToLocalCS(*it, P);
395 double elementEdge = start - initialR(2) * euclidean_norm(initialP) / initialP(2);
396
397 elementPosition ep = {start, pathLength_m, elementEdge};
398 elementRegistry_m.insert(std::make_pair(name, ep));
399 }
400}
401
403 std::map<std::string, std::set<elementPosition, elementPositionComp> > tmpRegistry;
404
405 for (auto it = elementRegistry_m.begin(); it != elementRegistry_m.end(); ++ it) {
406 const std::string &name = (*it).first;
407 elementPosition &ep = (*it).second;
408
409 auto prior = tmpRegistry.find(name);
410 if (prior == tmpRegistry.end()) {
411 std::set<elementPosition, elementPositionComp> tmpSet;
412 tmpSet.insert(ep);
413 tmpRegistry.insert(std::make_pair(name, tmpSet));
414 continue;
415 }
416
417 std::set<elementPosition, elementPositionComp> &set = (*prior).second;
418 set.insert(ep);
419 }
420
421 auto allElements = itsOpalBeamline_m.getElementByType(ElementType::ANY);
422 FieldList::iterator it = allElements.begin();
423 const FieldList::iterator end = allElements.end();
424 for (; it != end; ++ it) {
425 std::string name = (*it).getElement()->getName();
426 auto trit = tmpRegistry.find(name);
427 if (trit == tmpRegistry.end()) continue;
428
429 std::queue<std::pair<double, double> > range;
430 std::set<elementPosition, elementPositionComp> &set = (*trit).second;
431
432 for (auto sit = set.begin(); sit != set.end(); ++ sit) {
433 range.push(std::make_pair((*sit).elementEdge_m, (*sit).endField_m));
434 }
435 (*it).getElement()->setActionRange(range);
436 }
437}
438
439void OrbitThreader::setDesignEnergy(FieldList &allElements, const std::set<std::string> &visitedElements) {
440 double kineticEnergyeV = reference_m.getM() * (sqrt(dot(p_m, p_m) + 1.0) - 1.0);
441
442 FieldList::iterator it = allElements.begin();
443 const FieldList::iterator end = allElements.end();
444 for (; it != end; ++ it) {
445 std::shared_ptr<Component> element = (*it).getElement();
446 if (visitedElements.find(element->getName()) == visitedElements.end() &&
447 !(element->getType() == ElementType::RFCAVITY ||
448 element->getType() == ElementType::TRAVELINGWAVE)) {
449
450 element->setDesignEnergy(kineticEnergyeV);
451 }
452 }
453}
454
456 FieldList allElements = itsOpalBeamline_m.getElementByType(ElementType::ANY);
457 FieldList::iterator it = allElements.begin();
458 const FieldList::iterator end = allElements.end();
459
460 for (; it != end; ++ it) {
461 if (it->getElement()->getType() == ElementType::MARKER) {
462 continue;
463 }
464 BoundingBox other = it->getBoundingBoxInLabCoords();
465 globalBoundingBox_m.enlargeToContainBoundingBox(other);
466 }
467
469}
470
471
474 for (const Vector_t& pos : {r_m - 10 * dR, r_m + 10 * dR}) {
475 globalBoundingBox_m.enlargeToContainPosition(pos);
476 }
477}
478
479double OrbitThreader::computeDriftLengthToBoundingBox(const std::set<std::shared_ptr<Component>> & elements,
480 const Vector_t & position,
481 const Vector_t & direction) const {
482 if (elements.empty() ||
483 (elements.size() == 1 && (*elements.begin())->getType() == ElementType::DRIFT)) {
484 boost::optional<Vector_t> intersectionPoint = globalBoundingBox_m.getIntersectionPoint(position, direction);
485
486 return intersectionPoint ? euclidean_norm(intersectionPoint.get() - position): 10.0;
487 }
488
489 return std::numeric_limits<double>::max();
490}
double dot(const Vector3D &lhs, const Vector3D &rhs)
Vector dot product.
Definition Vector3D.cpp:118
PartBunchBase< T, Dim >::ConstIterator end(PartBunchBase< T, Dim > const &bunch)
std::list< ClassicField > FieldList
Tps< T > sqrt(const Tps< T > &x)
Square root.
Definition TpsMath.h:91
T euclidean_norm(const Vector< T > &)
Euclidean norm.
Definition Vector.h:243
elements
Definition IndexMap.cpp:163
#define EOL
#define HITMATERIAL
#define EVERYTHINGFINE
Inform * gmsg
Definition Main.cpp:70
PETE_TBTree< FnCopysign, PETE_Scalar< Vektor< T1, Dim > >, typename T2::PETE_Expr_t > copysign(const Vektor< T1, Dim > &l, const PETE_Expr< T2 > &r)
Inform & endl(Inform &inf)
Definition Inform.cpp:42
Inform & level1(Inform &inf)
Definition Inform.cpp:45
const std::string name
STL namespace.
constexpr double c
The velocity of light in m/s.
Definition Physics.h:45
constexpr double eV2MeV
Definition Units.h:77
constexpr double s2ns
Definition Units.h:44
int autoPhase
Definition Options.cpp:69
std::string combineFilePath(std::initializer_list< std::string > ilist)
Definition Util.cpp:197
double getGamma(Vector_t p)
Definition Util.h:46
static OpalData * getInstance()
Definition OpalData.cpp:196
double getPhaseAtMaxEnergy(const Vector_t &R, const Vector_t &P, double t, double dt)
std::set< std::shared_ptr< Component > > value_t
Definition IndexMap.h:47
void processElementRegister()
void updateBoundingBoxWithCurrentPosition()
void checkElementLengths(const std::set< std::shared_ptr< Component > > &elements)
double distTrackBack_m
void setDesignEnergy(FieldList &allElements, const std::set< std::string > &visitedElements)
BorisPusher integrator_m
ValueRange< double > pathLengthRange_m
IndexMap imap_m
ValueRange< long > stepRange_m
unsigned int errorFlag_m
double dt_m
the time step
double time_m
the simulated time
OpalBeamline & itsOpalBeamline_m
double computeDriftLengthToBoundingBox(const std::set< std::shared_ptr< Component > > &elements, const Vector_t &position, const Vector_t &direction) const
std::multimap< std::string, elementPosition > elementRegistry_m
Vector_t r_m
position of reference particle in lab coordinates
const PartData & reference_m
std::ofstream logger_m
BoundingBox globalBoundingBox_m
Vector_t p_m
momentum of reference particle
bool containsCavity(const IndexMap::value_t &activeSet)
void integrate(const IndexMap::value_t &activeSet, double maxDrift=10.0)
OrbitThreader(const PartData &ref, const Vector_t &r, const Vector_t &p, double s, double maxDiffZBunch, double t, double dT, StepSizeConfig stepSizes, OpalBeamline &bl)
StepSizeConfig stepSizes_m
final position in path length
double getMaxDesignEnergy(const IndexMap::value_t &elementSet) const
void registerElement(const IndexMap::value_t &elementSet, double, const Vector_t &r, const Vector_t &p)
double pathLength_m
position of reference particle in path length
void autophaseCavities(const IndexMap::value_t &activeSet, const std::set< std::string > &visitedElements)
void computeBoundingBox()
size_t loggingFrequency_m
const double zstop_m
virtual double getDesignEnergy() const override
Definition RFCavity.h:339
The base class for all OPAL exceptions.
static int myNode()
Definition IpplInfo.cpp:691
Vektor< double, 3 > Vector_t