gfields
Loading...
Searching...
No Matches
gmagneto.cc
Go to the documentation of this file.
1// gemc
2#include "gfactory.h"
3
4// gfields
5#include "gmagneto.h"
6#include "gfield_options.h"
7
8// guts
9#include <gemc/guts/gutsConventions.h>
10#include <gemc/guts/gutilities.h>
11
12// CLHEP
13#include <CLHEP/Units/SystemOfUnits.h>
14
15// c++
16#include <algorithm>
17#include <cmath>
18#include <exception>
19
20#include "G4TransportationManager.hh"
21#include "G4PropagatorInField.hh"
22
23namespace {
24
25bool is_unset_field_name(const std::string& name) {
26 return name.empty() || name == guts::SERIALIZED_NULL_TOKEN || name == "not provided";
27}
28
29double configured_max_field_step(const std::shared_ptr<GOptions>& gopts) {
30 if (gopts == nullptr || !gopts->doesOptionExist(gfields::MAX_FIELD_STEP_OPTION)) { return 0.0; }
31 const auto value = gopts->getOptionalScalarString(gfields::MAX_FIELD_STEP_OPTION);
32 return value ? gutilities::getG4Number(*value) : 0.0;
33}
34
35} // namespace
36
37
38GMagneto::GMagneto(const std::shared_ptr<GOptions>& gopts,
39 const std::set<std::string>& required_fields) : GBase(gopts, GMAGNETO_LOGGER) {
40 // Allocate the registries that will hold field objects and their corresponding managers.
41 fields_map = std::make_shared<gFieldMap>();
42 fields_manager = std::make_shared<gFieldMgrMap>();
43
44 // Factory manager responsible for loading plugins and instantiating objects.
45 GManager gFieldManager(gopts);
46
47 // Translate user configuration (options) into concrete field definitions.
48 // TODO: this should be done in gemc instead and passed to gmagneto? could be kept here
49 std::vector<GFieldDefinition> field_definition_array = gfields::get_GFieldDefinition(gopts);
50
51 for (auto& field_definition : field_definition_array) {
52 std::string name = field_definition.name;
53
54 // When a filter is given, load only the fields that are actually used. Fields no volume
55 // references (e.g. reset via -no_field) have their plugin and map skipped entirely.
56 if (!required_fields.empty() && required_fields.find(name) == required_fields.end()) {
57 log->info(1, "Field <", name, "> is not used by any volume: skipping plugin and map load.");
58 continue;
59 }
60
61 log->info(1, field_definition);
62
63 // Only create each named field once; repeated names are ignored by this map check.
64 if (fields_map->find(name) == fields_map->end()) {
65 // Load the plugin, instantiate the field object, and cache it by name.
66 fields_map->emplace(name, gFieldManager.LoadAndRegisterObjectFromLibrary<GField>(
67 field_definition.gfieldPluginName(), gopts));
68
69 // Pass the configuration down to the concrete implementation so it can parse/cache parameters.
70 fields_map->at(name)->load_field_definitions(field_definition);
71
72 // Create and cache the Geant4 field manager responsible for stepping/chord finding.
73 fields_manager->emplace(name, fields_map->at(name)->create_FieldManager());
74 }
75 }
76
77 const double max_field_step = configured_max_field_step(gopts);
78 if (max_field_step > 0.0) {
79 G4TransportationManager::GetTransportationManager()
80 ->GetPropagatorInField()
81 ->SetLargestAcceptableStep(max_field_step);
82 log->info(1, "Maximum acceptable field step set to ", max_field_step / CLHEP::mm, " mm.");
83 }
84}
85
87 const std::shared_ptr<GOptions>& gopts, double& field_polarity,
88 std::shared_ptr<GLogger> caller_log) {
89 if (gopts == nullptr) { return nullptr; }
90
91 if (gopts->doesOptionExist(gfields::NO_FIELD_OPTION)) {
92 const auto no_field_value = gopts->getOptionalScalarString(gfields::NO_FIELD_OPTION);
93 if (no_field_value && *no_field_value == gfields::NO_FIELD_ALL) {
94 if (caller_log != nullptr) {
95 caller_log->info(1, "Global field reset by -", gfields::NO_FIELD_OPTION, "=", gfields::NO_FIELD_ALL,
96 ": direct field probes disabled.");
97 }
98 return nullptr;
99 }
100 }
101
102 if (!gopts->doesOptionExist(gfields::GLOBAL_FIELD_OPTION)) { return nullptr; }
103
104 const auto field_name = gopts->getOptionalScalarString(gfields::GLOBAL_FIELD_OPTION);
105 if (!field_name || is_unset_field_name(*field_name)) { return nullptr; }
106
107 for (const auto& field_definition : gfields::get_GFieldDefinition(gopts)) {
108 if (field_definition.name != *field_name) { continue; }
109
110 const auto torus_scale_it = field_definition.field_parameters.find("torus_scale");
111 if (torus_scale_it != field_definition.field_parameters.end()) {
112 try {
113 field_polarity = std::stod(torus_scale_it->second) < 0.0 ? -1.0 : 1.0;
114 } catch (const std::exception&) {
115 if (caller_log != nullptr) {
116 caller_log->warning("Could not parse torus_scale <", torus_scale_it->second,
117 "> for field polarity; using +1.");
118 }
119 field_polarity = 1.0;
120 }
121 }
122 break;
123 }
124
125 auto magneto = std::make_unique<GMagneto>(gopts, std::set<std::string>{*field_name});
126 if (magneto->isField(*field_name)) {
127 if (caller_log != nullptr) {
128 caller_log->info(1, "Using magnetic field <", *field_name,
129 "> for direct probes with torus polarity ", field_polarity);
130 }
131 return magneto->getField(*field_name);
132 }
133
134 if (caller_log != nullptr) {
135 caller_log->warning("Global field <", *field_name,
136 "> is configured but was not available for direct probes.");
137 }
138 return nullptr;
139}
140
142 const std::shared_ptr<GField>& magnetic_field, const G4ThreeVector& position) {
143 using namespace CLHEP;
144
145 if (magnetic_field == nullptr) { return 0.0; }
146
147 const double point[3] = {position.x(), position.y(), position.z()};
148 double bfield[3] = {0.0, 0.0, 0.0};
149 magnetic_field->GetFieldValue(point, bfield);
150
151 return std::sqrt(bfield[0] * bfield[0] + bfield[1] * bfield[1] + bfield[2] * bfield[2]) / tesla;
152}
153
154std::vector<std::string> GMagneto::getFieldNames() const {
155 std::vector<std::string> names;
156 names.reserve(fields_map->size());
157 for (const auto& [name, field] : *fields_map) { names.push_back(name); }
158 std::sort(names.begin(), names.end());
159 return names;
160}
GBase(const std::shared_ptr< GOptions > &gopt, std::string logger_name="")
std::shared_ptr< GLogger > log
Abstract base class representing a magnetic field.
Definition gfield.h:105
GMagneto(const std::shared_ptr< GOptions > &gopts, const std::set< std::string > &required_fields={})
Construct and initialize the magnetic field registry.
Definition gmagneto.cc:38
static double magnetic_field_magnitude_tesla(const std::shared_ptr< GField > &magnetic_field, const G4ThreeVector &position)
Return the magnetic-field magnitude at a position in Tesla.
Definition gmagneto.cc:141
std::vector< std::string > getFieldNames() const
Return the configured field names.
Definition gmagneto.cc:154
static std::shared_ptr< GField > initialize_magnetic_field(const std::shared_ptr< GOptions > &gopts, double &field_polarity, std::shared_ptr< GLogger > caller_log=nullptr)
Load the configured global magnetic field for code that needs direct field probes.
Definition gmagneto.cc:86
std::shared_ptr< T > LoadAndRegisterObjectFromLibrary(std::string_view name, const std::shared_ptr< GOptions > &gopts)
constexpr const char * GMAGNETO_LOGGER
Definition gfield.h:14
constexpr char NO_FIELD_ALL[]
Special gfields::NO_FIELD_OPTION value that resets all fields, including gfields::GLOBAL_FIELD_OPTION...
constexpr char NO_FIELD_OPTION[]
Command-line option name used to reset (remove) field associations.
constexpr char MAX_FIELD_STEP_OPTION[]
Command-line option name used to set the global maximum acceptable field step.
constexpr char GLOBAL_FIELD_OPTION[]
Command-line option name used to associate a field with the ROOT world volume.
std::vector< GFieldDefinition > get_GFieldDefinition(const std::shared_ptr< GOptions > &gopts)
Build the list of field definitions from the provided options.
double getG4Number(const string &v, bool warnIfNotUnit=false)
constexpr char SERIALIZED_NULL_TOKEN[]