LDMX Software
AuxInfoReader.cxx
1#include "SimCore/Geo/AuxInfoReader.h"
2
3#include <array>
4
5// LDMX
6#include "Framework/Exception/Exception.h"
11
12// Geant4
13#include "G4FieldManager.hh"
14#include "G4GDMLEvaluator.hh"
15#include "G4LogicalVolumeStore.hh"
16#include "G4ProductionCuts.hh"
17#include "G4ProductionCutsTable.hh"
18#include "G4Region.hh"
19#include "G4RegionStore.hh"
20#include "G4SDManager.hh"
21#include "G4SystemOfUnits.hh"
22#include "G4UniformMagField.hh"
23
24// STL
25#include <cstdlib>
26#include <string>
27
28using std::string;
29
30namespace simcore::geo {
31
32AuxInfoReader::AuxInfoReader(G4GDMLParser* theParser,
34 : parser_(theParser), eval_(new G4GDMLEvaluator) {}
35
40
42 const G4GDMLAuxListType* aux_info_list = parser_->GetAuxList();
43 for (const auto& aux_info : *aux_info_list) {
44 G4String aux_type = aux_info.type;
45 G4String aux_val = aux_info.value;
46
47 if (aux_type == "SensDet") {
48 std::cerr
49 << "[ WARN ] : Not defining SensDet in GDML since v1.0 of SimCore. "
50 "See https://github.com/LDMX-Software/SimCore/issues/39"
51 << std::endl;
52 } else if (aux_type == "MagneticField") {
53 createMagneticField(aux_val, aux_info.auxList);
54 } else if (aux_type == "Region") {
55 createRegion(aux_val, aux_info.auxList);
56 } else if (aux_type == "ColorMode") {
57 selectColorMode(aux_val);
58 } else if (aux_type == "VisAttributes") {
59 createVisAttributes(aux_val, aux_info.auxList);
60 } else if (aux_type == "DetectorVersion") {
61 createDetectorHeader(aux_val, aux_info.auxList);
62 }
63 }
64 return;
65}
66
68 const G4LogicalVolumeStore* lvs = G4LogicalVolumeStore::GetInstance();
69 std::vector<G4LogicalVolume*>::const_iterator lvciter;
70 for (lvciter = lvs->begin(); lvciter != lvs->end(); lvciter++) {
71 G4GDMLAuxListType aux_info_list =
72 parser_->GetVolumeAuxiliaryInformation(*lvciter);
73
74 for (const auto& aux_info : aux_info_list) {
75 G4String aux_type = aux_info.type;
76 G4String aux_val = aux_info.value;
77
78 G4LogicalVolume* lv = (*lvciter);
79
80 if (aux_type == "MagneticField") {
81 const G4String& mag_field_name = aux_val;
82 G4MagneticField* mag_field =
84 if (mag_field != nullptr) {
85 auto mgr = new G4FieldManager(mag_field);
86 lv->SetFieldManager(
87 mgr,
88 true /* FIXME: hard-coded to force field manager to daughters */);
89 // G4cout << "Assigned magnetic field " << magFieldName << " to
90 // volume " << lv->GetName() << G4endl;
91 } else {
92 EXCEPTION_RAISE(
93 "MissingInfo",
94 "Unknown MagneticField ref in volume's auxiliary info: " +
95 std::string(mag_field_name.data()));
96 }
97 } else if (aux_type == "Region") {
98 const G4String& region_name = aux_val;
99 G4Region* region = G4RegionStore::GetInstance()->GetRegion(region_name);
100 if (region != nullptr) {
101 region->AddRootLogicalVolume(lv);
102 // G4cout << "Added volume " << lv->GetName() << " to region " <<
103 // regionName << G4endl;
104 } else {
105 EXCEPTION_RAISE("MissingInfo", "Reference region '" +
106 std::string(region_name.data()) +
107 "' was not found!");
108 }
109 } else if (aux_type == "VisAttributes") {
110 // Same as when registering visattributes, must match mode (if in use)
111 auto match = std::find(universal_visattributes_.begin(),
112 universal_visattributes_.end(), aux_val);
113 if (match == universal_visattributes_.end() && color_mode_ != "" &&
114 aux_val.find(color_mode_) == std::string::npos) {
115 continue;
116 }
117 const G4String& vis_name = aux_val;
118 G4VisAttributes* vis_attributes =
120 if (vis_attributes != nullptr) {
121 lv->SetVisAttributes(vis_attributes);
122 // G4cout << "Assigned VisAttributes " << visName << " to volume "
123 // << lv->GetName() << G4endl;
124 } else {
125 EXCEPTION_RAISE("MissingInfo", "Referenced VisAttributes '" +
126 std::string(vis_name.data()) +
127 "' was not found!");
128 }
129 }
130 }
131 }
132}
133
134void AuxInfoReader::createMagneticField(const G4String& magFieldName,
135 const G4GDMLAuxListType* auxInfoList) {
136 // Find type of the mag field.
137 G4String mag_field_type("");
138 for (const auto& aux_info : *auxInfoList) {
139 G4String aux_type = aux_info.type;
140 G4String aux_val = aux_info.value;
141
142 if (aux_type == "MagneticFieldType") {
143 mag_field_type = aux_val;
144 break;
145 }
146 }
147
148 if (mag_field_type == "") {
149 EXCEPTION_RAISE("MissingInfo",
150 "Missing MagFieldType for magnetic field definition.");
151 }
152
153 G4MagneticField* mag_field = nullptr;
154
155 // Create a uniform mag field using the built-in Geant4 type.
156 if (mag_field_type == "G4UniformMagField") {
157 double bx, by, bz;
158 bx = by = bz = 0.;
159 for (const auto& aux_info : *auxInfoList) {
160 G4String aux_type = aux_info.type;
161 G4String aux_val = aux_info.value;
162 G4String aux_unit = aux_info.unit;
163
164 G4String expr = aux_val + "*" + aux_unit;
165 if (aux_type == "bx") {
166 bx = eval_->Evaluate(expr);
167 } else if (aux_type == "by") {
168 by = eval_->Evaluate(expr);
169 } else if (aux_type == "bz") {
170 bz = eval_->Evaluate(expr);
171 }
172 }
173 G4ThreeVector field_components(bx, by, bz);
174 mag_field = new G4UniformMagField(field_components);
175
176 // G4cout << "Created G4UniformMagField " << magFieldName << " with field
177 // components " << fieldComponents << G4endl << G4endl;
178
179 // Create a global 3D field map by reading from a data file.
180 } else if (mag_field_type == "MagneticFieldMap3D") {
181 string file_name;
182 double offset_x{};
183 double offset_y{};
184 double offset_z{};
185
186 for (const auto& aux_info : *auxInfoList) {
187 G4String aux_type = aux_info.type;
188 G4String aux_val = aux_info.value;
189 G4String aux_unit = aux_info.unit;
190
191 G4String expr = aux_val + "*" + aux_unit;
192
193 if (aux_type == "File") {
194 file_name = aux_val;
195 } else if (aux_type == "OffsetX") {
196 offset_x = eval_->Evaluate(expr);
197 } else if (aux_type == "OffsetY") {
198 offset_y = eval_->Evaluate(expr);
199 } else if (aux_type == "OffsetZ") {
200 offset_z = eval_->Evaluate(expr);
201 }
202 }
203
204 if (file_name.size() == 0) {
205 EXCEPTION_RAISE("MissingInfo",
206 "File info with field data was not provided.");
207 }
208
209 // Create new 3D field map.
210 mag_field =
211 new MagneticFieldMap3D(file_name.c_str(), offset_x, offset_y, offset_z);
212
213 // Assign field map as global field.
214 G4FieldManager* field_mgr =
215 G4TransportationManager::GetTransportationManager()->GetFieldManager();
216 if (field_mgr->GetDetectorField() != nullptr) {
217 EXCEPTION_RAISE("MisAssign", "Global mag field was already assigned.");
218 }
219 field_mgr->SetDetectorField(mag_field);
220 field_mgr->CreateChordFinder(mag_field);
221
222 } else {
223 EXCEPTION_RAISE("UnknownType", "Unknown MagFieldType '" +
224 std::string(mag_field_type.data()) +
225 "' in auxiliary info.");
226 }
227
228 MagneticFieldStore::getInstance()->addMagneticField(magFieldName, mag_field);
229}
230
231void AuxInfoReader::createRegion(const G4String& name,
232 const G4GDMLAuxListType* auxInfoList) {
233 bool store_trajectories = true;
234 for (const auto& aux_info : *auxInfoList) {
235 G4String aux_type = aux_info.type;
236 G4String aux_val = aux_info.value;
237
238 if (aux_type == "StoreTrajectories") {
239 if (aux_val == "false") {
240 store_trajectories = false;
241 } else if (aux_val == "true") {
242 store_trajectories = true;
243 }
244 }
245 }
246 G4VUserRegionInformation* region_info =
247 new UserRegionInformation(store_trajectories);
248 // This looks like a memory leak, but isn't. I (Einar) have checked. Geant4
249 // registers the region in the constructor and deletes it at the end.
250 //
251 // Some static analysis tools may struggle with identifying that this one
252 // happens to be fine. The NOLINT comment tells clang-tidy to not bother
253 // within the region
254 //
255 // NOLINTBEGIN
256 auto region = new G4Region(name);
257 region->SetUserInformation(region_info);
258 // To get rid of those pesky G4 warnings
259 region->SetProductionCuts(G4ProductionCutsTable::GetProductionCutsTable()
260 ->GetDefaultProductionCuts());
261}
262// NOLINTEND
263
264void AuxInfoReader::selectColorMode(const G4String& mode) {
265 // Chooses which coloring scheme to use for the visattributes
266 // Currently (March 2026) there are two modes implemented:
267 // Region: Only visattributes with "Region" in the name will be applied
268 // Material: Only visattributes with "Material" in the name will be applied
269 // The Region mode colors all volumes in the same subdetector the same color
270 // For example, all volumes in the HCal will be colored orange
271 // The Material mode colors each volume based on which material it's made from
272 //'Unimportant' materials (like glue) are left uncolored or invisible
273 // To add more modes follow these steps:
274 // 1) Make more visattributes in 'visattributes.gdml'
275 // Your new attribute names must share a common string, like "Region"
276 // 2) Add the shared string as a valid option in this function
277
278 std::vector<std::string> valid_modes = {"Region", "Material"};
279
280 auto match = std::find(valid_modes.begin(), valid_modes.end(), mode);
281
282 if (match == valid_modes.end()) {
283 EXCEPTION_RAISE("InvalidMode", "Color mode setting " + mode +
284 " doesn't match any available modes!");
285 }
286
287 // If the input matches a valid mode (doesn't matter which one), proceed
288 color_mode_ = mode;
289}
290
291void AuxInfoReader::createVisAttributes(const G4String& name,
292 const G4GDMLAuxListType* auxInfoList) {
293 std::array<G4double, 4> rgba = {1., 1., 1., 1.};
294 G4bool visible = true;
295 G4bool dau_invisible = false;
296 G4bool force_wireframe = false;
297 G4bool force_solid = false;
298 G4double line_width = 1.0;
299 G4VisAttributes::LineStyle line_style = G4VisAttributes::unbroken;
300
301 // There are some visattributes which should be accepted regardless of mode
302 universal_visattributes_ = {
303 "InvisibleNoDau", "InvisibleShowDau", "NoDau", "GrayWireFrame",
304 "BlueWireFrame", "BlueSolid", "Invisible", "SpVis"};
305 auto match = std::find(universal_visattributes_.begin(),
306 universal_visattributes_.end(), name);
307
308 // If a visattribute is not universal, a color mode is set, and the
309 // visattribute doesn't match the current mode, don't register it
310 if (match == universal_visattributes_.end() && color_mode_ != "" &&
311 name.find(color_mode_) == std::string::npos) {
312 ldmx_log(debug) << "VisAttribute " << name
313 << " doesn't match current mode. Skipping...";
314 return;
315 // Note that if color mode is not set, the last visattributes
316 // defined for each volume will be the active ones
317 }
318
319 for (const auto& aux_info : *auxInfoList) {
320 G4String aux_type = aux_info.type;
321 G4String aux_val = aux_info.value;
322
323 if (aux_type == "R") {
324 rgba[0] = atof(aux_val.c_str());
325 } else if (aux_type == "G") {
326 rgba[1] = atof(aux_val.c_str());
327 } else if (aux_type == "B") {
328 rgba[2] = atof(aux_val.c_str());
329 } else if (aux_type == "A") {
330 rgba[3] = atof(aux_val.c_str());
331 } else if (aux_type == "Style") {
332 if (aux_val == "wireframe") {
333 force_wireframe = true;
334 } else if (aux_val == "solid") {
335 force_solid = true;
336 }
337 } else if (aux_type == "DaughtersInvisible") {
338 if (aux_val == "true") {
339 dau_invisible = true;
340 } else if (aux_val == "false") {
341 dau_invisible = false;
342 }
343 } else if (aux_type == "Visible") {
344 if (aux_val == "true") {
345 visible = true;
346 } else if (aux_val == "false") {
347 visible = false;
348 }
349 } else if (aux_type == "LineStyle") {
350 if (aux_val == "unbroken") {
351 line_style = G4VisAttributes::unbroken;
352 } else if (aux_val == "dashed") {
353 line_style = G4VisAttributes::dashed;
354 } else if (aux_val == "dotted") {
355 line_style = G4VisAttributes::dotted;
356 }
357 } else if (aux_type == "LineWidth") {
358 line_width = atof(aux_val.c_str());
359 }
360 }
361
362 auto vis_attributes = new G4VisAttributes();
363 vis_attributes->SetColor(rgba[0], rgba[1], rgba[2], rgba[3]);
364 vis_attributes->SetVisibility(visible);
365 vis_attributes->SetDaughtersInvisible(dau_invisible);
366 vis_attributes->SetForceWireframe(force_wireframe);
367 vis_attributes->SetForceSolid(force_solid);
368 vis_attributes->SetLineWidth(line_width);
369 vis_attributes->SetLineStyle(line_style);
370 VisAttributesStore::getInstance()->addVisAttributes(name, vis_attributes);
371
372 // G4cout << "Created VisAttributes " << name << G4endl << (*visAttributes) <<
373 // G4endl << G4endl;
374}
375
376void AuxInfoReader::createDetectorHeader(const G4String& auxValue,
377 const G4GDMLAuxListType* auxInfoList) {
378 int detector_version = atoi(auxValue.c_str());
379
380 std::string detector_name("");
381 std::string author("");
382 std::string description("");
383
384 for (const auto& aux_info : *auxInfoList) {
385 G4String aux_type = aux_info.type;
386 G4String aux_val = aux_info.value;
387
388 if (aux_type == "DetectorName") {
389 detector_name = aux_val;
390 } else if (aux_type == "Author") {
391 author = aux_val;
392 } else if (aux_type == "Description") {
393 description = aux_val;
394 }
395 }
396
397 detector_header_ = new ldmx::DetectorHeader(detector_name, detector_version,
398 description, author);
399
400 /*G4cout << G4endl;
401 G4cout << "Read detector header from userinfo: " << G4endl;
402 G4cout << " DetectorName: " << detector_header_->getName() << G4endl;
403 G4cout << " DetectorVersion: " << detector_header_->getVersion() << G4endl;
404 G4cout << " Author: " << detector_header_->getAuthor() << G4endl;
405 G4cout << " Description: " << detector_header_->getDescription() << G4endl;
406 G4cout << G4endl;*/
407}
408} // namespace simcore::geo
Class for defining a global 3D magnetic field.
Class providing a global store to access magnetic field objects.
Class which provides extra information for a detector region.
Class that provides a global visualization attributes store.
Class encapsulating parameters for configuring a processor.
Definition Parameters.h:29
Defines detector header information.
A 3D B-field map defined as a grid of points with associated B-field values.
static MagneticFieldStore * getInstance()
Get the global instance of the magnetic field store.
G4MagneticField * getMagneticField(const std::string &name)
Get a magnetic field by name.
void addMagneticField(const std::string &name, G4MagneticField *magField)
Add a magnetic field by name.
Defines extra information for a detector region.
G4VisAttributes * getVisAttributes(const std::string &name)
Get vis attributes by name.
void addVisAttributes(const std::string &name, G4VisAttributes *visAttributes)
Register a vis attributes by name.
static VisAttributesStore * getInstance()
Get the global instance of the store.
void createDetectorHeader(const G4String &detectorVersion, const G4GDMLAuxListType *auxInfoList)
Create the detector header from the global auxinfo.
virtual ~AuxInfoReader()
Class destructor.
void assignAuxInfoToVolumes()
Assign auxiliary info to volumes such as sensitive detectors.
void readGlobalAuxInfo()
Read the global auxiliary information from the auxinfo block.
AuxInfoReader(G4GDMLParser *parser, const framework::config::Parameters &ps)
Class constructor.
void createVisAttributes(const G4String &name, const G4GDMLAuxListType *auxInfoList)
Create visualization attributes from GDML data.
std::string color_mode_
Color mode for visattributes.
G4GDMLEvaluator * eval_
The GDML expression evaluator.
G4GDMLParser * parser_
The GDML parser.
ldmx::DetectorHeader * detector_header_
Detector header with name and version.
void createMagneticField(const G4String &name, const G4GDMLAuxListType *auxInfoList)
Create a magnetic field from GDML data.
void createRegion(const G4String &name, const G4GDMLAuxListType *auxInfoList)
Create a detector region from GDML data.
void selectColorMode(const G4String &mode)
Select the 'mode' for coloring in the visualization.