gfields
Loading...
Searching...
No Matches
gfield_options.cc
Go to the documentation of this file.
1#include "gfield_options.h"
2#include "gfieldConventions.h"
3#include "gmagneto.h"
4
5// geant4
6#include "G4UnitsTable.hh"
7
8// gemc
9#include "gutilities.h"
10#include "gfactory_options.h"
11#include "goptionsConventions.h"
12
13// c++
14#include <cmath>
15#include <cstdlib>
16#include <fstream>
17#include <iostream>
18#include <sstream>
19
20namespace gfields {
21
22namespace {
23
24struct FieldQueryPoint {
25 double position[3] = {0.0, 0.0, 0.0};
26 std::string source;
27 int line = 0;
28};
29
30bool is_query_set(const std::string& value) {
31 return !value.empty() && value != UNINITIALIZEDSTRINGQUANTITY && value != "not provided";
32}
33
34FieldQueryPoint parse_field_query_point(const std::string& expression, const std::string& source, int line) {
35 std::string cleaned = expression;
36 if (const auto comment = cleaned.find('#'); comment != std::string::npos) {
37 cleaned = cleaned.substr(0, comment);
38 }
39 for (auto& c : cleaned) {
40 if (c == ',') { c = ' '; }
41 }
43
44 FieldQueryPoint point;
45 point.source = source;
46 point.line = line;
47
48 if (cleaned.empty()) { return point; }
49
50 std::istringstream tokens(cleaned);
51 std::string x;
52 std::string y;
53 std::string z;
54 std::string extra;
55 tokens >> x >> y >> z >> extra;
56 if (x.empty() || y.empty() || z.empty() || !extra.empty()) {
57 std::cerr << FATALERRORL << "field query point must contain exactly three coordinates with units";
58 if (line > 0) { std::cerr << " at " << source << ":" << line; }
59 else { std::cerr << " in " << source; }
60 std::cerr << ". Got <" << expression << ">." << std::endl;
61 std::exit(EC__G4NUMBERERROR);
62 }
63
64 point.position[0] = gutilities::getG4Number(x, true);
65 point.position[1] = gutilities::getG4Number(y, true);
66 point.position[2] = gutilities::getG4Number(z, true);
67 return point;
68}
69
70bool is_blank_or_comment_line(const std::string& line) {
72 return trimmed.empty() || trimmed[0] == '#';
73}
74
75void append_field_query_file_points(const std::string& filename, std::vector<FieldQueryPoint>& points) {
76 std::ifstream input(filename);
77 if (!input) {
78 std::cerr << FATALERRORL << "can't open field query point file " << filename << "." << std::endl;
79 std::exit(EC__FILENOTFOUND);
80 }
81
82 std::string line;
83 int line_number = 0;
84 while (std::getline(input, line)) {
85 ++line_number;
86 if (is_blank_or_comment_line(line)) { continue; }
87 points.push_back(parse_field_query_point(line, filename, line_number));
88 }
89}
90
91void print_field_query_result(const std::string& field_name,
92 const FieldQueryPoint& point,
93 const double bfield[3]) {
94 const double bmag = std::sqrt(
95 bfield[0] * bfield[0] +
96 bfield[1] * bfield[1] +
97 bfield[2] * bfield[2]);
98 std::cout << "field=" << field_name
99 << " source=" << point.source;
100 if (point.line > 0) { std::cout << ":" << point.line; }
101 std::cout << " x=" << G4BestUnit(point.position[0], "Length")
102 << " y=" << G4BestUnit(point.position[1], "Length")
103 << " z=" << G4BestUnit(point.position[2], "Length")
104 << " Bx=" << G4BestUnit(bfield[0], "Magnetic flux density")
105 << " By=" << G4BestUnit(bfield[1], "Magnetic flux density")
106 << " Bz=" << G4BestUnit(bfield[2], "Magnetic flux density")
107 << " |B|=" << G4BestUnit(bmag, "Magnetic flux density")
108 << std::endl;
109}
110
111} // namespace
112
113// Build field definitions by reading the option tree and translating each entry into a GFieldDefinition.
114std::vector<GFieldDefinition> get_GFieldDefinition(const std::shared_ptr<GOptions>& gopts) {
115 std::vector<GFieldDefinition> gfield_defs;
116
117 // Multipoles:
118 // Each "gmultipoles" entry becomes one independently named field definition.
119 auto gmultipoles_node = gopts->getOptionNode("gmultipoles");
120 for (auto gmultipoles_item : gmultipoles_node) {
121 GFieldDefinition gfield_def = GFieldDefinition();
122
123 // Core identity and integration configuration.
124 gfield_def.name = gopts->get_variable_in_option<std::string>(
125 gmultipoles_item, "name", goptions::NODFLT);
126 gfield_def.integration_stepper = gopts->get_variable_in_option<std::string>(
127 gmultipoles_item, "integration_stepper", GFIELD_DEFAULT_INTEGRATION_STEPPER);
128 gfield_def.minimum_step = gutilities::getG4Number(gopts->get_variable_in_option<std::string>(
129 gmultipoles_item, "minimum_step", GFIELD_DEFAULT_MINIMUM_STEP));
130
131 // Multipole parameters:
132 // Values are stored as strings to preserve unit expressions and are parsed later by the concrete field.
133 gfield_def.add_map_parameter("pole_number", gopts->get_variable_in_option<std::string>(
134 gmultipoles_item, "pole_number", goptions::NODFLT));
135 gfield_def.add_map_parameter("vx", gopts->get_variable_in_option<std::string>(
136 gmultipoles_item, "vx", GFIELD_DEFAULT_VERTEX));
137 gfield_def.add_map_parameter("vy", gopts->get_variable_in_option<std::string>(
138 gmultipoles_item, "vy", GFIELD_DEFAULT_VERTEX));
139 gfield_def.add_map_parameter("vz", gopts->get_variable_in_option<std::string>(
140 gmultipoles_item, "vz", GFIELD_DEFAULT_VERTEX));
141 gfield_def.add_map_parameter("rotation_angle", gopts->get_variable_in_option<std::string>(
142 gmultipoles_item, "rotation_angle", GFIELD_DEFAULT_ROTANGLE));
143 gfield_def.add_map_parameter("rotaxis", gopts->get_variable_in_option<std::string>(
144 gmultipoles_item, "rotaxis", goptions::NODFLT));
145 gfield_def.add_map_parameter("strength", gopts->get_variable_in_option<std::string>(
146 gmultipoles_item, "strength", goptions::NODFLT));
147 gfield_def.add_map_parameter("longitudinal", gopts->get_variable_in_option<std::string>(
148 gmultipoles_item, "longitudinal", "false"));
149
150 // The type field controls the shared-library plugin name through GFieldDefinition::gfieldPluginName().
151 gfield_def.type = "multipoles";
152
153 gfield_defs.push_back(gfield_def);
154 }
155
156 // Generic plugin-backed fields:
157 // Each "gfields" entry names a field, selects a plugin through "type", and carries an arbitrary
158 // set of scalar parameters that are forwarded verbatim to the plugin via field_parameters. This
159 // lets external plugins (e.g. clas12 mapped fields) be configured without changing this parser.
160 auto gfields_node = gopts->getOptionNode("gfields");
161 for (auto gfields_item : gfields_node) {
162 GFieldDefinition gfield_def = GFieldDefinition();
163
164 // Core identity and integration configuration (the schema-defined keys).
165 gfield_def.name = gopts->get_variable_in_option<std::string>(
166 gfields_item, "name", goptions::NODFLT);
167 gfield_def.type = gopts->get_variable_in_option<std::string>(
168 gfields_item, "type", goptions::NODFLT);
169 gfield_def.integration_stepper = gopts->get_variable_in_option<std::string>(
170 gfields_item, "integration_stepper", GFIELD_DEFAULT_INTEGRATION_STEPPER);
171 gfield_def.minimum_step = gutilities::getG4Number(gopts->get_variable_in_option<std::string>(
172 gfields_item, "minimum_step", GFIELD_DEFAULT_MINIMUM_STEP));
173
174 // Every remaining (scalar) key is forwarded to the plugin as a string parameter.
175 // Nested maps/sequences are not supported here: plugin parameters must be scalar values.
176 for (auto it = gfields_item.begin(); it != gfields_item.end(); ++it) {
177 auto key = it->first.as<std::string>();
178 if (key == "name" || key == "type" || key == "integration_stepper" || key == "minimum_step") {
179 continue;
180 }
181 gfield_def.add_map_parameter(key, it->second.as<std::string>());
182 }
183
184 gfield_defs.push_back(gfield_def);
185 }
186
187 return gfield_defs;
188}
189
190
191// Define all options for this module, including plugin fields and logger integration (non-Doxygen summary).
197
198 std::string help;
199 help = "Adds electromagnetic multipole field(s) to the simulation. \n \n";
200 help += "Mandatory keys: name, pole_number, rotaxis, strength. \n \n";
201 help += "Example (a quadrupole centered 30 cm downstream): \n";
202 help += "-gmultipoles=\"[{name: q1, pole_number: 4, rotaxis: Z, strength: 1.2, vz: 30*cm}]\"\n";
203 std::vector<GVariable> gmultipoles = {
204 {"name", goptions::NODFLT, "Field name (unique key used by GMagneto maps)"},
205 {"integration_stepper", GFIELD_DEFAULT_INTEGRATION_STEPPER, "Geant4 integration stepper name (string)"},
206 {"minimum_step", GFIELD_DEFAULT_MINIMUM_STEP, "Minimum step for the G4ChordFinder (Geant4 length units)"},
207 {"pole_number", goptions::NODFLT, "Pole number (even integer >= 2): 2=dipole, 4=quadrupole, ..."},
208 {"vx", GFIELD_DEFAULT_VERTEX, "Origin X component (Geant4 length units)"},
209 {"vy", GFIELD_DEFAULT_VERTEX, "Origin Y component (Geant4 length units)"},
210 {"vz", GFIELD_DEFAULT_VERTEX, "Origin Z component (Geant4 length units)"},
211 {"rotation_angle", GFIELD_DEFAULT_ROTANGLE, "Roll rotation angle about rotaxis (Geant4 angle units)"},
212 {"rotaxis", goptions::NODFLT, "Rotation/longitudinal axis: one of X, Y, Z"},
213 {"strength", goptions::NODFLT, "Field strength in Tesla (defined at 1 m reference radius for multipoles)"},
214 {"longitudinal", "false", "If true, return a uniform field aligned with rotaxis (solenoid-like)"}
215 };
216 goptions.defineOption("gmultipoles", "define the e.m. gmultipoles", gmultipoles, help);
217
218 std::string gfields_help;
219 gfields_help = "Adds a generic, plugin-backed electromagnetic field to the simulation. \n \n";
220 gfields_help += "The 'type' selects the plugin shared library named gfield<type>Factory. \n";
221 gfields_help += "Any additional scalar keys are forwarded verbatim to that plugin as string \n";
222 gfields_help += "parameters (so the plugin alone decides which parameters it understands). \n \n";
223 gfields_help += "Mandatory keys: name, type. \n \n";
224 gfields_help += "Example (clas12 binary mapped field from the clas12-systems plugin): \n";
225 gfields_help += "-gfields=\"[{name: clas12, type: clas12bin, solenoid: solenoid_map, torus: torus_map}]\"\n";
226 std::vector<GVariable> gfields = {
227 {"name", goptions::NODFLT, "Field name (unique key used by GMagneto maps)"},
228 {"type", goptions::NODFLT, "Field type; selects the plugin shared library gfield<type>Factory"},
229 {"integration_stepper", GFIELD_DEFAULT_INTEGRATION_STEPPER, "Geant4 integration stepper name (string)"},
230 {"minimum_step", GFIELD_DEFAULT_MINIMUM_STEP, "Minimum step for the G4ChordFinder (Geant4 length units)"}
231 };
232 goptions.defineOption("gfields", "define a generic plugin-backed e.m. field", gfields, gfields_help);
233
234 goptions.defineOption(
235 GVariable("fieldAt", UNINITIALIZEDSTRINGQUANTITY, "query all configured fields at x y z"),
236 "Evaluate all configured electromagnetic fields at one absolute coordinate.\n \n"
237 "The value must contain three coordinate expressions with units, separated by spaces.\n \n"
238 "Example: -fieldAt=\"10*cm 0*mm 2*m\"\n \n");
239
240 goptions.defineOption(
241 GVariable("fieldMapPoints", UNINITIALIZEDSTRINGQUANTITY, "ASCII file of x y z points for field queries"),
242 "Evaluate all configured electromagnetic fields at coordinates listed in an ASCII file.\n \n"
243 "Each non-empty, non-comment line must contain three coordinate expressions with units.\n"
244 "Coordinates may be separated by spaces or commas. Lines beginning with # are ignored.\n \n"
245 "Example: -fieldMapPoints=points.txt\n \n");
246
247 return goptions;
248}
249
250bool runFieldQueries(const std::shared_ptr<GOptions>& gopts) {
251 const auto field_at = gopts->getScalarString("fieldAt");
252 const auto field_map_points = gopts->getScalarString("fieldMapPoints");
253
254 if (!is_query_set(field_at) && !is_query_set(field_map_points)) { return false; }
255
256 std::vector<FieldQueryPoint> points;
257 if (is_query_set(field_at)) { points.push_back(parse_field_query_point(field_at, "fieldAt", 0)); }
258 if (is_query_set(field_map_points)) { append_field_query_file_points(field_map_points, points); }
259
260 auto magneto = std::make_shared<GMagneto>(gopts);
261 auto field_names = magneto->getFieldNames();
262 if (field_names.empty()) {
263 std::cerr << FATALERRORL << "field query requested, but no electromagnetic fields are configured."
264 << std::endl;
265 std::exit(EC__NOOPTIONFOUND);
266 }
267
268 std::cout << "# field query results" << std::endl;
269 for (const auto& point : points) {
270 for (const auto& field_name : field_names) {
271 double bfield[3] = {0.0, 0.0, 0.0};
272 magneto->getField(field_name)->GetFieldValue(point.position, bfield);
273 print_field_query_result(field_name, point, bfield);
274 }
275 }
276
277 return true;
278}
279
280}
constexpr const char * PLUGIN_LOGGER
constexpr const char * GFIELD_LOGGER
Definition gfield.h:13
constexpr const char * GMAGNETO_LOGGER
Definition gfield.h:14
std::string source
int line
double position[3]
#define EC__NOOPTIONFOUND
#define GFIELD_DEFAULT_ROTANGLE
Default multipole roll rotation angle (string with Geant4 units).
#define GFIELD_DEFAULT_VERTEX
Default origin coordinate component for multipole fields (string with Geant4 units).
#define GFIELD_DEFAULT_INTEGRATION_STEPPER
Default integration stepper name used when the requested stepper is unsupported.
#define GFIELD_DEFAULT_MINIMUM_STEP
Default minimum step for the chord finder (string with Geant4 units).
#define UNINITIALIZEDSTRINGQUANTITY
#define FATALERRORL
GOptions defineOptions()
std::vector< GFieldDefinition > get_GFieldDefinition(const std::shared_ptr< GOptions > &gopts)
Build the list of field definitions from the provided options.
bool runFieldQueries(const std::shared_ptr< GOptions > &gopts)
Evaluate configured fields from fieldAt and fieldMapPoints options, if requested.
GOptions defineOptions()
Define all options used by the GField module and its built-in field factories.
const std::string NODFLT
double getG4Number(const string &v, bool warnIfNotUnit=false)
string removeLeadingAndTrailingSpacesFromString(const std::string &input)
Lightweight configuration carrier used to load and configure a GField plugin.
Definition gfield.h:27
double minimum_step
Minimum step size used when constructing the G4ChordFinder (Geant4 length units).
Definition gfield.h:40
std::string name
Field name key used by GMagneto maps.
Definition gfield.h:34
void add_map_parameter(const std::string &key, const std::string &value)
Add or overwrite a parameter in the field-parameter map.
Definition gfield.h:53
std::string integration_stepper
Integration stepper name (string) used when creating the G4ChordFinder.
Definition gfield.h:37
std::string type
Field type discriminator used to derive the plugin factory name (e.g. "multipoles").
Definition gfield.h:43