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//
23
29#include "BasicActions/Option.h"
31#include "Physics/Physics.h"
32#include "Physics/Units.h"
34#include "Utilities/Options.h"
35#include "Utilities/Util.h"
36
37#include <algorithm>
38#include <cmath>
39#include <filesystem>
40#include <fstream>
41#include <iomanip>
42#include <iostream>
43#include <limits>
44#include <map>
45#include <memory>
46#include <queue>
47#include <set>
48#include <string>
49#include <utility>
50
51#define HITMATERIAL 0x80000000
52#define EOL 0x40000000
53#define EVERYTHINGFINE 0x00000000
54extern Inform *gmsg;
55
57 const Vector_t& r,
58 const Vector_t& p,
59 double s,
60 double maxDiffZBunch,
61 double t,
62 double dt,
63 StepSizeConfig stepSizes,
64 OpalBeamline& bl) :
65 r_m(r),
66 p_m(p),
67 pathLength_m(s),
68 time_m(t),
69 dt_m(dt),
70 stepSizes_m(stepSizes),
71 zstop_m(stepSizes.getFinalZStop() + std::copysign(1.0, dt) * 2 * maxDiffZBunch),
72 itsOpalBeamline_m(bl),
73 errorFlag_m(0),
74 integrator_m(ref),
75 reference_m(ref)
76{
77 auto opal = OpalData::getInstance();
78 if (Ippl::myNode() == 0 && !opal->isOptimizerRun()) {
79 std::string fileName = Util::combineFilePath({
80 opal->getAuxiliaryOutputDirectory(),
81 opal->getInputBasename() + "_DesignPath.dat"
82 });
83 if (opal->getOpenMode() == OpalData::OpenMode::WRITE ||
84 !std::filesystem::exists(fileName)) {
85 logger_m.open(fileName);
86 logger_m << "#"
87 << std::setw(17) << "1 - s"
88 << std::setw(18) << "2 - Rx"
89 << std::setw(18) << "3 - Ry"
90 << std::setw(18) << "4 - Rz"
91 << std::setw(18) << "5 - Px"
92 << std::setw(18) << "6 - Py"
93 << std::setw(18) << "7 - Pz"
94 << std::setw(18) << "8 - Efx"
95 << std::setw(18) << "9 - Efy"
96 << std::setw(18) << "10 - Efz"
97 << std::setw(18) << "11 - Bfx"
98 << std::setw(18) << "12 - Bfy"
99 << std::setw(18) << "13 - Bfz"
100 << std::setw(18) << "14 - Ekin"
101 << std::setw(18) << "15 - t"
102 << std::endl;
103 } else {
104 logger_m.open(fileName, std::ios_base::app);
105 }
106
107 loggingFrequency_m = std::max(1.0, std::round(1e-11 / std::abs(dt_m)));
108 } else {
109 loggingFrequency_m = std::numeric_limits<size_t>::max();
110 }
116 distTrackBack_m = std::min(pathLength_m, std::max(0.0, maxDiffZBunch));
118}
119
120void OrbitThreader::checkElementLengths(const std::set<std::shared_ptr<Component>>& fields) {
122 ++ stepSizes_m;
123 }
124 if (stepSizes_m.reachedEnd()) {
125 return;
126 }
127 double driftLength = Physics::c * std::abs(stepSizes_m.getdT()) * euclidean_norm(p_m) / Util::getGamma(p_m);
128 for (const std::shared_ptr<Component>& field : fields) {
129 double length = field->getElementLength();
130 int numSteps = field->getRequiredNumberOfTimeSteps();
131
132 if (length < numSteps * driftLength) {
133 throw OpalException("OrbitThreader::checkElementLengths",
134 "The time step is too long compared to the length of the\n"
135 "element '" + field->getName() + "'\n" +
136 "The length of the element is: " + std::to_string(length) + "\n"
137 "The distance the particles drift in " + std::to_string(numSteps) +
138 " time step(s) is: " + std::to_string(numSteps * driftLength));
139 }
140 }
141}
142
144 double initialPathLength = pathLength_m;
145
147 std::set<std::string> visitedElements;
148
149 trackBack();
152
153 Vector_t nextR = r_m / (Physics::c * dt_m);
154 integrator_m.push(nextR, p_m, dt_m);
155 nextR *= Physics::c * dt_m;
156
157 setDesignEnergy(allElements, visitedElements);
158
159 auto elementSet = itsOpalBeamline_m.getElements(nextR);
160 std::set<std::shared_ptr<Component>> intersection, currentSet;
162 do {
163 checkElementLengths(elementSet);
164 if (containsCavity(elementSet)) {
165 autophaseCavities(elementSet, visitedElements);
166 }
167
168 double initialS = pathLength_m;
169 Vector_t initialR = r_m;
170 Vector_t initialP = p_m;
171 double maxDistance = computeDriftLengthToBoundingBox(elementSet, r_m, p_m);
172 integrate(elementSet, maxDistance);
173
174 registerElement(elementSet, initialS, initialR, initialP);
175
176 if (errorFlag_m == HITMATERIAL) {
177 // Shouldn't be reached because reference particle
178 // isn't stopped by collimators
179 pathLength_m += std::copysign(1.0, dt_m);
180 }
181
182 imap_m.add(initialS, pathLength_m, elementSet);
183
184 IndexMap::value_t::const_iterator it = elementSet.begin();
185 const IndexMap::value_t::const_iterator end = elementSet.end();
186 for (; it != end; ++ it) {
187 visitedElements.insert((*it)->getName());
188 }
189
190 setDesignEnergy(allElements, visitedElements);
191
192 currentSet = elementSet;
194 nextR = r_m / (Physics::c * dt_m);
195 integrator_m.push(nextR, p_m, dt_m);
196 nextR *= Physics::c * dt_m;
197
198 elementSet = itsOpalBeamline_m.getElements(nextR);
199 }
200 intersection.clear();
201 std::set_intersection(currentSet.begin(), currentSet.end(),
202 elementSet.begin(), elementSet.end(),
203 std::inserter(intersection, intersection.begin()));
204 } while (errorFlag_m != HITMATERIAL &&
205 errorFlag_m != EOL &&
208 intersection.empty() && !(elementSet.empty() || currentSet.empty())));
209
211 *gmsg << level1 << "\n" << imap_m << endl;
212 imap_m.saveSDDS(initialPathLength);
213
215}
216
217void OrbitThreader::integrate(const IndexMap::value_t& activeSet, double /*maxDrift*/) {
219 Vector_t nextR;
220 do {
222
223 IndexMap::value_t::const_iterator it = activeSet.begin();
224 const IndexMap::value_t::const_iterator end = activeSet.end();
225 Vector_t oldR = r_m;
226
227 r_m /= Physics::c * dt_m;
229 r_m *= Physics::c * dt_m;
230
231 Vector_t Ef(0.0), Bf(0.0);
232 std::string names("\t");
233 for (; it != end; ++ it) {
236 Vector_t localE(0.0), localB(0.0);
237
238 if ((*it)->applyToReferenceParticle(localR, localP, time_m + 0.5 * dt_m, localE, localB)) {
240 return;
241 }
242 names += (*it)->getName() + ", ";
243
244 Ef += itsOpalBeamline_m.rotateFromLocalCS(*it, localE);
245 Bf += itsOpalBeamline_m.rotateFromLocalCS(*it, localB);
246 }
247
248 if (((pathLength_m > 0.0 &&
249 pathLength_m < zstop_m) || dt_m < 0.0) &&
251 !OpalData::getInstance()->isOptimizerRun()) {
252 logger_m << std::setw(18) << std::setprecision(8) << pathLength_m + std::copysign(euclidean_norm(r_m - oldR), dt_m)
253 << std::setw(18) << std::setprecision(8) << r_m(0)
254 << std::setw(18) << std::setprecision(8) << r_m(1)
255 << std::setw(18) << std::setprecision(8) << r_m(2)
256 << std::setw(18) << std::setprecision(8) << p_m(0)
257 << std::setw(18) << std::setprecision(8) << p_m(1)
258 << std::setw(18) << std::setprecision(8) << p_m(2)
259 << std::setw(18) << std::setprecision(8) << Ef(0)
260 << std::setw(18) << std::setprecision(8) << Ef(1)
261 << std::setw(18) << std::setprecision(8) << Ef(2)
262 << std::setw(18) << std::setprecision(8) << Bf(0)
263 << std::setw(18) << std::setprecision(8) << Bf(1)
264 << std::setw(18) << std::setprecision(8) << Bf(2)
265 << std::setw(18) << std::setprecision(8) << reference_m.getM() * (sqrt(dot(p_m, p_m) + 1) - 1) * Units::eV2MeV
266 << std::setw(18) << std::setprecision(8) << (time_m + 0.5 * dt_m) * Units::s2ns
267 << names
268 << std::endl;
269 }
270
271 r_m /= Physics::c * dt_m;
272 integrator_m.kick(r_m, p_m, Ef, Bf, dt_m);
274 r_m *= Physics::c * dt_m;
275
276 pathLength_m += std::copysign(euclidean_norm(r_m - oldR), dt_m);
277 ++ currentStep_m;
278 time_m += dt_m;
279
280 nextR = r_m / (Physics::c * dt_m);
281 integrator_m.push(nextR, p_m, dt_m);
282 nextR *= Physics::c * dt_m;
283
284 if (activeSet.empty() &&
289 return;
290 }
291
292 } while (activeSet == itsOpalBeamline_m.getElements(nextR));
293}
294
296 IndexMap::value_t::const_iterator it = activeSet.begin();
297 const IndexMap::value_t::const_iterator end = activeSet.end();
298
299 for (; it != end; ++ it) {
300 if ((*it)->getType() == ElementType::TRAVELINGWAVE ||
301 (*it)->getType() == ElementType::RFCAVITY) {
302 return true;
303 }
304 }
305
306 return false;
307}
308
310 const std::set<std::string>& visitedElements) {
311 if (Options::autoPhase == 0) return;
312
313 IndexMap::value_t::const_iterator it = activeSet.begin();
314 const IndexMap::value_t::const_iterator end = activeSet.end();
315
316 for (; it != end; ++ it) {
317 if (((*it)->getType() == ElementType::TRAVELINGWAVE ||
318 (*it)->getType() == ElementType::RFCAVITY) &&
319 visitedElements.find((*it)->getName()) == visitedElements.end()) {
320
323
325 ap.getPhaseAtMaxEnergy(initialR,
326 initialP,
327 time_m,
328 dt_m);
329 }
330 }
331}
332
333
335 IndexMap::value_t::const_iterator it = elementSet.begin();
336 const IndexMap::value_t::const_iterator end = elementSet.end();
337
338 double designEnergy = 0.0;
339 for (; it != end; ++ it) {
340 if ((*it)->getType() == ElementType::TRAVELINGWAVE ||
341 (*it)->getType() == ElementType::RFCAVITY) {
342 const RFCavity *element = static_cast<const RFCavity *>((*it).get());
343 designEnergy = std::max(designEnergy, element->getDesignEnergy());
344 }
345 }
346
347 return designEnergy;
348}
349
351 dt_m *= -1;
352 ValueRange<double> tmpRange;
353 std::swap(tmpRange, pathLengthRange_m);
354 double initialPathLength = pathLength_m;
355
356 Vector_t nextR = r_m / (Physics::c * dt_m);
357 integrator_m.push(nextR, p_m, dt_m);
358 nextR *= Physics::c * dt_m;
359
360 while (std::abs(initialPathLength - pathLength_m) < distTrackBack_m) {
361 auto elementSet = itsOpalBeamline_m.getElements(nextR);
362
363 double maxDrift = computeDriftLengthToBoundingBox(elementSet, r_m, -p_m);
364 maxDrift = std::min(maxDrift, distTrackBack_m);
365 integrate(elementSet, maxDrift);
366
367 nextR = r_m / (Physics::c * dt_m);
368 integrator_m.push(nextR, p_m, dt_m);
369 nextR *= Physics::c * dt_m;
370 }
371 std::swap(tmpRange, pathLengthRange_m);
372 currentStep_m *= -1;
373 dt_m *= -1;
374
376}
377
379 double start,
380 const Vector_t& R,
381 const Vector_t& P) {
382
383 IndexMap::value_t::const_iterator it = elementSet.begin();
384 const IndexMap::value_t::const_iterator end = elementSet.end();
385
386 for (; it != end; ++ it) {
387 bool found = false;
388 std::string name = (*it)->getName();
389 auto prior = elementRegistry_m.equal_range(name);
390
391 for (auto pit = prior.first; pit != prior.second; ++ pit) {
392 if (std::abs((*pit).second.endField_m - start) < 1e-10) {
393 found = true;
394 (*pit).second.endField_m = pathLength_m;
395 break;
396 }
397 }
398
399 if (found) continue;
400
402 Vector_t initialP = itsOpalBeamline_m.rotateToLocalCS(*it, P);
403 double elementEdge = start - initialR(2) * euclidean_norm(initialP) / initialP(2);
404
405 elementPosition ep = {start, pathLength_m, elementEdge};
406 elementRegistry_m.insert(std::make_pair(name, ep));
407 }
408}
409
411 std::map<std::string, std::set<elementPosition, elementPositionComp> > tmpRegistry;
412
413 for (auto it = elementRegistry_m.begin(); it != elementRegistry_m.end(); ++ it) {
414 const std::string &name = (*it).first;
415 elementPosition &ep = (*it).second;
416
417 auto prior = tmpRegistry.find(name);
418 if (prior == tmpRegistry.end()) {
419 std::set<elementPosition, elementPositionComp> tmpSet;
420 tmpSet.insert(ep);
421 tmpRegistry.insert(std::make_pair(name, tmpSet));
422 continue;
423 }
424
425 std::set<elementPosition, elementPositionComp> &set = (*prior).second;
426 set.insert(ep);
427 }
428
430 FieldList::iterator it = allElements.begin();
431 const FieldList::iterator end = allElements.end();
432 for (; it != end; ++ it) {
433 std::string name = (*it).getElement()->getName();
434 auto trit = tmpRegistry.find(name);
435 if (trit == tmpRegistry.end()) continue;
436
437 std::queue<std::pair<double, double> > range;
438 std::set<elementPosition, elementPositionComp> &set = (*trit).second;
439
440 for (auto sit = set.begin(); sit != set.end(); ++ sit) {
441 range.push(std::make_pair((*sit).elementEdge_m, (*sit).endField_m));
442 }
443 (*it).getElement()->setActionRange(range);
444 }
445}
446
447void OrbitThreader::setDesignEnergy(FieldList& allElements, const std::set<std::string>& visitedElements) {
448 double kineticEnergyeV = reference_m.getM() * (sqrt(dot(p_m, p_m) + 1.0) - 1.0);
449
450 FieldList::iterator it = allElements.begin();
451 const FieldList::iterator end = allElements.end();
452 for (; it != end; ++ it) {
453 std::shared_ptr<Component> element = (*it).getElement();
454 if (visitedElements.find(element->getName()) == visitedElements.end() &&
455 !(element->getType() == ElementType::RFCAVITY ||
456 element->getType() == ElementType::TRAVELINGWAVE)) {
457
458 element->setDesignEnergy(kineticEnergyeV);
459 }
460 }
461}
462
465 FieldList::iterator it = allElements.begin();
466 const FieldList::iterator end = allElements.end();
467
468 for (; it != end; ++ it) {
469 if (it->getElement()->getType() == ElementType::MARKER) {
470 continue;
471 }
472 BoundingBox other = it->getBoundingBoxInLabCoords();
474 }
475
477}
478
481 for (const Vector_t& pos : {r_m - 10 * dR, r_m + 10 * dR}) {
483 }
484}
485
486double OrbitThreader::computeDriftLengthToBoundingBox(const std::set<std::shared_ptr<Component>>& elements,
487 const Vector_t& position,
488 const Vector_t& direction) const {
489 if (elements.empty() ||
490 (elements.size() == 1 && (*elements.begin())->getType() == ElementType::DRIFT)) {
491 std::optional<Vector_t> intersectionPoint = globalBoundingBox_m.getIntersectionPoint(position, direction);
492
493 return intersectionPoint ? euclidean_norm(intersectionPoint.value() - position) : 10.0;
494 }
495
496 return std::numeric_limits<double>::max();
497}
PartBunchBase< T, Dim >::ConstIterator end(PartBunchBase< T, Dim > const &bunch)
std::list< ClassicField > FieldList
double dot(const Vector3D &lhs, const Vector3D &rhs)
Vector dot product.
Definition Vector3D.cpp:118
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
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
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:67
std::string combineFilePath(std::initializer_list< std::string > ilist)
Definition Util.cpp:200
double getGamma(Vector_t p)
Definition Util.h:49
static OpalData * getInstance()
Definition OpalData.cpp:196
double getPhaseAtMaxEnergy(const Vector_t &R, const Vector_t &P, double t, double dt)
void add(key_t::first_type initialStep, key_t::second_type finalStep, const value_t &val)
Definition IndexMap.cpp:113
void tidyUp(double zstop)
Definition IndexMap.cpp:148
std::set< std::shared_ptr< Component > > value_t
Definition IndexMap.h:47
void saveSDDS(double startS) const
Definition IndexMap.cpp:177
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
double getdT() const
unsigned long long getNumStepsFinestResolution() const
double getZStop() const
bool reachedEnd() const
ValueRange< double > getPathLengthRange() const
virtual double getDesignEnergy() const override
Definition RFCavity.h:336
double getM() const
The constant mass per particle.
Definition PartData.h:122
std::optional< Vector_t > getIntersectionPoint(const Vector_t &position, const Vector_t &direction) const
void enlargeToContainBoundingBox(const BoundingBox &boundingBox)
void enlargeToContainPosition(const Vector_t &position)
bool isOutside(T value) const
Definition ValueRange.h:46
void enlargeIfOutside(T value)
Definition ValueRange.h:36
bool isInside(T value) const
Definition ValueRange.h:41
FieldList getElementByType(ElementType)
Vector_t rotateToLocalCS(const std::shared_ptr< Component > &comp, const Vector_t &r) const
Vector_t transformToLocalCS(const std::shared_ptr< Component > &comp, const Vector_t &r) const
Vector_t rotateFromLocalCS(const std::shared_ptr< Component > &comp, const Vector_t &r) const
CoordinateSystemTrafo getCSTrafoLab2Local(const std::shared_ptr< Component > &comp) const
std::set< std::shared_ptr< Component > > getElements(const Vector_t &x)
void kick(const Vector_t &R, Vector_t &P, const Vector_t &Ef, const Vector_t &Bf, const double &dt) const
Definition BorisPusher.h:65
void push(Vector_t &R, const Vector_t &P, const double &dt) const
The base class for all OPAL exceptions.
static int myNode()
Definition IpplInfo.cpp:691