OPALX (Object Oriented Parallel Accelerator Library for Exascal) master (dc2a29eed580)
OPALX
Loading...
Searching...
No Matches
Astra1DDynamic.cpp
Go to the documentation of this file.
2#include "Fields/Fieldmap.hpp"
4#include "Physics/Physics.h"
5#include "Physics/Units.h"
6#include "Utilities/GSLFFT.h"
9#include "Utilities/Util.h"
10
11#include <algorithm>
12#include <cmath>
13#include <fstream>
14#include <ios>
15
16Astra1DDynamic::Astra1DDynamic(const std::string& filename) : Fieldmap(filename) {
17 std::ifstream file;
18 std::string tmpString;
19 // int skippedValues = 0;
20 double tmpDouble;
21
23
24 // open field map, parse it and disable element on error
25 file.open(Filename_m.c_str());
26 if (file.good()) {
27 bool parsing_passed = true;
28 try {
29 parsing_passed = interpretLine<std::string, int>(file, tmpString, accuracy_m);
30 } catch (GeneralOpalException& e) {
31 parsing_passed = interpretLine<std::string, int, std::string>(
32 file, tmpString, accuracy_m, tmpString);
33
34 tmpString = Util::toUpper(tmpString);
35 if (tmpString != "TRUE" && tmpString != "FALSE")
37 "Astra1DDynamic::Astra1DDynamic",
38 "The third string on the first line of 1D field "
39 "maps has to be either TRUE or FALSE");
40
41 normalize_m = (tmpString == "TRUE");
42 }
43
44 parsing_passed = parsing_passed && interpretLine<double>(file, frequency_m);
45
46 int acceptedValues = 0;
47
48 // Read first data point
49 parsing_passed = parsing_passed && interpretLine<double, double>(file, zbegin_m, tmpDouble);
50
51 double tmpDouble2 = zbegin_m;
52
53 if (parsing_passed) {
54 ++acceptedValues;
56 }
57
58 // Read remaining data points
59 while (true) {
60 bool line_ok = interpretLine<double, double>(file, zend_m, tmpDouble, false);
61 if (!line_ok) {
62 break;
63 }
64
65 if (zend_m - tmpDouble2 > 1e-10) {
66 tmpDouble2 = zend_m;
67 ++acceptedValues;
68 }
69 }
70
71 num_gridpz_m = acceptedValues;
72 lines_read_m = 0;
73
74 if (!parsing_passed && !file.eof()) {
76 zend_m = zbegin_m - 1e-3;
78 "Astra1DDynamic::Astra1DDynamic",
79 "Error reading fieldmap '" + Filename_m + "'");
80 } else {
81 // conversion from MHz to Hz and from frequency to angular frequency
84 }
85 length_m = 2.0 * num_gridpz_m * (zend_m - zbegin_m) / (num_gridpz_m - 1);
86
87 file.close();
88 } else {
90 zbegin_m = 0.0;
91 zend_m = -1e-3;
92 }
93}
94
96
98 if (FourCoefs_m.extent(0) != 0) {
99 return;
100 }
101
102 // Need at least two valid points for dz, spline, and FFT setup
103 if (num_gridpz_m < 2) {
105 "Astra1DDynamic::readMap",
106 "Fieldmap must contain at least two valid sampling points");
107 }
108
109 std::ifstream in(Filename_m.c_str());
110 if (!in.good()) {
112 "Astra1DDynamic::readMap", "Cannot open fieldmap '" + Filename_m + "'");
113 }
114
115 const double dz = (zend_m - zbegin_m) / (num_gridpz_m - 1);
116
117 // CPU arrays - GSL (FFT + spline) is CPU-only
118 double* RealValues = new double[2 * num_gridpz_m];
119 double* zvals = new double[num_gridpz_m];
120
121 // Initialize to something known for easier debugging
122 for (int i = 0; i < 2 * num_gridpz_m; ++i) {
123 RealValues[i] = 0.0;
124 }
125 for (int i = 0; i < num_gridpz_m; ++i) {
126 zvals[i] = 0.0;
127 }
128
129 // GSL objects (CPU only)
132
133 // Future optimization candidate:
134 // gsl_fft_real_wavetable* real =
135 // gsl_fft_real_wavetable_alloc(2 * num_gridpz_m);
136 // gsl_fft_real_workspace* work =
137 // gsl_fft_real_workspace_alloc(2 * num_gridpz_m);
138
139 // Allocate Kokkos View (final data used on GPU)
140 const int size = 2 * accuracy_m - 1;
141 FourCoefs_m = Kokkos::DualView<double*>("FourCoefs", size);
142 auto coefs = FourCoefs_m.view_host();
143
144 // ---- read file / parse fieldmap ----
145 // Skip the first two header lines exactly like the original code
146 std::string tmpString;
147 getLine(in, tmpString);
148 getLine(in, tmpString);
149
150 double Ez_max = 0.0;
151 double lastAcceptedZ = zbegin_m - dz;
152 int accepted = 0;
153
154 while (accepted < num_gridpz_m) {
155 double ztmp = 0.0;
156 double eztmp = 0.0;
157
158 const bool ok = interpretLine<double, double>(in, ztmp, eztmp, false);
159 if (!ok) {
160 break;
161 }
162
163 if (ztmp - lastAcceptedZ > 1e-10) {
164 zvals[accepted] = ztmp;
165 RealValues[accepted] = eztmp;
166 Ez_max = std::max(Ez_max, std::abs(eztmp));
167 lastAcceptedZ = ztmp;
168 ++accepted;
169 }
170 }
171 in.close();
172
173 if (accepted != num_gridpz_m) {
174 gsl_spline_free(Ez_interpolant);
175 gsl_interp_accel_free(Ez_accel);
176 delete[] zvals;
177 delete[] RealValues;
178
179 std::ostringstream os;
180 os << "Mismatch between counted and parsed fieldmap points in '" << Filename_m
181 << "': expected " << num_gridpz_m << ", got " << accepted;
182 throw GeneralOpalException("Astra1DDynamic::readMap", os.str());
183 }
184
185 if (Ez_max == 0.0) {
186 gsl_spline_free(Ez_interpolant);
187 gsl_interp_accel_free(Ez_accel);
188 delete[] zvals;
189 delete[] RealValues;
190
192 "Astra1DDynamic::readMap",
193 "Maximum on-axis field is zero in fieldmap '" + Filename_m + "'");
194 }
195
196 // Interpolate onto an equidistant grid and build a mirrored periodic array
197 gsl_spline_init(Ez_interpolant, zvals, RealValues, num_gridpz_m);
198
199 int ii = num_gridpz_m;
200 for (int i = 0; i < num_gridpz_m - 1; ++i, ++ii) {
201 const double z = zbegin_m + dz * i;
202 RealValues[ii] = gsl_spline_eval(Ez_interpolant, z, Ez_accel);
203 }
204
205 // Ensure periodicity by mirroring
206 RealValues[ii++] = RealValues[num_gridpz_m - 1];
207 --ii;
208 for (int i = 0; i < num_gridpz_m; ++i, --ii) {
209 RealValues[i] = RealValues[ii];
210 }
211
212 // Disable normalization if requested
213 const double norm = normalize_m ? Ez_max : 1.0;
214
215 // Compute Fourier coefficients explicitly from the mirrored periodic samples.
216 const int M = 2 * num_gridpz_m;
217
218 double a0 = 0.0;
219 for (int j = 0; j < M; ++j) {
220 a0 += RealValues[j];
221 }
222 coefs(0) = a0 / (norm * Units::Vpm2MVpm * M);
223
224 for (int l = 1; l < accuracy_m; ++l) {
225 double a_l = 0.0;
226 double b_l = 0.0;
227
228 for (int j = 0; j < M; ++j) {
229 const double theta = Physics::two_pi * double(j) / double(M);
230 a_l += RealValues[j] * std::cos(l * theta);
231 b_l += RealValues[j] * std::sin(l * theta);
232 }
233
234 a_l *= 2.0 / double(M);
235 b_l *= 2.0 / double(M);
236
237 const int n = 2 * l - 1;
238 coefs(n) = a_l / (norm * Units::Vpm2MVpm);
239 coefs(n + 1) = -b_l / (norm * Units::Vpm2MVpm);
240 }
241
242 // Future optimization candidate:
243 // gsl_fft_real_transform(RealValues, 1, 2 * num_gridpz_m, real, work);
244 // coefs(0) = RealValues[0] / (norm * Units::Vpm2MVpm * 2.0 * num_gridpz_m);
245 // for (int i = 1; i < size; ++i) {
246 // coefs(i) = RealValues[i] / (norm * Units::Vpm2MVpm * num_gridpz_m);
247 // }
248
249 gsl_spline_free(Ez_interpolant);
250 gsl_interp_accel_free(Ez_accel);
251 // gsl_fft_real_workspace_free(work);
252 // gsl_fft_real_wavetable_free(real);
253
254 delete[] zvals;
255 delete[] RealValues;
256
257 FourCoefs_m.modify<Kokkos::HostSpace>();
258 FourCoefs_m.sync<Kokkos::DefaultExecutionSpace>();
259
260 Inform m("Astra1DDynamic::readMap");
261 m << level3 << "Read in fieldmap '" << Filename_m << "'" << endl;
262}
263
265 if (FourCoefs_m.extent(0) != 0) {
266 // Reset View (releases memory)
267 FourCoefs_m = Kokkos::DualView<double*>();
268
269 Inform m("Astra1DDynamic::freeMap");
270 m << level3 << "Freed fieldmap '" << Filename_m << "'" << endl;
271 }
272}
273
280void Astra1DDynamic::applyField(std::shared_ptr<ParticleContainer_t> pc, double) {
281 // Local copies of member variables for use in the lambda function
282 const double zbegin = zbegin_m;
283 const double zend = zend_m;
284 const double length = length_m;
285 const double xlrep = xlrep_m;
286 const int accuracy = accuracy_m;
287 const size_t nLocal = pc->getLocalNum();
288
289 // Device-accessible Fourier coefficients
290 auto FourCoefs_device = FourCoefs_m.view_device();
291
292 auto Rview = pc->R.getView();
293 auto Eview = pc->E.getView();
294 auto Bview = pc->B.getView();
295
296 Kokkos::parallel_for(
297 "Astra1DDynamic::applyField", nLocal, KOKKOS_LAMBDA(const size_t i) {
298 const auto& R = Rview(i);
299
300 // 1D Astra field map is only bounded in z
301 if (R(2) >= zbegin && R(2) < zend) {
303 R, Eview(i), Bview(i), FourCoefs_device, zbegin, length, xlrep,
304 accuracy);
305 }
306 });
307}
308
310 std::shared_ptr<ParticleContainer_t> pc, double electricScale, double magneticScale,
311 double startField, double endField) {
312 const double zbegin = zbegin_m;
313 const double zend = zend_m;
314 const double length = length_m;
315 const double xlrep = xlrep_m;
316 const int accuracy = accuracy_m;
317
318 auto FourCoefs_device = FourCoefs_m.view_device();
319
320 auto Rview = pc->R.getView();
321 auto Eview = pc->E.getView();
322 auto Bview = pc->B.getView();
323
324 const size_t nLocal = pc->getLocalNum();
325
326 Kokkos::parallel_for(
327 "Astra1DDynamic::applyRFField", nLocal, KOKKOS_LAMBDA(const size_t i) {
329 Rview(i), Eview(i), Bview(i), FourCoefs_device, zbegin, zend, length, xlrep,
330 accuracy, electricScale, magneticScale, startField, endField);
331 });
332}
333
335 std::shared_ptr<ParticleContainer_t> pc, double entryElectricScale,
336 double entryMagneticScale, double core1ElectricScale, double core1MagneticScale,
337 double core2ElectricScale, double core2MagneticScale, double exitElectricScale,
338 double exitMagneticScale, double /*startField*/, double startCoreField,
339 double startExitField, double mappedStartExitField, double periodLength, double cellLength,
340 double elementLength) {
341 const double zbegin = zbegin_m;
342 const double zend = zend_m;
343 const double length = length_m;
344 const double xlrep = xlrep_m;
345 const int accuracy = accuracy_m;
346 const size_t nLocal = pc->getLocalNum();
347
348 auto FourCoefs_device = FourCoefs_m.view_device();
349
350 auto Rview = pc->R.getView();
351 auto Eview = pc->E.getView();
352 auto Bview = pc->B.getView();
353
354 Kokkos::parallel_for(
355 "Astra1DDynamic::applyTravelingWave", nLocal, KOKKOS_LAMBDA(const size_t i) {
357 Rview(i), Eview(i), Bview(i), FourCoefs_device, zbegin, zend, length, xlrep,
358 accuracy, entryElectricScale, entryMagneticScale, core1ElectricScale,
359 core1MagneticScale, core2ElectricScale, core2MagneticScale,
360 exitElectricScale, exitMagneticScale, startCoreField, startExitField,
361 mappedStartExitField, periodLength, cellLength, elementLength);
362 });
363}
364
367 if (isInside(R)) {
369
370 return false;
371 } else {
372 return true;
373 }
374}
375
386 const DiffDirection& /*dir*/) const {
387 const double kz = Physics::two_pi * (R(2) - zbegin_m) / length_m + Physics::pi;
388 double ezp = 0.0;
389
390 auto FourCoefs = FourCoefs_m.view_host();
391
392 int n = 1;
393 for (int l = 1; l < accuracy_m; ++l, n += 2) {
394 ezp += Physics::two_pi / length_m * l
395 * (-FourCoefs(n) * std::sin(kz * l) - FourCoefs(n + 1) * std::cos(kz * l));
396 }
397
398 E(2) += ezp;
399
400 return false;
401}
402
403void Astra1DDynamic::getFieldDimensions(double& zBegin, double& zEnd) const {
404 zBegin = zbegin_m;
405 zEnd = zend_m;
406}
407
409 double& /*xIni*/, double& /*xFinal*/, double& /*yIni*/, double& /*yFinal*/,
410 double& /*zIni*/, double& /*zFinal*/) const {
411 throw GeneralOpalException("Astra1DDynamic::getFieldDimensions", "not implemented");
412}
413
415
416void Astra1DDynamic::getInfo(Inform* msg) {
417 (*msg) << Filename_m << " (1D dynamic); zini= " << zbegin_m << " m; zfinal= " << zend_m << " m;"
418 << endl;
419}
420
422
423void Astra1DDynamic::setFrequency(double freq) { frequency_m = freq; }
424
425// This function re-reads the fieldmap file to extract the on-axis Ez values,
426// which are stored in F as pairs of (z, Ez).
427// Not computationally optimal, but it was like that in the original
428// OPAL implementation and the on-axis field values are needed for the
429// `TravelingWave` element, which uses the `Astra1DDynamic` fieldmap.
430void Astra1DDynamic::getOnaxisEz(std::vector<std::pair<double, double>>& F) {
431 double Ez_max = 0.0;
432 double tmpDouble;
433 int tmpInt;
434 std::string tmpString;
435
436 F.resize(num_gridpz_m);
437
438 std::ifstream in(Filename_m.c_str());
439 interpretLine<std::string, int>(in, tmpString, tmpInt);
440 interpretLine<double>(in, tmpDouble);
441
442 for (int i = 0; i < num_gridpz_m; ++i) {
443 interpretLine<double, double>(in, F[i].first, F[i].second);
444 if (std::abs(F[i].second) > Ez_max) {
445 Ez_max = std::abs(F[i].second);
446 }
447 }
448 in.close();
449
450 for (int i = 0; i < num_gridpz_m; ++i) {
451 F[i].second /= Ez_max;
452 }
453}
ippl::Vector< T, Dim > Vector_t
@ TAstraDynamic
Definition Fieldmap.h:17
DiffDirection
Definition Fieldmap.h:54
constexpr int gsl_interp_cspline
Definition GSLSpline.h:26
double gsl_spline_eval(const gsl_spline *spline, const double x, gsl_interp_accel *accel)
Evaluate a spline at x using an accelerator.
Definition GSLSpline.h:66
gsl_interp_accel * gsl_interp_accel_alloc()
Allocate an interpolation accelerator.
Definition GSLSpline.h:50
void gsl_spline_init(gsl_spline *spline, const double *x, const double *y, const size_t n)
Initialize a spline with tabulated data.
Definition GSLSpline.h:57
void gsl_spline_free(const gsl_spline *spline)
Free a spline instance.
Definition GSLSpline.h:83
void gsl_interp_accel_free(const gsl_interp_accel *accel)
Free an accelerator instance.
Definition GSLSpline.h:87
gsl_spline * gsl_spline_alloc(const int type, size_t)
Allocate a spline instance (size ignored).
Definition GSLSpline.h:38
Template PIC bunch: IPPL PicManager, shared field mesh/solver, and multiple particle containers.
Accelerator caching last interval indices.
Base class for the linear and cubic interpolation spline classes.
double xlrep_m
Wave number (omega / c)
static KOKKOS_INLINE_FUNCTION void computeTravelingWaveField(const Vector_t< double, 3 > &R, Vector_t< double, 3 > &E, Vector_t< double, 3 > &B, const ViewType &FourCoefs, double zbegin, double zend, double length, double xlrep, int accuracy, double entryElectricScale, double entryMagneticScale, double core1ElectricScale, double core1MagneticScale, double core2ElectricScale, double core2MagneticScale, double exitElectricScale, double exitMagneticScale, double startCoreField, double startExitField, double mappedStartExitField, double periodLength, double cellLength, double elementLength)
void freeMap() override
Pure virtual method to free the map data.
virtual double getFrequency() const override
Get the frequency.
Kokkos::DualView< double * > FourCoefs_m
Fourier coefficients of Ez(z) (device-accessible) Stored as: [a0, a1, b1, a2, b2, ....
static KOKKOS_INLINE_FUNCTION void computeField(const Vector_t< double, 3 > &R, Vector_t< double, 3 > &E, Vector_t< double, 3 > &B, const ViewType &FourCoefs, double zbegin, double length, double xlrep, int accuracy)
void applyRFField(std::shared_ptr< ParticleContainer_t > pc, double electricScale, double magneticScale, double startField, double endField)
Apply RF-scaled Astra1DDynamic field to all particles.
virtual void getOnaxisEz(std::vector< std::pair< double, double > > &F) override
static KOKKOS_INLINE_FUNCTION void computeRFField(const Vector_t< double, 3 > &R, Vector_t< double, 3 > &E, Vector_t< double, 3 > &B, const ViewType &FourCoefs, double zbegin, double zend, double length, double xlrep, int accuracy, double electricScale, double magneticScale, double startField, double endField)
int accuracy_m
Number of Fourier modes used.
virtual void setFrequency(double freq) override
Set the frequency.
virtual void getInfo(Inform *msg) override
Print info about the field map.
virtual void swap() override
Swap coordinates.
bool isInside(const Vector_t< double, 3 > &r) const override
Checks if the given coordinate is inside the volume covered by the fieldmap.
void applyField(std::shared_ptr< ParticleContainer_t > pc, double) override
Apply the FM to all the particles.
void applyTravelingWave(std::shared_ptr< ParticleContainer_t > pc, double entryElectricScale, double entryMagneticScale, double core1ElectricScale, double core1MagneticScale, double core2ElectricScale, double core2MagneticScale, double exitElectricScale, double exitMagneticScale, double startField, double startCoreField, double startExitField, double mappedStartExitField, double periodLength, double cellLength, double elementLength)
Apply the traveling-wave RF field map to all particles.
Astra1DDynamic(const std::string &filename)
int num_gridpz_m
Number of grid points in z-direction (input sampling)
double length_m
Effective periodic length of the field map [m].
void readMap() override
Pure virtual method to read the map data. Called by the public static readMap().
double zbegin_m
Z Bounds relative to element edge.
virtual void getFieldDimensions(double &zBegin, double &zEnd) const override
Get the longitudinal dimensions of the field.
virtual bool getFieldDerivative(const Vector_t< double, 3 > &R, Vector_t< double, 3 > &E, Vector_t< double, 3 > &B, const DiffDirection &dir) const override
Get the field derivative with respect to a direction.
virtual bool getFieldstrength(const Vector_t< double, 3 > &R, Vector_t< double, 3 > &E, Vector_t< double, 3 > &B) const override
Get the field strength at a given point.
Abstract base class for all field maps. It acts as a factory for creating specific field map types ba...
Definition Fieldmap.h:62
MapType Type
Definition Fieldmap.h:231
void disableFieldmapWarning()
Definition Fieldmap.cpp:489
bool normalize_m
Definition Fieldmap.h:236
int lines_read_m
Definition Fieldmap.h:234
std::string Filename_m
Definition Fieldmap.h:233
void getLine(std::ifstream &in, std::string &buffer)
Definition Fieldmap.h:237
void noFieldmapWarning()
Definition Fieldmap.cpp:496
constexpr double two_pi
The value of.
Definition Physics.h:40
constexpr double c
The velocity of light in m/s.
Definition Physics.h:60
constexpr double pi
The value of.
Definition Physics.h:36
constexpr double MHz2Hz
Definition Units.h:113
constexpr double Vpm2MVpm
Definition Units.h:125
std::string toUpper(const std::string &str)
Definition Util.cpp:141