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