6#include "G4UnitsTable.hh"
24struct FieldQueryPoint {
30bool is_query_set(
const std::string& value) {
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);
39 for (
auto& c : cleaned) {
40 if (c ==
',') { c =
' '; }
44 FieldQueryPoint point;
48 if (cleaned.empty()) {
return point; }
50 std::istringstream tokens(cleaned);
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";
59 else { std::cerr <<
" in " <<
source; }
60 std::cerr <<
". Got <" << expression <<
">." << std::endl;
61 std::exit(EC__G4NUMBERERROR);
70bool is_blank_or_comment_line(
const std::string&
line) {
72 return trimmed.empty() || trimmed[0] ==
'#';
75void append_field_query_file_points(
const std::string& filename, std::vector<FieldQueryPoint>& points) {
76 std::ifstream input(filename);
78 std::cerr <<
FATALERRORL <<
"can't open field query point file " << filename <<
"." << std::endl;
79 std::exit(EC__FILENOTFOUND);
84 while (std::getline(input,
line)) {
86 if (is_blank_or_comment_line(
line)) {
continue; }
87 points.push_back(parse_field_query_point(
line, filename, line_number));
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")
115 std::vector<GFieldDefinition> gfield_defs;
119 auto gmultipoles_node = gopts->getOptionNode(
"gmultipoles");
120 for (
auto gmultipoles_item : gmultipoles_node) {
124 gfield_def.
name = gopts->get_variable_in_option<std::string>(
133 gfield_def.
add_map_parameter(
"pole_number", gopts->get_variable_in_option<std::string>(
141 gfield_def.
add_map_parameter(
"rotation_angle", gopts->get_variable_in_option<std::string>(
143 gfield_def.
add_map_parameter(
"rotaxis", gopts->get_variable_in_option<std::string>(
145 gfield_def.
add_map_parameter(
"strength", gopts->get_variable_in_option<std::string>(
147 gfield_def.
add_map_parameter(
"longitudinal", gopts->get_variable_in_option<std::string>(
148 gmultipoles_item,
"longitudinal",
"false"));
151 gfield_def.
type =
"multipoles";
153 gfield_defs.push_back(gfield_def);
160 auto gfields_node = gopts->getOptionNode(
"gfields");
161 for (
auto gfields_item : gfields_node) {
165 gfield_def.
name = gopts->get_variable_in_option<std::string>(
167 gfield_def.
type = gopts->get_variable_in_option<std::string>(
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") {
184 gfield_defs.push_back(gfield_def);
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)"},
207 {
"pole_number",
goptions::NODFLT,
"Pole number (even integer >= 2): 2=dipole, 4=quadrupole, ..."},
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)"}
216 goptions.defineOption(
"gmultipoles",
"define the e.m. gmultipoles", gmultipoles, help);
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"},
232 goptions.defineOption(
"gfields",
"define a generic plugin-backed e.m. field",
gfields, gfields_help);
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");
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");
251 const auto field_at = gopts->getScalarString(
"fieldAt");
252 const auto field_map_points = gopts->getScalarString(
"fieldMapPoints");
254 if (!is_query_set(field_at) && !is_query_set(field_map_points)) {
return false; }
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); }
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."
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);
constexpr const char * PLUGIN_LOGGER
constexpr const char * GFIELD_LOGGER
constexpr const char * GMAGNETO_LOGGER
#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
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.
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.
double minimum_step
Minimum step size used when constructing the G4ChordFinder (Geant4 length units).
std::string name
Field name key used by GMagneto maps.
void add_map_parameter(const std::string &key, const std::string &value)
Add or overwrite a parameter in the field-parameter map.
std::string integration_stepper
Integration stepper name (string) used when creating the G4ChordFinder.
std::string type
Field type discriminator used to derive the plugin factory name (e.g. "multipoles").