OPALX (Object Oriented Parallel Accelerator Library for Exascal) master (dc2a29eed580)
OPALX
Loading...
Searching...
No Matches
Monitor.cpp
Go to the documentation of this file.
1//
2// Class Monitor
3// Defines the abstract interface for a beam position monitor.
4//
5// Copyright (c) 2026, Paul Scherrer Institut, Villigen PSI, Switzerland
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/Monitor.h"
19
22#include "Fields/Fieldmap.h"
23#include "PartBunch/PartBunch.h"
24#include "Physics/Physics.h"
27#include "Utilities/Options.h"
28#include "Utilities/Util.h"
29
30#include <cmath>
31#include <filesystem>
32#include <fstream>
33#include <memory>
34
35std::map<double, SetStatistics> Monitor::statFileEntries_sm;
36const double Monitor::halfLength_s = 0.005;
37
39
41 : Component(right),
42 filename_m(right.filename_m),
43 plane_m(right.plane_m),
44 type_m(right.type_m),
45 numPassages_m(0) {}
46
47Monitor::Monitor(const std::string& name)
48 : Component(name),
49 filename_m(""),
50 plane_m(OFF),
51 type_m(CollectionType::SPATIAL),
52 numPassages_m(0) {}
53
55
56void Monitor::accept(BeamlineVisitor& visitor) const { visitor.visitMonitor(*this); }
57
58bool Monitor::apply(const std::shared_ptr<ParticleContainer_t>& pc) {
59 if (!online_m || lossDs_m == nullptr || pc == nullptr || RefPartBunch_m == nullptr) {
60 return false;
61 }
62
64 return false;
65 }
66
67 const long long globalStep = RefPartBunch_m->getGlobalTrackStep();
68 const double sPos = pc->get_sPos();
69
70 if (globalStep == 0 && std::abs(sPos) < 1.0e-14) {
71 Inform msg("Monitor::apply(pc)");
72 msg << level5 << "Ignoring pre-tracking spatial particle crossing"
73 << " globalStep=" << globalStep << " pc_spos=" << sPos << endl;
74 return false;
75 }
76
77 const auto nLoc = pc->getLocalNum();
78 if (nLoc == 0) {
79 return false;
80 }
81
82 auto Rview = pc->R.getView();
83 auto Pview = pc->P.getView();
84 auto dtview = pc->dt.getView();
85 auto IDview = pc->ID.getView();
86 auto Qview = pc->getQView();
87 auto Mview = pc->getMView();
88
89 auto hR = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), Rview);
90 auto hP = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), Pview);
91 auto hdt = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), dtview);
92 auto hID = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), IDview);
93 auto hQ = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), Qview);
94 auto hM = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), Mview);
95
96 const bool qmAreAttributes =
97 (pc->getQMStorageMode() == ParticleContainer_t::QMStorageMode::Attributes);
98
99 const double bunchTime = RefPartBunch_m->getT();
100
101 for (size_t i = 0; i < nLoc; ++i) {
102 const Vector_t<double, 3> R = hR(i);
103 const Vector_t<double, 3> P = hP(i);
104 const double dt = hdt(i);
105
106 const Vector_t<double, 3> singleStep = Physics::c * dt * Util::getBeta(P);
107
108 if (singleStep(2) == 0.0) {
109 continue;
110 }
111
112 if (dt * R(2) < 0.0 && dt * (R(2) + singleStep(2)) > 0.0) {
113 const double frac = -R(2) / singleStep(2);
114
115 const Vector_t<double, 3> crossingR = R + frac * singleStep;
116 const double crossingTime = bunchTime + 0.5 * RefPartBunch_m->getdT() + frac * dt;
117
118 const std::size_t id = static_cast<std::size_t>(hID(i));
119 const double q = qmAreAttributes ? hQ(i) : hQ(0);
120 const double m = qmAreAttributes ? hM(i) : hM(0);
121
122 lossDs_m->addParticle(OpalParticle(id, crossingR, P, crossingTime, q, m));
123 }
124 }
125
126 return false;
127}
128
130 const size_t& i, const double& t, Vector_t<double, 3>& /*E*/, Vector_t<double, 3>& /*B*/) {
131 if (!online_m || lossDs_m == nullptr || RefPartBunch_m == nullptr) {
132 return false;
133 }
134
136 return false;
137 }
138
139 const std::shared_ptr<ParticleContainer_t> pc = RefPartBunch_m->getParticleContainer();
140 if (!pc) {
141 return false;
142 }
143
144 auto Rview = pc->R.getView();
145 auto Pview = pc->P.getView();
146 auto dtview = pc->dt.getView();
147 auto IDview = pc->ID.getView();
148 auto Qview = pc->getQView();
149 auto Mview = pc->getMView();
150
151 const Vector_t<double, 3> R = Rview(i);
152 const Vector_t<double, 3> P = Pview(i);
153 const double dt = dtview(i);
154
155 const Vector_t<double, 3> singleStep = Physics::c * dt * Util::getBeta(P);
156
157 if (singleStep(2) == 0.0) {
158 return false;
159 }
160
161 if (dt * R(2) < 0.0 && dt * (R(2) + singleStep(2)) > 0.0) {
162 const double frac = -R(2) / singleStep(2);
163 const Vector_t<double, 3> crossingR = R + frac * singleStep;
164 const double crossingTime = t + frac * dt;
165
166 const bool qmAreAttributes =
167 (pc->getQMStorageMode() == ParticleContainer_t::QMStorageMode::Attributes);
168
169 const std::size_t id = static_cast<std::size_t>(IDview(i));
170 const double q = qmAreAttributes ? Qview(i) : Qview(0);
171 const double m = qmAreAttributes ? Mview(i) : Mview(0);
172
173 lossDs_m->addParticle(OpalParticle(id, crossingR, P, crossingTime, q, m));
174 }
175
176 return false;
177}
178
180 const Vector_t<double, 3>& /*R*/, const Vector_t<double, 3>& /*P*/, const double& /*t*/,
182 // Monitor is field-free.
183 // This overload does not provide particle ID and may not provide the correct
184 // per-particle dt/q/m information, so it cannot safely save monitor hits.
185 return false;
186}
187
189 const Vector_t<double, 3>& refR, const Vector_t<double, 3>& refP) {
190 if (lossDs_m == nullptr || RefPartBunch_m == nullptr) {
191 return;
192 }
193
194 const std::shared_ptr<ParticleContainer_t> pc = RefPartBunch_m->getParticleContainer();
195 if (!pc) {
196 return;
197 }
198
199 const double dt = RefPartBunch_m->getdT();
200 const double cdt = Physics::c * dt;
201
202 const Vector_t<double, 3> driftPerTimeStep = cdt * Util::getBeta(refP);
203
204 if (driftPerTimeStep(2) == 0.0) {
205 return;
206 }
207
208 const double tau = -refR(2) / driftPerTimeStep(2);
209
211 refR + tau * driftPerTimeStep, getQuaternion(refP, Vector_t<double, 3>(0.0, 0.0, 1.0)));
212
213 const CoordinateSystemTrafo refToLocalCSTrafo =
214 update * (getCSTrafoGlobal2Local() * pc->getToLabTrafo());
215
216 auto Rview = pc->R.getView();
217 auto Pview = pc->P.getView();
218 auto IDview = pc->ID.getView();
219 auto Qview = pc->getQView();
220 auto Mview = pc->getMView();
221
222 auto hR = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), Rview);
223 auto hP = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), Pview);
224 auto hID = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), IDview);
225 auto hQ = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), Qview);
226 auto hM = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), Mview);
227
228 const auto nLoc = pc->getLocalNum();
229
230 const bool qmAreAttributes =
231 (pc->getQMStorageMode() == ParticleContainer_t::QMStorageMode::Attributes);
232
233 const double particleTime = RefPartBunch_m->getT();
234
235 for (size_t i = 0; i < nLoc; ++i) {
236 const Vector_t<double, 3> globalR = hR(i);
237 const Vector_t<double, 3> globalP = hP(i);
238
239 const Vector_t<double, 3> beta = refToLocalCSTrafo.rotateTo(Util::getBeta(globalP));
240
241 const Vector_t<double, 3> dS = (tau - 0.5) * cdt * beta;
242
243 const Vector_t<double, 3> localR = refToLocalCSTrafo.transformTo(globalR) + dS;
244
245 const std::size_t id = static_cast<std::size_t>(hID(i));
246
247 const double q = qmAreAttributes ? hQ(i) : hQ(0);
248
249 const double macroMassGeV = qmAreAttributes ? hM(i) : hM(0);
250 const double macroWeight = std::abs(q) / std::abs(Physics::q_e);
251
252 const double m = macroWeight > 0.0 ? macroMassGeV * Units::GeV2MeV / macroWeight
253 : macroMassGeV * Units::GeV2MeV;
254
255 lossDs_m->addParticle(OpalParticle(id, localR, globalP, particleTime, q, m));
256 }
257}
258
260 const Vector_t<double, 3>& R, const Vector_t<double, 3>& P, const double& t,
262 if (!online_m || lossDs_m == nullptr || RefPartBunch_m == nullptr
263 || OpalData::getInstance()->isInPrepState()) {
264 return false;
265 }
266
267 const std::shared_ptr<ParticleContainer_t> pc = RefPartBunch_m->getParticleContainer();
268 if (!pc) {
269 return false;
270 }
271
272 const double dt = RefPartBunch_m->getdT();
273 const double cdt = Physics::c * dt;
274 const Vector_t<double, 3> singleStep = cdt * Util::getBeta(P);
275
276 if (singleStep(2) == 0.0) {
277 return false;
278 }
279
280 if (dt * R(2) < 0.0 && dt * (R(2) + singleStep(2)) > 0.0) {
281 const long long globalStep = RefPartBunch_m->getGlobalTrackStep();
282 const double sPos = pc->get_sPos();
283
284 if (globalStep == 0 && std::abs(sPos) < 1.0e-14) {
285 Inform msg("Monitor::applyToReferenceParticle");
286 msg << level5 << "Ignoring pre-tracking reference crossing"
287 << " globalStep=" << globalStep << " pc_spos=" << sPos << endl;
288 return false;
289 }
290
291 const double frac = -R(2) / singleStep(2);
292 const double time = t + frac * dt;
293 const Vector_t<double, 3> dR = frac * singleStep;
294
295 const Vector_t<double, 3> dsVec = dR + 0.5 * singleStep;
296 const double ds = euclidean_norm(dsVec);
297
298 lossDs_m->addReferenceParticle(
300 time, pc->get_sPos() + ds, RefPartBunch_m->getGlobalTrackStep());
301
304
305 auto stats = lossDs_m->computeStatistics(1);
306 if (!stats.empty()) {
307 statFileEntries_sm.insert(std::make_pair(stats.begin()->spos_m, *stats.begin()));
308
309 OpalData::OpenMode openMode = (numPassages_m > 0)
312
313 lossDs_m->save(1, openMode);
314 }
315 }
316
318 }
319
320 return false;
321}
322
323void Monitor::initialise(PartBunch_t* bunch, double& startField, double& endField) {
324 RefPartBunch_m = bunch;
325
326 endField = startField + halfLength_s;
327 startField -= halfLength_s;
328
329 double currentPosition = endField;
330 if (bunch != nullptr) {
331 const std::shared_ptr<ParticleContainer_t> pc = bunch->getParticleContainer();
332 if (pc) {
333 currentPosition = pc->get_sPos();
334 }
335 }
336
338
340 || currentPosition < startField) {
341 namespace fs = std::filesystem;
342
343 const fs::path lossFileName(filename_m + ".h5");
344 if (fs::exists(lossFileName)) {
345 ippl::Comm->barrier();
346 if (ippl::Comm->rank() == 0) {
347 fs::remove(lossFileName);
348 }
349 ippl::Comm->barrier();
350 }
351 }
352
353 lossDs_m = std::make_unique<LossDataSink>(filename_m, !Options::asciidump, type_m);
354}
355
357
358void Monitor::goOnline(const double&) { online_m = true; }
359
361 if (!lossDs_m) {
362 return;
363 }
364
365 auto stats = lossDs_m->computeStatistics(numPassages_m);
366 for (auto& stat : stats) {
367 statFileEntries_sm.insert(std::make_pair(stat.spos_m, stat));
368 }
369
371 lossDs_m->save(numPassages_m);
372 }
373}
374
375bool Monitor::bends() const { return false; }
376
377void Monitor::getFieldExtend(double& zBegin, double& zEnd) const {
378 zBegin = -halfLength_s;
379 zEnd = halfLength_s;
380}
381
384
386 if (statFileEntries_sm.size() == 0) return;
387
388 std::string fileName =
389 OpalData::getInstance()->getInputBasename() + std::string("_Monitors.stat");
390 auto instance = OpalData::getInstance();
391 bool hasPriorTrack = instance->hasPriorTrack();
392 bool inRestartRun = instance->inRestartRun();
393
394 auto it = statFileEntries_sm.begin();
395 double spos = it->first;
396 Util::rewindLinesSDDS(fileName, spos, false);
397
398 MonitorStatisticsWriter writer(fileName, hasPriorTrack || inRestartRun);
399
400 for (const auto& entry : statFileEntries_sm) {
401 writer.addRow(entry.second);
402 }
403
404 statFileEntries_sm.clear();
405}
ippl::Vector< T, Dim > Vector_t
ElementType
Definition ElementBase.h:94
CollectionType
Template PIC bunch: IPPL PicManager, shared field mesh/solver, and multiple particle containers.
Quaternion getQuaternion(ippl::Vector< double, 3 > u, ippl::Vector< double, 3 > ref)
Return a unit quaternion that rotates vec onto reference.
KOKKOS_INLINE_FUNCTION double euclidean_norm(const Vector_t< T, D > &v)
Definition VectorMath.h:15
virtual void visitMonitor(const Monitor &)=0
Apply the algorithm to a beam position monitor.
bool online_m
Definition Component.h:226
PartBunch_t * RefPartBunch_m
Definition Component.h:225
Rigid spatial transform between a parent frame and a local frame.
ippl::Vector< double, 3 > transformFrom(const ippl::Vector< double, 3 > &r) const
Map a point from the local frame back to the parent frame.
ippl::Vector< double, 3 > rotateFrom(const ippl::Vector< double, 3 > &r) const
Rotate a vector from the local frame back into the parent frame.
ippl::Vector< double, 3 > rotateTo(const ippl::Vector< double, 3 > &r) const
Rotate a vector from the parent frame into the local frame.
ippl::Vector< double, 3 > transformTo(const ippl::Vector< double, 3 > &r) const
Map a point from the parent frame to the local frame.
bool update(const AttributeSet &)
Update element.
CoordinateSystemTrafo getCSTrafoGlobal2Local() const
std::string getOutputFN() const
Get output filename.
CoordinateSystemTrafo csTrafoGlobal2Local_m
void addRow(const SetStatistics &set)
CollectionType type_m
Definition Monitor.h:109
static void writeStatistics()
Definition Monitor.cpp:385
void accept(BeamlineVisitor &) const override
Apply visitor to Monitor.
Definition Monitor.cpp:56
std::string filename_m
Definition Monitor.h:107
virtual void goOnline(const double &kineticEnergy) override
Definition Monitor.cpp:358
~Monitor() override
Definition Monitor.cpp:54
virtual ElementType getType() const override
Definition Monitor.cpp:383
virtual void goOffline() override
Definition Monitor.cpp:360
std::unique_ptr< LossDataSink > lossDs_m
Definition Monitor.h:112
Monitor()
Definition Monitor.cpp:38
static std::map< double, SetStatistics > statFileEntries_sm
Definition Monitor.h:114
virtual void finalise() override
Definition Monitor.cpp:356
unsigned int numPassages_m
Definition Monitor.h:110
virtual bool applyToReferenceParticle(const Vector_t< double, 3 > &R, const Vector_t< double, 3 > &P, const double &t, Vector_t< double, 3 > &E, Vector_t< double, 3 > &B) override
Apply to reference particle with position R and momemtum P.
Definition Monitor.cpp:259
virtual void initialise(PartBunch_t *bunch, double &startField, double &endField) override
Definition Monitor.cpp:323
static const double halfLength_s
Definition Monitor.h:115
void driftToCorrectPositionAndSave(const Vector_t< double, 3 > &R, const Vector_t< double, 3 > &P)
Definition Monitor.cpp:188
virtual bool bends() const override
Definition Monitor.cpp:375
virtual void getFieldExtend(double &zBegin, double &zEnd) const override
Return the field-support extent of the component.
Definition Monitor.cpp:377
virtual bool apply(const std::shared_ptr< ParticleContainer_t > &pc) override
Apply to all particles. Kernel launch moved inside the function.
Definition Monitor.cpp:58
std::string getInputBasename()
get input file name without extension
Definition OpalData.cpp:571
OpenMode getOpenMode() const
Definition OpalData.cpp:300
static OpalData * getInstance()
Definition OpalData.cpp:193
OpenMode
Enum for writing to files.
Definition OpalData.h:58
Vector_t< double, Dim > R(size_t)
Do not use; throws (access positions via ParticleContainer::R).
Definition PartBunch.h:611
double getdT() const
Get the global time step.
Definition PartBunch.h:642
double getT() const
Get the current simulation time.
Definition PartBunch.h:651
long long getGlobalTrackStep() const
Current global tracking step.
Definition PartBunch.h:673
bool asciidump
Definition Options.cpp:87
constexpr double q_e
The elementary charge in As.
Definition Physics.h:84
constexpr double c
The velocity of light in m/s.
Definition Physics.h:60
constexpr double GeV2MeV
Definition Units.h:80
unsigned int rewindLinesSDDS(const std::string &fileName, double maxSPos, bool checkForTime)
rewind the SDDS file such that the spos of the last step is less or equal to maxSPos
Definition Util.cpp:214
ippl::Vector< double, 3 > getBeta(ippl::Vector< double, 3 > p)
Definition Util.h:51