Unit-bearing multipole/dipole field parameters (vx, vy, vz, rotation_angle, strength) are parsed with bare std::stod(), which silently drops any unit expression such as 30*deg, 2*tesla, or 10*cm.
Affected files
gemc/gfields/gfield.h:144 — get_field_parameter_int uses bare stoi
gemc/gfields/gfield.h:151 — get_field_parameter_double uses bare stod
gemc/gfields/gfieldFactories/multipoles/gfield_multipoles.cc:160-185 — consumers
gemc/gfields/gfield_options.cc:24,28-34 — values stored as unit-bearing strings ("parsed later by the concrete field")
Details
The option layer deliberately keeps multipole parameters as raw strings to preserve unit expressions (gfield_options.cc:27):
// Values are stored as strings to preserve unit expressions and are parsed later by the concrete field.
gfield_def.add_map_parameter("vx", gopts->get_variable_in_option<std::string>(gmultipoles_item, "vx", GFIELD_DEFAULT_VERTEX));
...
gfield_def.add_map_parameter("rotation_angle", ...);
gfield_def.add_map_parameter("strength", ...);
Note that minimum_step in the same file (:24) is correctly run through gutilities::getG4Number(...). But the concrete field reads the others through the bare accessor:
double get_field_parameter_double(const std::string& key) { return stod(gfield_definitions.field_parameters[key]); }
std::stod("30*deg") returns 30.0 and discards *deg — no error, no warning. So rotation_angle, vx/vy/vz, and strength are interpreted in raw internal units (radians, mm, default field unit) regardless of what the user wrote. In load_field_definitions:
origin[0] = get_field_parameter_double("vx");
origin[1] = get_field_parameter_double("vy");
origin[2] = get_field_parameter_double("vz");
rotation_angle = get_field_parameter_double("rotation_angle");
...
strength = get_field_parameter_double("strength");
The only integer parameter is pole_number (get_field_parameter_int("pole_number"), :160), a plain count of poles. A pole count must NOT take units, so stoi is correct there and should stay as-is.
Impact
Any multipole/dipole field configured with unit expressions (the documented and natural way to specify 30*deg, 2*tesla, 10*cm) is silently misinterpreted, producing fields with the wrong magnitude, position, and rotation. Failure is silent — the simulation runs and produces physically wrong field maps.
Proposed fix
Route the double accessor through gutilities::getG4Number, which already implements unit-expression parsing. Leave the int accessor on stoi (pole counts are unitless). Add the gutilities.h include since gfield.h does not currently pull it in.
--- a/gemc/gfields/gfield.h
+++ b/gemc/gfields/gfield.h
@@
#include <gemc/gfactory/gfactory.h>
#include <gemc/gbase/gbase.h>
+#include <gemc/guts/gutilities.h>
@@
- int get_field_parameter_int(const std::string& key) { return stoi(gfield_definitions.field_parameters[key]); }
+ int get_field_parameter_int(const std::string& key) { return stoi(gfield_definitions.field_parameters[key]); }
@@
- double get_field_parameter_double(const std::string& key) { return stod(gfield_definitions.field_parameters[key]); }
+ double get_field_parameter_double(const std::string& key) { return gutilities::getG4Number(gfield_definitions.field_parameters[key]); }
gutilities::getG4Number is declared in gemc/guts/gutilities.h:280:
double getG4Number(const string& v, bool warnIfNotUnit = false);
(Adjust the include path to match how other headers in this module reference gutilities.h; the bracket form above mirrors the existing gfactory.h/gbase.h includes in gfield.h.) No change is needed at the call sites in gfield_multipoles.cc.
Split out from #102 (one issue per bug). Found via an AI-assisted code review with manual verification against commit 5f8ce875.
Unit-bearing multipole/dipole field parameters (
vx,vy,vz,rotation_angle,strength) are parsed with barestd::stod(), which silently drops any unit expression such as30*deg,2*tesla, or10*cm.Affected files
gemc/gfields/gfield.h:144—get_field_parameter_intuses barestoigemc/gfields/gfield.h:151—get_field_parameter_doubleuses barestodgemc/gfields/gfieldFactories/multipoles/gfield_multipoles.cc:160-185— consumersgemc/gfields/gfield_options.cc:24,28-34— values stored as unit-bearing strings ("parsed later by the concrete field")Details
The option layer deliberately keeps multipole parameters as raw strings to preserve unit expressions (
gfield_options.cc:27):Note that
minimum_stepin the same file (:24) is correctly run throughgutilities::getG4Number(...). But the concrete field reads the others through the bare accessor:std::stod("30*deg")returns30.0and discards*deg— no error, no warning. Sorotation_angle,vx/vy/vz, andstrengthare interpreted in raw internal units (radians, mm, default field unit) regardless of what the user wrote. Inload_field_definitions:The only integer parameter is
pole_number(get_field_parameter_int("pole_number"),:160), a plain count of poles. A pole count must NOT take units, sostoiis correct there and should stay as-is.Impact
Any multipole/dipole field configured with unit expressions (the documented and natural way to specify
30*deg,2*tesla,10*cm) is silently misinterpreted, producing fields with the wrong magnitude, position, and rotation. Failure is silent — the simulation runs and produces physically wrong field maps.Proposed fix
Route the double accessor through
gutilities::getG4Number, which already implements unit-expression parsing. Leave the int accessor onstoi(pole counts are unitless). Add thegutilities.hinclude sincegfield.hdoes not currently pull it in.gutilities::getG4Numberis declared ingemc/guts/gutilities.h:280:(Adjust the include path to match how other headers in this module reference
gutilities.h; the bracket form above mirrors the existinggfactory.h/gbase.hincludes ingfield.h.) No change is needed at the call sites ingfield_multipoles.cc.Split out from #102 (one issue per bug). Found via an AI-assisted code review with manual verification against commit
5f8ce875.