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