OPALX (Object Oriented Parallel Accelerator Library for Exascal) master (dc2a29eed580)
OPALX
Loading...
Searching...
No Matches
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// All rights reserved
8//
9// This file is part of OPALX.
10//
11// OPAL is free software: you can redistribute it and/or modify
12// it under the terms of the GNU General Public License as published by
13// the Free Software Foundation, either version 3 of the License, or
14// (at your option) any later version.
15//
16// You should have received a copy of the GNU General Public License
17// along with OPAL. If not, see <https://www.gnu.org/licenses/>.
18//
19
21
26#include "BasicActions/Option.h"
28#include "Physics/Units.h"
30#include "Utilities/Options.h"
31#include "Utilities/Util.h"
32
33#include <filesystem>
34
35#include <algorithm>
36#include <cmath>
37#include <iostream>
38#include <limits>
39#include <vector>
40
41#define HITMATERIAL 0x80000000
42#define EOL 0x40000000
43#define EVERYTHINGFINE 0x00000000
44extern Inform* gmsg;
45
47 const PartData& ref, const Vector_t<double, 3>& r, const Vector_t<double, 3>& p, double s,
48 double maxDiffZBunch, double t, double dt, StepSizeConfig stepSizes, OpalBeamline& bl)
49 : r_m(r),
50 p_m(p),
51 pathLength_m(s),
52 time_m(t),
53 dt_m(dt),
54 stepSizes_m(stepSizes),
55 zstop_m(stepSizes.getFinalZStop() + std::copysign(1.0, dt) * 2 * maxDiffZBunch),
56 itsOpalBeamline_m(bl),
57 errorFlag_m(0),
58 integrator_m{},
59 reference_m(ref) {
60 auto opal = OpalData::getInstance();
61 if (ippl::Comm->rank() == 0 && !opal->isOptimizerRun()) {
62 std::string fileName = Util::combineFilePath(
63 {opal->getAuxiliaryOutputDirectory(),
64 opal->getInputBasename() + "_DesignPath.dat"});
65 if (opal->getOpenMode() == OpalData::OpenMode::WRITE
66 || !std::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 }
86
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) {
96 }
97 if (stepSizes_m.reachedEnd()) {
98 return;
99 }
100 double driftLength =
102 for (const std::shared_ptr<Component>& field : fields) {
103 double fieldBegin = 0.0;
104 double fieldEnd = 0.0;
105 field->getFieldExtend(fieldBegin, fieldEnd);
106
107 const double length = std::abs(fieldEnd - fieldBegin);
108 const int numSteps = field->getRequiredNumberOfTimeSteps();
109
110 if (length < numSteps * driftLength) {
111 throw OpalException("OrbitThreader::checkElementLengths",
112 "The time step is too long compared to the length of the\n"
113 "element '" + field->getName() + "'\n" +
114 "The field-support extent of the element is: "
115 + std::to_string(length) + "\n"
116 "The distance the particles drift in " + std::to_string(numSteps) +
117 " time step(s) is: " + std::to_string(numSteps * driftLength));
118 }
119 }
120}
121
123 double initialPathLength = pathLength_m;
124
126 std::set<std::string> visitedElements;
127
128 trackBack();
131
133 integrator_m.push(nextR, p_m, dt_m);
134 nextR = nextR * Physics::c * dt_m;
135 setDesignEnergy(allElements, visitedElements);
136
137 auto elementSet = itsOpalBeamline_m.getElements(nextR);
138 std::set<std::shared_ptr<Component>> intersection, currentSet;
140
141 *gmsg << "* OrbitThreader dt_m= " << dt_m << endl;
142
143 do {
144 checkElementLengths(elementSet);
145 if (containsCavity(elementSet)) {
146 autophaseCavities(elementSet, visitedElements);
147 }
148
149 double initialS = pathLength_m;
150 Vector_t<double, 3> initialR = r_m;
151 Vector_t<double, 3> initialP = p_m;
152 double maxDistance = computeDriftLengthToBoundingBox(elementSet, r_m, p_m);
153
154 integrate(elementSet, maxDistance);
155
156 *gmsg << "* OrbitThreader maxDistance= " << maxDistance << endl;
157 *gmsg << "* OrbitThreader #elements = " << elementSet.size() << endl;
158
159 registerElement(elementSet, initialS, initialR, initialP);
160
161 if (errorFlag_m == HITMATERIAL) {
162 // Shouldn't be reached because reference particle
163 // isn't stopped by collimators
164 pathLength_m += std::copysign(1.0, dt_m);
165 }
166
167 imap_m.add(initialS, pathLength_m, elementSet);
168
169 IndexMap::value_t::const_iterator it = elementSet.begin();
170 const IndexMap::value_t::const_iterator end = elementSet.end();
171 for (; it != end; ++it) {
172 visitedElements.insert((*it)->getName());
173 }
174
175 setDesignEnergy(allElements, visitedElements);
176
177 currentSet = elementSet;
179 nextR = r_m / (Physics::c * dt_m);
180 integrator_m.push(nextR, p_m, dt_m);
181 nextR = nextR * Physics::c * dt_m;
182
183 elementSet = itsOpalBeamline_m.getElements(nextR);
184 }
185 intersection.clear();
186 std::set_intersection(
187 currentSet.begin(), currentSet.end(), elementSet.begin(), elementSet.end(),
188 std::inserter(intersection, intersection.begin()));
190 && !(pathLengthRange_m.isOutside(pathLength_m) && intersection.empty()
191 && !(elementSet.empty() || currentSet.empty())));
192
194 *gmsg << level1 << "\n" << imap_m << endl;
195 imap_m.saveSDDS(initialPathLength);
197}
198
199void OrbitThreader::integrate(const IndexMap::value_t& activeSet, double /*maxDrift*/) {
202 do {
204
205 IndexMap::value_t::const_iterator it = activeSet.begin();
206 const IndexMap::value_t::const_iterator end = activeSet.end();
208
209 r_m /= Physics::c * dt_m;
211 r_m = r_m * Physics::c * dt_m;
212
213 Vector_t<double, 3> Ef(0.0), Bf(0.0);
214 std::string names("\t");
215 for (; it != end; ++it) {
218 Vector_t<double, 3> localE(0.0), localB(0.0);
219
220 if ((*it)->applyToReferenceParticle(
221 localR, localP, time_m + 0.5 * dt_m, localE, localB)) {
223 return;
224 }
225 names += (*it)->getName() + ", ";
226
227 Ef += itsOpalBeamline_m.rotateFromLocalCS(*it, localE);
228 Bf += itsOpalBeamline_m.rotateFromLocalCS(*it, localB);
229 }
230
231 if (((pathLength_m > 0.0 && pathLength_m < zstop_m) || dt_m < 0.0)
232 && currentStep_m % loggingFrequency_m == 0 && ippl::Comm->rank() == 0
233 && !OpalData::getInstance()->isOptimizerRun()) {
234 const Vector<double, 3> d = r_m - oldR;
235
236 logger_m << std::setw(18) << std::setprecision(8)
237 << pathLength_m + std::copysign(euclidean_norm(d), dt_m) << std::setw(18)
238 << std::setprecision(8) << r_m(0) << std::setw(18) << std::setprecision(8)
239 << r_m(1) << std::setw(18) << std::setprecision(8) << r_m(2) << std::setw(18)
240 << std::setprecision(8) << p_m(0) << std::setw(18) << std::setprecision(8)
241 << p_m(1) << std::setw(18) << std::setprecision(8) << p_m(2) << std::setw(18)
242 << std::setprecision(8) << Ef(0) << std::setw(18) << std::setprecision(8)
243 << Ef(1) << std::setw(18) << std::setprecision(8) << Ef(2) << std::setw(18)
244 << std::setprecision(8) << Bf(0) << std::setw(18) << std::setprecision(8)
245 << Bf(1) << std::setw(18) << std::setprecision(8) << Bf(2) << std::setw(18)
246 << std::setprecision(8)
247 << reference_m.getM() * (sqrt(dot(p_m, p_m) + 1) - 1) * Units::eV2MeV
248 << std::setw(18) << std::setprecision(8) << (time_m + 0.5 * dt_m) * Units::s2ns
249 << names << std::endl;
250 }
251
252 r_m /= Physics::c * dt_m;
255 r_m = r_m * Physics::c * dt_m;
256
257 const Vector<double, 3> d = r_m - oldR;
258
259 pathLength_m += std::copysign(euclidean_norm(d), dt_m);
260
262 time_m += dt_m;
263
264 nextR = r_m / (Physics::c * dt_m);
265 integrator_m.push(nextR, p_m, dt_m);
266 nextR = nextR * Physics::c * dt_m;
267
268 if (activeSet.empty()
273 return;
274 }
275
276 } while (activeSet == itsOpalBeamline_m.getElements(nextR));
277}
278
280 IndexMap::value_t::const_iterator it = activeSet.begin();
281 const IndexMap::value_t::const_iterator end = activeSet.end();
282
283 for (; it != end; ++it) {
284 if ((*it)->getType() == ElementType::TRAVELINGWAVE
285 || (*it)->getType() == ElementType::RFCAVITY) {
286 return true;
287 }
288 }
289
290 return false;
291}
292
294 const IndexMap::value_t& activeSet, const std::set<std::string>& visitedElements) {
295 if (Options::autoPhase == 0) return;
296
297 IndexMap::value_t::const_iterator it = activeSet.begin();
298 const IndexMap::value_t::const_iterator end = activeSet.end();
299
300 for (; it != end; ++it) {
301 if (((*it)->getType() == ElementType::TRAVELINGWAVE
302 || (*it)->getType() == ElementType::RFCAVITY)
303 && visitedElements.find((*it)->getName()) == visitedElements.end()) {
306
308 ap.getPhaseAtMaxEnergy(initialR, initialP, time_m, dt_m);
309 }
310 }
311}
312
314 IndexMap::value_t::const_iterator it = elementSet.begin();
315 const IndexMap::value_t::const_iterator end = elementSet.end();
316
317 double designEnergy = 0.0;
318 for (; it != end; ++it) {
319 if ((*it)->getType() == ElementType::TRAVELINGWAVE
320 || (*it)->getType() == ElementType::RFCAVITY) {
321 const RFCavity* element = static_cast<const RFCavity*>((*it).get());
322 designEnergy = std::max(designEnergy, element->getDesignEnergy());
323 }
324 }
325
326 return designEnergy;
327}
328
330 dt_m *= -1;
331 ValueRange<double> tmpRange;
332 std::swap(tmpRange, pathLengthRange_m);
333 double initialPathLength = pathLength_m;
334
336 integrator_m.push(nextR, p_m, dt_m);
337 nextR = nextR * Physics::c * dt_m;
338
339 while (std::abs(initialPathLength - pathLength_m) < distTrackBack_m) {
340 auto elementSet = itsOpalBeamline_m.getElements(nextR);
341
342 double maxDrift = computeDriftLengthToBoundingBox(elementSet, r_m, -p_m);
343 maxDrift = std::min(maxDrift, distTrackBack_m);
344 integrate(elementSet, maxDrift);
345
346 nextR = r_m / (Physics::c * dt_m);
347 integrator_m.push(nextR, p_m, dt_m);
348 nextR = nextR * Physics::c * dt_m;
349 }
350 std::swap(tmpRange, pathLengthRange_m);
351 currentStep_m *= -1;
352 dt_m *= -1;
353
355}
356
358 const IndexMap::value_t& elementSet, double start, const Vector_t<double, 3>& R,
359 const Vector_t<double, 3>& P) {
360 IndexMap::value_t::const_iterator it = elementSet.begin();
361 const IndexMap::value_t::const_iterator end = elementSet.end();
362
363 for (; it != end; ++it) {
364 bool found = false;
365 auto prior = elementRegistry_m.equal_range(*it);
366
367 for (auto pit = prior.first; pit != prior.second; ++pit) {
368 if (std::abs((*pit).second.endField_m - start) < 1e-10) {
369 found = true;
370 (*pit).second.endField_m = pathLength_m;
371 break;
372 }
373 }
374
375 if (found) continue;
376
379 double elementEdge = start - initialR(2) * euclidean_norm(initialP) / initialP(2);
380
381 elementPosition ep = {start, pathLength_m, elementEdge};
382 elementRegistry_m.insert(std::make_pair(*it, ep));
383 }
384}
385
387 using registry_key_t = std::shared_ptr<Component>;
388 using registry_map_t = std::map<
389 registry_key_t, std::set<elementPosition, elementPositionComp>,
390 std::owner_less<registry_key_t>>;
391 registry_map_t tmpRegistry;
392
393 for (auto it = elementRegistry_m.begin(); it != elementRegistry_m.end(); ++it) {
394 const registry_key_t& element = (*it).first;
395 elementPosition& ep = (*it).second;
396
397 auto prior = tmpRegistry.find(element);
398 if (prior == tmpRegistry.end()) {
399 std::set<elementPosition, elementPositionComp> tmpSet;
400 tmpSet.insert(ep);
401 tmpRegistry.insert(std::make_pair(element, tmpSet));
402 continue;
403 }
404
405 std::set<elementPosition, elementPositionComp>& set = (*prior).second;
406 set.insert(ep);
407 }
408
410 std::vector<ReferencePathSegment> registeredSegments;
411 for (auto& [element, set] : tmpRegistry) {
412 std::queue<std::pair<double, double>> range;
413
414 for (auto sit = set.begin(); sit != set.end(); ++sit) {
415 range.push(std::make_pair((*sit).elementEdge_m, (*sit).endField_m));
416 registeredSegments.push_back(ReferencePathSegment(
417 (*sit).startField_m, (*sit).endField_m,
418 ReferencePathSegment::element_set_t{element}, (*sit).elementEdge_m));
419 }
420 element->setActionRange(range);
421 }
422
423 std::sort(
424 registeredSegments.begin(), registeredSegments.end(),
425 [](const ReferencePathSegment& lhs, const ReferencePathSegment& rhs) {
426 if (lhs.getBegin() < rhs.getBegin()) {
427 return true;
428 }
429 if (lhs.getBegin() > rhs.getBegin()) {
430 return false;
431 }
432 return lhs.getEnd() < rhs.getEnd();
433 });
434
435 for (const auto& segment : registeredSegments) {
436 actionRangeRegistrationModel_m.addSegment(segment);
437 }
438}
439
441 FieldList& allElements, const std::set<std::string>& visitedElements) {
442 double kineticEnergyeV = reference_m.getM() * (sqrt(dot(p_m, p_m) + 1.0) - 1.0);
443
444 FieldList::iterator it = allElements.begin();
445 const FieldList::iterator end = allElements.end();
446 for (; it != end; ++it) {
447 std::shared_ptr<Component> element = (*it).getElement();
448 if (visitedElements.find(element->getName()) == visitedElements.end()
449 && !(element->getType() == ElementType::RFCAVITY
450 || element->getType() == ElementType::TRAVELINGWAVE)) {
451 element->setDesignEnergy(kineticEnergyeV);
452 }
453 }
454}
455
458 FieldList::iterator it = allElements.begin();
459 const FieldList::iterator end = allElements.end();
460 for (; it != end; ++it) {
461 if (it->getElement()->getType() == ElementType::MARKER) {
462 continue;
463 }
464 BoundingBox other = it->getBoundingBoxInLabCoords();
466 }
468}
469
472 std::array<Vector_t<double, 3>, 2> positions = {r_m - 10 * dR, r_m + 10 * dR};
473
474 for (const Vector_t<double, 3>& pos : positions) {
476 }
477}
478
480 const std::set<std::shared_ptr<Component>>& elements, const Vector_t<double, 3>& position,
481 const Vector_t<double, 3>& direction) const {
482 if (elements.empty()
483 || (elements.size() == 1 && (*elements.begin())->getType() == ElementType::DRIFT)) {
484 std::optional<Vector_t<double, 3>> intersectionPoint =
485 globalBoundingBox_m.getIntersectionPoint(position, direction);
486 if (intersectionPoint) {
487 const Vector_t<double, 3> r = intersectionPoint.value() - position;
488 return euclidean_norm(r);
489 }
490 return 10;
491 }
492
493 return std::numeric_limits<double>::max();
494}
std::list< BeamlineFieldElement > FieldList
Inform * gmsg
Definition changes.cpp:7
ippl::Vector< T, Dim > Vector_t
elements
Definition IndexMap.cpp:168
#define EOL
#define HITMATERIAL
#define EVERYTHINGFINE
Inform * gmsg
Definition changes.cpp:7
ippl::Vector< T, Dim > Vector
double dot(const Vector3D &lhs, const Vector3D &rhs)
Vector dot product.
Definition Vector3D.cpp:95
KOKKOS_INLINE_FUNCTION double euclidean_norm(const Vector_t< T, D > &v)
Definition VectorMath.h:15
KOKKOS_INLINE_FUNCTION void kick(const Vector_t< double, 3 > &R, Vector_t< double, 3 > &P, const Vector_t< double, 3 > &Ef, const Vector_t< double, 3 > &Bf, const double &dt, const double &mass, const double &charge) const
Definition BorisPusher.h:45
KOKKOS_INLINE_FUNCTION void push(Vector_t< double, 3 > &R, const Vector_t< double, 3 > &P, const double &dt) const
void enlargeToContainPosition(const Vector_t< double, 3 > &position)
std::optional< Vector_t< double, 3 > > getIntersectionPoint(const Vector_t< double, 3 > &position, const Vector_t< double, 3 > &direction) const
void enlargeToContainBoundingBox(const BoundingBox &boundingBox)
double getPhaseAtMaxEnergy(const Vector_t< double, 3 > &R, const Vector_t< double, 3 > &P, double t, double dt)
Rigid spatial transform between a parent frame and a local frame.
void add(key_t::first_type initialStep, key_t::second_type finalStep, const value_t &val)
Definition IndexMap.cpp:111
void tidyUp(double zstop)
Definition IndexMap.cpp:147
std::set< std::shared_ptr< Component > > value_t
Definition IndexMap.h:46
void saveSDDS(double startS) const
Definition IndexMap.cpp:182
FieldList getElementByType(ElementType)
Vector_t< double, 3 > rotateToLocalCS(const std::shared_ptr< Component > &comp, const Vector_t< double, 3 > &r) const
CoordinateSystemTrafo getCSTrafoLab2Local(const std::shared_ptr< Component > &comp) const
Return the nominal rigid placement transform .
Vector_t< double, 3 > transformToLocalCS(const std::shared_ptr< Component > &comp, const Vector_t< double, 3 > &r) const
std::set< std::shared_ptr< Component > > getElements(const Vector_t< double, 3 > &x)
Vector_t< double, 3 > rotateFromLocalCS(const std::shared_ptr< Component > &comp, const Vector_t< double, 3 > &r) const
static OpalData * getInstance()
Definition OpalData.cpp:193
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
std::multimap< std::shared_ptr< Component >, elementPosition, std::owner_less< std::shared_ptr< Component > > > elementRegistry_m
unsigned int errorFlag_m
double dt_m
the time step
double time_m
the simulated time
OpalBeamline & itsOpalBeamline_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
ReferencePathModel actionRangeRegistrationModel_m
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
KOKKOS_INLINE_FUNCTION double getM() const
The constant mass per particle.
Definition PartData.h:109
KOKKOS_INLINE_FUNCTION double getQ() const
The constant charge per particle.
Definition PartData.h:107
Interface for standing wave cavities.
Definition RFCavity.h:34
virtual double getDesignEnergy() const override
Definition RFCavity.h:323
Reference-coordinate interval with optional legacy ELEMEDGE anchor.
std::set< std::shared_ptr< Component > > element_set_t
double getdT() const
unsigned long long getNumStepsFinestResolution() const
double getZStop() const
bool reachedEnd() const
ValueRange< double > getPathLengthRange() const
bool isOutside(T value) const
Definition ValueRange.h:40
void enlargeIfOutside(T value)
Definition ValueRange.h:34
bool isInside(T value) const
Definition ValueRange.h:38
int autoPhase
Definition Options.cpp:71
constexpr double c
The velocity of light in m/s.
Definition Physics.h:60
constexpr double eV2MeV
Definition Units.h:77
constexpr double s2ns
Definition Units.h:44
std::string combineFilePath(std::initializer_list< std::string > ilist)
Definition Util.cpp:193
double getGamma(ippl::Vector< double, 3 > p)
Definition Util.h:44
STL namespace.