gfields
Loading...
Searching...
No Matches
gfield_asciimap.cc
Go to the documentation of this file.
1// dladdr (used to locate this plugin's own path) needs _GNU_SOURCE on glibc.
2#ifndef _GNU_SOURCE
3#define _GNU_SOURCE
4#endif
5
6// plugin header
7#include "gfield_asciimap.h"
8
9// gemc gfield framework
10#include "gfieldConventions.h"
11
12// geant4 / CLHEP
13#include "G4ThreeVector.hh"
14#include "CLHEP/Units/SystemOfUnits.h"
15
16// c++
17#include <cmath>
18#include <cstdlib>
19#include <dlfcn.h>
20#include <fstream>
21#include <sstream>
22
23using namespace CLHEP;
24
25// Tells the loader how to create a GField in this plugin .so/.dylib.
26extern "C" GField* GFieldFactory(const std::shared_ptr<GOptions>& g) {
27 return static_cast<GField*>(new GField_AsciiMapFactory(g));
28}
29
30
31// ---------------------------------------------------------------------------------------------------
32// Configuration helpers
33// ---------------------------------------------------------------------------------------------------
34
35std::string GField_AsciiMapFactory::param_string(const std::string& key, const std::string& dflt) const {
36 auto it = gfield_definitions.field_parameters.find(key);
37 return (it != gfield_definitions.field_parameters.end() && !it->second.empty()) ? it->second : dflt;
38}
39
40double GField_AsciiMapFactory::param_g4number(const std::string& key, const std::string& dflt) const {
41 return gutilities::getG4Number(param_string(key, dflt));
42}
43
44std::string GField_AsciiMapFactory::field_maps_directory() const {
45 // Locate this plugin's own shared object on disk and point at the sibling "fields" directory
46 // (<plugin_dir>/../fields), the layout produced by `meson install`. No environment variable is used.
47 Dl_info info;
48 if (dladdr(reinterpret_cast<const void*>(&GFieldFactory), &info) != 0 && info.dli_fname != nullptr) {
49 const std::string plugin_path = info.dli_fname;
50 const auto slash = plugin_path.find_last_of('/');
51 if (slash != std::string::npos) { return plugin_path.substr(0, slash) + "/../fields"; }
52 }
53 log->warning("GField_AsciiMapFactory: could not resolve plugin path; using ./fields for the map.");
54 return "fields";
55}
56
57std::optional<unsigned> GField_AsciiMapFactory::axis_of_coordinate(const std::string& name) const {
58 switch (symmetry) {
59 case Symmetry::dipole_x:
60 case Symmetry::dipole_y:
61 case Symmetry::dipole_z:
62 if (name == "longitudinal") return 0;
63 if (name == "transverse") return 1;
64 return std::nullopt;
65 case Symmetry::cyl_x:
66 case Symmetry::cyl_y:
67 case Symmetry::cyl_z:
68 if (name == "transverse") return 0;
69 if (name == "longitudinal") return 1;
70 return std::nullopt;
71 case Symmetry::phi_segmented:
72 if (name == "azimuthal") return 0;
73 if (name == "transverse") return 1;
74 if (name == "longitudinal") return 2;
75 return std::nullopt;
76 case Symmetry::cartesian_3d:
77 case Symmetry::cartesian_3d_quadrant:
78 if (name == "X") return 0;
79 if (name == "Y") return 1;
80 if (name == "Z") return 2;
81 return std::nullopt;
82 }
83 return std::nullopt;
84}
85
86
87// ---------------------------------------------------------------------------------------------------
88// Loading
89// ---------------------------------------------------------------------------------------------------
90
91void GField_AsciiMapFactory::load_coordinate(const std::string& key) {
92 const std::string value = param_string(key, "");
93 if (value.empty()) {
94 log->error(
96 "GField_AsciiMapFactory: missing coordinate <%1> for field <%2>.", key, gfield_definitions.name);
97 }
98
99 // Split "name, npoints, min, max" on commas.
100 const auto tokens = gutilities::getStringVectorFromStringWithDelimiter(value, ",");
101 if (tokens.size() != 4) {
102 log->error(
104 "GField_AsciiMapFactory: coordinate <%1> must be \"name, npoints, min, max\". Got <%2>.", key,
105 value);
106 }
107
108 const std::string name = tokens[0];
109 const int npoints = std::stoi(tokens[1]);
110 const double min = gutilities::getG4Number(tokens[2]);
111 const double max = gutilities::getG4Number(tokens[3]);
112
113 const auto axis = axis_of_coordinate(name);
114 if (!axis) {
115 log->error(
117 "GField_AsciiMapFactory: coordinate name <%1> is not valid for symmetry <%2>.", name,
118 gfield_definitions.field_parameters["symmetry"]);
119 return;
120 }
121 if (npoints < 2) {
122 log->error(
124 "GField_AsciiMapFactory: coordinate <%1> needs at least 2 points; got %2.", name, npoints);
125 }
126
127 np[*axis] = static_cast<unsigned>(npoints);
128 startMap[*axis] = min;
129 endMap[*axis] = max;
130 cellSize[*axis] = (max - min) / (npoints - 1);
131
132 // The map-file column for this coordinate is read in the same unit used in the min/max expression,
133 // so a bare column value is converted back to Geant4 units with that factor.
134 const std::string unit = [&]() {
135 for (const std::string& expr : {tokens[3], tokens[2]}) {
136 const auto star = expr.find('*');
137 if (star != std::string::npos) {
138 return gutilities::removeLeadingAndTrailingSpacesFromString(expr.substr(star + 1));
139 }
140 }
141 return std::string("mm");
142 }();
143
144 columns.push_back(Column{static_cast<int>(*axis), gutilities::getG4Number(1.0, unit)});
145}
146
147void GField_AsciiMapFactory::load_map_file() {
148 const std::string map_name = param_string("map", "");
149 if (map_name.empty()) {
150 log->error(
151 gfields::ERR_MAP_FILE_NOT_FOUND, "GField_AsciiMapFactory: no 'map' file given for field <%1>.",
152 gfield_definitions.name);
153 }
154
155 // Resolve the map file. An explicit path (containing '/') is used as-is. Otherwise the lookup order
156 // is: the explicit `dir` parameter, then the directory of the YAML that defined the field (so a plain
157 // .yaml run from its own directory or referenced by absolute path just works), then the `fields`
158 // directory installed next to the plugin.
159 std::string path = map_name;
160 if (map_name.find('/') == std::string::npos) {
161 std::vector<std::string> candidates;
162 if (const std::string dir = param_string("dir", ""); !dir.empty()) { candidates.push_back(dir); }
163 if (!gfield_definitions.config_dir.empty()) { candidates.push_back(gfield_definitions.config_dir); }
164 candidates.push_back(field_maps_directory());
165
166 path.clear();
167 for (const auto& dir : candidates) {
168 const std::string trial = dir + "/" + map_name;
169 if (std::ifstream(trial).good()) { path = trial; break; }
170 }
171 if (path.empty()) {
172 log->error(
174 "GField_AsciiMapFactory: cannot find map <%1> for field <%2> in dir/config/plugin locations.",
175 map_name, gfield_definitions.name);
176 }
177 }
178
179 std::ifstream in(path);
180 if (!in.good()) {
181 log->error(gfields::ERR_MAP_FILE_NOT_FOUND, "GField_AsciiMapFactory: cannot open map file <%1>.", path);
182 }
183 log->info(
184 1, "Loading ASCII field map <%1> with symmetry <%2>.", path,
185 gfield_definitions.field_parameters["symmetry"]);
186
187 // Total grid points and field-value scaling.
188 std::size_t total = np[0];
189 for (int d = 1; d < ndim; ++d) { total *= np[d]; }
190
191 const double field_scale = param_g4number("scale", "1") *
192 gutilities::getG4Number(1.0, param_string("field_unit", "gauss"));
193
194 B1.assign(total, 0.0f);
195 if (ncomp >= 2) { B2.assign(total, 0.0f); }
196 if (ncomp >= 3) { B3.assign(total, 0.0f); }
197
198 const double tolerance = 0.001; // relative cell-position tolerance for the grid-consistency check
199
200 std::size_t read_points = 0;
201 std::string line;
202 int line_number = 0;
203 while (std::getline(in, line)) {
204 ++line_number;
206 if (trimmed.empty() || trimmed[0] == '#') { continue; }
207
208 std::istringstream tokens(trimmed);
209
210 // Read the coordinate columns (file-column order), convert them to Geant4 units and turn each
211 // into its canonical axis index.
212 unsigned grid_index[3] = {0, 0, 0};
213 bool line_ok = true;
214 for (int c = 0; c < ndim; ++c) {
215 double raw = 0.0;
216 if (!(tokens >> raw)) { line_ok = false; break; }
217 const double coord = raw * columns[c].unitFactor;
218 const int axis = columns[c].axis;
219 const unsigned i = static_cast<unsigned>(
220 std::floor((coord - startMap[axis] + cellSize[axis] / 2) / cellSize[axis]));
221 grid_index[axis] = i;
222
223 const double expected = startMap[axis] + i * cellSize[axis];
224 if (cellSize[axis] != 0.0 && std::fabs(expected - coord) > tolerance * std::fabs(cellSize[axis])) {
225 log->warning(
226 "GField_AsciiMapFactory: %1:%2 axis %3 value %4 does not match grid point %5.", path,
227 line_number, axis, coord, expected);
228 }
229 }
230
231 double comp[3] = {0.0, 0.0, 0.0};
232 for (int k = 0; k < ncomp && line_ok; ++k) {
233 if (!(tokens >> comp[k])) { line_ok = false; }
234 }
235 if (!line_ok) {
236 log->error(
237 gfields::ERR_MAP_FILE_NOT_FOUND, "GField_AsciiMapFactory: %1:%2 has fewer than %3 numbers.",
238 path, line_number, ndim + ncomp);
239 }
240
241 std::size_t flat = (ndim == 2) ? idx2(grid_index[0], grid_index[1])
242 : idx3(grid_index[0], grid_index[1], grid_index[2]);
243 if (flat >= total) {
244 log->warning("GField_AsciiMapFactory: %1:%2 maps outside the grid; skipped.", path, line_number);
245 continue;
246 }
247
248 B1[flat] = static_cast<float>(comp[0] * field_scale);
249 if (ncomp >= 2) { B2[flat] = static_cast<float>(comp[1] * field_scale); }
250 if (ncomp >= 3) { B3[flat] = static_cast<float>(comp[2] * field_scale); }
251 ++read_points;
252 }
253
254 if (read_points != total) {
255 log->warning(
256 "GField_AsciiMapFactory: %1 provided %2 points but the grid expects %3.", path, read_points, total);
257 }
258 log->info(1, "ASCII field map <%1> loaded: %2 points.", gfield_definitions.name, read_points);
259}
260
262 gfield_definitions = gfd;
263
264 // Decode the symmetry once. ndim/ncomp follow from it.
265 const std::string sym = param_string("symmetry", "");
266 if (sym == "dipole-x") { symmetry = Symmetry::dipole_x; ndim = 2; ncomp = 1; }
267 else if (sym == "dipole-y") { symmetry = Symmetry::dipole_y; ndim = 2; ncomp = 1; }
268 else if (sym == "dipole-z") { symmetry = Symmetry::dipole_z; ndim = 2; ncomp = 1; }
269 else if (sym == "cylindrical-x") { symmetry = Symmetry::cyl_x; ndim = 2; ncomp = 2; }
270 else if (sym == "cylindrical-y") { symmetry = Symmetry::cyl_y; ndim = 2; ncomp = 2; }
271 else if (sym == "cylindrical-z") { symmetry = Symmetry::cyl_z; ndim = 2; ncomp = 2; }
272 else if (sym == "phi-segmented") { symmetry = Symmetry::phi_segmented; ndim = 3; ncomp = 3; }
273 else if (sym == "cartesian_3D") { symmetry = Symmetry::cartesian_3d; ndim = 3; ncomp = 3; }
274 else if (sym == "cartesian_3D_quadrant") { symmetry = Symmetry::cartesian_3d_quadrant; ndim = 3; ncomp = 3; }
275 else {
276 log->error(
277 gfields::ERR_WRONG_FIELD_SYMMETRY, "GField_AsciiMapFactory: unknown symmetry <%1> for field <%2>.",
278 sym, gfield_definitions.name);
279 }
280
281 // Grid coordinates, in map-file column order.
282 columns.clear();
283 columns.reserve(ndim);
284 load_coordinate("coordinate1");
285 load_coordinate("coordinate2");
286 if (ndim == 3) { load_coordinate("coordinate3"); }
287
288 // Interpolation.
289 linear = (param_string("interpolation", "linear") != "none");
290
291 // Overall placement.
292 mapOrigin[0] = param_g4number("vx", "0");
293 mapOrigin[1] = param_g4number("vy", "0");
294 mapOrigin[2] = param_g4number("vz", "0");
295 mapRotation[0] = param_g4number("rx", "0*deg");
296 mapRotation[1] = param_g4number("ry", "0*deg");
297 mapRotation[2] = param_g4number("rz", "0*deg");
298 sinAlpha = std::sin(mapRotation[0]); cosAlpha = std::cos(mapRotation[0]);
299 sinBeta = std::sin(mapRotation[1]); cosBeta = std::cos(mapRotation[1]);
300 sinGamma = std::sin(mapRotation[2]); cosGamma = std::cos(mapRotation[2]);
301
302 load_map_file();
303}
304
305
306// ---------------------------------------------------------------------------------------------------
307// Field evaluation
308// ---------------------------------------------------------------------------------------------------
309
310void GField_AsciiMapFactory::GetFieldValue(const double pos[3], G4double* bfield) const {
311 bfield[0] = bfield[1] = bfield[2] = 0.0;
312
313 if (std::isnan(pos[0]) || std::isnan(pos[1]) || std::isnan(pos[2])) {
314 log->warning("GField_AsciiMapFactory: field coordinates requested are nan.");
315 return;
316 }
317
318 // Shift to the map frame.
319 const double x[3] = {pos[0] - mapOrigin[0], pos[1] - mapOrigin[1], pos[2] - mapOrigin[2]};
320
321 switch (symmetry) {
322 case Symmetry::dipole_x:
323 case Symmetry::dipole_y:
324 case Symmetry::dipole_z:
325 value_dipole(x, bfield);
326 break;
327 case Symmetry::cyl_x:
328 case Symmetry::cyl_y:
329 case Symmetry::cyl_z:
330 value_cylindrical(x, bfield);
331 break;
332 case Symmetry::phi_segmented:
333 value_phi_segmented(x, bfield);
334 break;
335 case Symmetry::cartesian_3d:
336 case Symmetry::cartesian_3d_quadrant:
337 value_cartesian3d(x, bfield);
338 break;
339 }
340
341 log->info(
342 2, "ASCII field <%1> at (%2, %3, %4) cm = (%5, %6, %7) gauss", gfield_definitions.name, pos[0] / cm,
343 pos[1] / cm, pos[2] / cm, bfield[0] / gauss, bfield[1] / gauss, bfield[2] / gauss);
344}
345
346
347void GField_AsciiMapFactory::value_dipole(const double x[3], double* bfield) const {
348 // Axis 0 = longitudinal, axis 1 = transverse.
349 double LC = 0.0, TC = 0.0;
350 if (symmetry == Symmetry::dipole_z) { TC = std::fabs(x[0]); LC = x[1]; }
351 else if (symmetry == Symmetry::dipole_x) { TC = std::fabs(x[1]); LC = x[2]; }
352 else /* dipole_y */ { TC = std::fabs(x[0]); LC = x[2]; }
353
354 if (LC < startMap[0] || TC < startMap[1]) { return; }
355 unsigned IL = static_cast<unsigned>(std::floor((LC - startMap[0]) / cellSize[0]));
356 unsigned IT = static_cast<unsigned>(std::floor((TC - startMap[1]) / cellSize[1]));
357 if (IL >= np[0] - 1 || IT >= np[1] - 1) { return; }
358
359 double b = 0.0;
360 if (!linear) {
361 if (std::fabs(startMap[0] + IL * cellSize[0] - LC) > std::fabs(startMap[0] + (IL + 1) * cellSize[0] - LC)) IL++;
362 if (std::fabs(startMap[1] + IT * cellSize[1] - TC) > std::fabs(startMap[1] + (IT + 1) * cellSize[1] - TC)) IT++;
363 b = B1[idx2(IL, IT)];
364 }
365 else {
366 const double xlr = (LC - (startMap[0] + IL * cellSize[0])) / cellSize[0];
367 const double xtr = (TC - (startMap[1] + IT * cellSize[1])) / cellSize[1];
368 const double b10 = B1[idx2(IL, IT)] * (1.0 - xtr) + B1[idx2(IL, IT + 1)] * xtr;
369 const double b11 = B1[idx2(IL + 1, IT)] * (1.0 - xtr) + B1[idx2(IL + 1, IT + 1)] * xtr;
370 b = b10 * (1.0 - xlr) + b11 * xlr;
371 }
372
373 if (symmetry == Symmetry::dipole_x) { bfield[0] = b; }
374 else if (symmetry == Symmetry::dipole_y) { bfield[1] = b; }
375 else { bfield[2] = b; }
376
377 rotate_field(bfield);
378}
379
380
381void GField_AsciiMapFactory::value_cylindrical(const double x[3], double* bfield) const {
382 // Axis 0 = transverse (radial), axis 1 = longitudinal.
383 double LC = 0.0, TC = 0.0, phi = 0.0;
384 if (symmetry == Symmetry::cyl_z) {
385 LC = x[2]; TC = std::sqrt(x[0] * x[0] + x[1] * x[1]); phi = G4ThreeVector(x[0], x[1], x[2]).phi();
386 }
387 else if (symmetry == Symmetry::cyl_x) {
388 LC = x[0]; TC = std::sqrt(x[1] * x[1] + x[2] * x[2]); phi = G4ThreeVector(x[1], x[2], x[0]).phi();
389 }
390 else /* cyl_y */ {
391 LC = x[1]; TC = std::sqrt(x[0] * x[0] + x[2] * x[2]); phi = G4ThreeVector(x[2], x[0], x[1]).phi();
392 }
393
394 if (TC < startMap[0] || LC < startMap[1]) { return; }
395 unsigned IT = static_cast<unsigned>(std::floor((TC - startMap[0]) / cellSize[0]));
396 unsigned IL = static_cast<unsigned>(std::floor((LC - startMap[1]) / cellSize[1]));
397 if (IT >= np[0] - 1 || IL >= np[1] - 1) { return; }
398
399 double b1 = 0.0, b2 = 0.0;
400 if (!linear) {
401 if (std::fabs(startMap[0] + IT * cellSize[0] - TC) > std::fabs(startMap[0] + (IT + 1) * cellSize[0] - TC)) IT++;
402 if (std::fabs(startMap[1] + IL * cellSize[1] - LC) > std::fabs(startMap[1] + (IL + 1) * cellSize[1] - LC)) IL++;
403 b1 = B1[idx2(IT, IL)];
404 b2 = B2[idx2(IT, IL)];
405 }
406 else {
407 const double xtr = (TC - (startMap[0] + IT * cellSize[0])) / cellSize[0];
408 const double xlr = (LC - (startMap[1] + IL * cellSize[1])) / cellSize[1];
409 const double b10 = B1[idx2(IT, IL)] * (1.0 - xtr) + B1[idx2(IT + 1, IL)] * xtr;
410 const double b11 = B1[idx2(IT, IL + 1)] * (1.0 - xtr) + B1[idx2(IT + 1, IL + 1)] * xtr;
411 b1 = b10 * (1.0 - xlr) + b11 * xlr;
412 const double b20 = B2[idx2(IT, IL)] * (1.0 - xtr) + B2[idx2(IT + 1, IL)] * xtr;
413 const double b21 = B2[idx2(IT, IL + 1)] * (1.0 - xtr) + B2[idx2(IT + 1, IL + 1)] * xtr;
414 b2 = b20 * (1.0 - xlr) + b21 * xlr;
415 }
416
417 if (symmetry == Symmetry::cyl_z) {
418 bfield[0] = b1 * std::cos(phi); bfield[1] = b1 * std::sin(phi); bfield[2] = b2;
419 }
420 else if (symmetry == Symmetry::cyl_x) {
421 bfield[0] = b2; bfield[1] = b1 * std::cos(phi); bfield[2] = b1 * std::sin(phi);
422 }
423 else /* cyl_y */ {
424 bfield[1] = b2; bfield[0] = b1 * std::sin(phi); bfield[2] = b1 * std::cos(phi);
425 }
426
427 rotate_field(bfield);
428}
429
430
431void GField_AsciiMapFactory::value_phi_segmented(const double x[3], double* bfield) const {
432 // Axis 0 = azimuthal, axis 1 = transverse, axis 2 = longitudinal. Fields are stored in the local
433 // (first-segment) frame and rotated back to the query phi.
434 double aC = std::atan2(x[1], x[0]) * rad; // phi
435 if (aC < 0) { aC += 360 * deg; }
436 const double tC = std::sqrt(x[0] * x[0] + x[1] * x[1]); // R
437 const double lC = x[2]; // Z
438
439 // Fold into the first 60-degree segment, keeping the rotation back to the lab.
440 double aLC = aC;
441 while (aLC / deg > 30) { aLC -= 60 * deg; }
442 const double dphi = aC - aLC;
443 const double aaLC = std::fabs(aLC);
444 const int sign = (aLC >= 0 ? 1 : -1);
445
446 if (aC < startMap[0] || tC < startMap[1] || lC < startMap[2]) { return; }
447 unsigned aI = static_cast<unsigned>(std::floor((aaLC - startMap[0]) / cellSize[0]));
448 unsigned tI = static_cast<unsigned>(std::floor((tC - startMap[1]) / cellSize[1]));
449 unsigned lI = static_cast<unsigned>(std::floor((lC - startMap[2]) / cellSize[2]));
450 if (aI >= np[0] - 1 || tI >= np[1] - 1 || lI >= np[2] - 1) { return; }
451
452 double mfield[3] = {0.0, 0.0, 0.0};
453 if (!linear) {
454 if (std::fabs(startMap[0] + aI * cellSize[0] - aaLC) > std::fabs(startMap[0] + (aI + 1) * cellSize[0] - aaLC)) aI++;
455 if (std::fabs(startMap[1] + tI * cellSize[1] - tC) > std::fabs(startMap[1] + (tI + 1) * cellSize[1] - tC)) tI++;
456 if (std::fabs(startMap[2] + lI * cellSize[2] - lC) > std::fabs(startMap[2] + (lI + 1) * cellSize[2] - lC)) lI++;
457 mfield[0] = B1[idx3(aI, tI, lI)];
458 mfield[1] = B2[idx3(aI, tI, lI)];
459 mfield[2] = B3[idx3(aI, tI, lI)];
460 }
461 else {
462 const double xaz = (aaLC - (startMap[0] + aI * cellSize[0])) / cellSize[0];
463 const double xtr = (tC - (startMap[1] + tI * cellSize[1])) / cellSize[1];
464 const double xlr = (lC - (startMap[2] + lI * cellSize[2])) / cellSize[2];
465
466 const std::vector<float>* comps[3] = {&B1, &B2, &B3};
467 for (int k = 0; k < 3; ++k) {
468 const std::vector<float>& B = *comps[k];
469 const double b00 = B[idx3(aI, tI, lI)] * (1 - xaz) + B[idx3(aI + 1, tI, lI)] * xaz;
470 const double b01 = B[idx3(aI, tI, lI + 1)] * (1 - xaz) + B[idx3(aI + 1, tI, lI + 1)] * xaz;
471 const double b10 = B[idx3(aI, tI + 1, lI)] * (1 - xaz) + B[idx3(aI + 1, tI + 1, lI)] * xaz;
472 const double b11 = B[idx3(aI, tI + 1, lI + 1)] * (1 - xaz) + B[idx3(aI + 1, tI + 1, lI + 1)] * xaz;
473 const double b0 = b00 * (1 - xtr) + b10 * xtr;
474 const double b1 = b01 * (1 - xtr) + b11 * xtr;
475 mfield[k] = b0 * (1 - xlr) + b1 * xlr;
476 }
477 }
478
479 // Rotate the local field back to the query azimuth.
480 bfield[0] = sign * mfield[0] * std::cos(dphi / rad) - mfield[1] * std::sin(dphi / rad);
481 bfield[1] = sign * mfield[0] * std::sin(dphi / rad) + mfield[1] * std::cos(dphi / rad);
482 bfield[2] = sign * mfield[2];
483
484 rotate_field(bfield);
485}
486
487
488void GField_AsciiMapFactory::value_cartesian3d(const double x[3], double* bfield) const {
489 // Axis 0 = X, axis 1 = Y, axis 2 = Z.
490 double XX = x[0], YY = x[1], ZZ = x[2];
491
492 if (symmetry == Symmetry::cartesian_3d_quadrant) {
493 // Fold the query point into the stored first quadrant (x>=0, y>=0).
494 if (x[0] >= 0 && x[1] >= 0) { XX = x[0]; YY = x[1]; }
495 else if (x[0] >= 0 && x[1] < 0) { XX = -x[1]; YY = x[0]; }
496 else if (x[0] < 0 && x[1] < 0) { XX = -x[0]; YY = -x[1]; }
497 else /* x[0] < 0 && x[1] >= 0 */ { XX = x[1]; YY = -x[0]; }
498 ZZ = x[2];
499 if (XX < 0 || YY < 0) { return; }
500 }
501
502 if (XX < startMap[0] || YY < startMap[1] || ZZ < startMap[2]) { return; }
503 if (XX >= endMap[0] || YY >= endMap[1] || ZZ >= endMap[2]) { return; }
504 const unsigned IXX = static_cast<unsigned>(std::floor((XX - startMap[0]) / cellSize[0]));
505 const unsigned IYY = static_cast<unsigned>(std::floor((YY - startMap[1]) / cellSize[1]));
506 const unsigned IZZ = static_cast<unsigned>(std::floor((ZZ - startMap[2]) / cellSize[2]));
507
508 double B[3] = {0.0, 0.0, 0.0};
509 if (!linear) {
510 unsigned ix = IXX, iy = IYY, iz = IZZ;
511 if (std::fabs(startMap[0] + ix * cellSize[0] - XX) > std::fabs(startMap[0] + (ix + 1) * cellSize[0] - XX)) ix++;
512 if (std::fabs(startMap[1] + iy * cellSize[1] - YY) > std::fabs(startMap[1] + (iy + 1) * cellSize[1] - YY)) iy++;
513 if (std::fabs(startMap[2] + iz * cellSize[2] - ZZ) > std::fabs(startMap[2] + (iz + 1) * cellSize[2] - ZZ)) iz++;
514 B[0] = B1[idx3(ix, iy, iz)];
515 B[1] = B2[idx3(ix, iy, iz)];
516 B[2] = B3[idx3(ix, iy, iz)];
517 }
518 else {
519 const double Xd = (XX - (startMap[0] + IXX * cellSize[0])) / cellSize[0];
520 const double Yd = (YY - (startMap[1] + IYY * cellSize[1])) / cellSize[1];
521 const double Zd = (ZZ - (startMap[2] + IZZ * cellSize[2])) / cellSize[2];
522
523 const std::vector<float>* comps[3] = {&B1, &B2, &B3};
524 for (int k = 0; k < 3; ++k) {
525 const std::vector<float>& Bk = *comps[k];
526 const double c00 = Bk[idx3(IXX, IYY, IZZ)] * (1 - Xd) + Bk[idx3(IXX + 1, IYY, IZZ)] * Xd;
527 const double c01 = Bk[idx3(IXX, IYY, IZZ + 1)] * (1 - Xd) + Bk[idx3(IXX + 1, IYY, IZZ + 1)] * Xd;
528 const double c10 = Bk[idx3(IXX, IYY + 1, IZZ)] * (1 - Xd) + Bk[idx3(IXX + 1, IYY + 1, IZZ)] * Xd;
529 const double c11 = Bk[idx3(IXX, IYY + 1, IZZ + 1)] * (1 - Xd) + Bk[idx3(IXX + 1, IYY + 1, IZZ + 1)] * Xd;
530 const double c0 = c00 * (1 - Yd) + c10 * Yd;
531 const double c1 = c01 * (1 - Yd) + c11 * Yd;
532 B[k] = c0 * (1 - Zd) + c1 * Zd;
533 }
534 }
535
536 if (symmetry == Symmetry::cartesian_3d_quadrant) {
537 // Mirror the field components back to the query quadrant.
538 if (x[0] >= 0 && x[1] >= 0) { bfield[0] = B[0]; bfield[1] = B[1]; }
539 else if (x[0] >= 0 && x[1] < 0) { bfield[0] = B[1]; bfield[1] = -B[0]; }
540 else if (x[0] < 0 && x[1] < 0) { bfield[0] = -B[0]; bfield[1] = -B[1]; }
541 else /* x[0] < 0 && x[1] >= 0 */ { bfield[0] = -B[1]; bfield[1] = B[0]; }
542 bfield[2] = B[2];
543 }
544 else {
545 bfield[0] = B[0]; bfield[1] = B[1]; bfield[2] = B[2];
546 }
547
548 rotate_field(bfield);
549}
550
551
552void GField_AsciiMapFactory::rotate_field(double* bfield) const {
553 // Rotate the field axes, not the point: each rotation is the inverse of the point rotation.
554 if (mapRotation[0] != 0) {
555 const double yPrime = bfield[1] * cosAlpha + bfield[2] * sinAlpha;
556 const double zPrime = -bfield[1] * sinAlpha + bfield[2] * cosAlpha;
557 bfield[1] = yPrime; bfield[2] = zPrime;
558 }
559 if (mapRotation[1] != 0) {
560 const double xPrime = bfield[0] * cosBeta - bfield[2] * sinBeta;
561 const double zPrime = bfield[0] * sinBeta + bfield[2] * cosBeta;
562 bfield[0] = xPrime; bfield[2] = zPrime;
563 }
564 if (mapRotation[2] != 0) {
565 const double xPrime = bfield[0] * cosGamma + bfield[1] * sinGamma;
566 const double yPrime = -bfield[0] * sinGamma + bfield[1] * cosGamma;
567 bfield[0] = xPrime; bfield[1] = yPrime;
568 }
569}
std::shared_ptr< GLogger > log
Concrete GField reading a magnetic field from an ASCII map and a YAML definition.
void load_field_definitions(GFieldDefinition gfd) override
Parse the YAML definition, build the grid and read the map file.
void GetFieldValue(const double pos[3], G4double *bfield) const override
Compute the field at the lab-frame point pos, writing {Bx,By,Bz} (Geant4 units) into bfield.
Abstract base class representing a magnetic field.
Definition gfield.h:105
GFieldDefinition gfield_definitions
Stored field definition used for configuration and logging.
Definition gfield.h:191
GField * GFieldFactory(const std::shared_ptr< GOptions > &g)
constexpr int ERR_WRONG_FIELD_SYMMETRY
constexpr int ERR_MAP_FILE_NOT_FOUND
constexpr int ERR_WRONG_COORDINATE_DEF
double getG4Number(const string &v, bool warnIfNotUnit=false)
vector< string > getStringVectorFromStringWithDelimiter(const string &input, const string &x)
string removeLeadingAndTrailingSpacesFromString(const std::string &input)
Lightweight configuration carrier used to load and configure a GField plugin.
Definition gfield.h:27