BDSIM
BDSIM is a Geant4 extension toolkit for simulation of particle transport in accelerator beamlines.
BDSSDManager.cc
1/*
2Beam Delivery Simulation (BDSIM) Copyright (C) Royal Holloway,
3University of London 2001 - 2022.
4
5This file is part of BDSIM.
6
7BDSIM is free software: you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published
9by the Free Software Foundation version 3 of the License.
10
11BDSIM is distributed in the hope that it will be useful, but
12WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with BDSIM. If not, see <http://www.gnu.org/licenses/>.
18*/
19#include "BDSDebug.hh"
20#include "BDSException.hh"
21#include "BDSGlobalConstants.hh"
22#include "BDSMultiSensitiveDetectorOrdered.hh"
23#include "BDSSensitiveDetector.hh" // for inheritance
24#include "BDSSDApertureImpacts.hh"
25#include "BDSSDCollimator.hh"
26#include "BDSSDEnergyDeposition.hh"
27#include "BDSSDEnergyDepositionGlobal.hh"
28#include "BDSSDFilterIon.hh"
29#include "BDSSDFilterOr.hh"
30#include "BDSSDFilterPDGIDSet.hh"
31#include "BDSSDFilterPrimary.hh"
32#include "BDSSDManager.hh"
33#include "BDSSDSampler.hh"
34#include "BDSSDSamplerCylinder.hh"
35#include "BDSSDSamplerSphere.hh"
36#include "BDSSDSamplerLink.hh"
37#include "BDSSDThinThing.hh"
38#include "BDSSDType.hh"
39#include "BDSSDTerminator.hh"
40#include "BDSSDVolumeExit.hh"
41
42#include "G4SDKineticEnergyFilter.hh"
43#include "G4SDManager.hh"
44#include "G4Version.hh"
45
46#if G4VERSION_NUMBER > 1029
47#include "G4MultiSensitiveDetector.hh"
48#endif
49
50#include <map>
51#include <string>
52#include <vector>
53
54class BDSLinkRegistry;
55
56BDSSDManager* BDSSDManager::instance = nullptr;
57
58BDSSDManager* BDSSDManager::Instance()
59{
60 if (!instance)
61 {instance = new BDSSDManager();}
62 return instance;
63}
64
65BDSSDManager::~BDSSDManager()
66{
67 // no need to delete SDs as they are all registered in G4SDManager
68 instance = nullptr;
69
70 for (auto& kv : filters)
71 {delete kv.second;}
72 for (auto& kv : extraSamplerFilters)
73 {delete kv.second;}
74}
75
77{
79 storeCollimatorHitsAll = g->StoreCollimatorHitsAll();
80 storeCollimatorHitsIons = g->StoreCollimatorHitsIons();
81 collimatorHitsMinimumKE = g->CollimatorHitsMinimumKE();
82 generateApertureImpacts = g->StoreApertureImpacts() || g->StoreApertureImpactsHistograms();
83 storeApertureImpactsAll = g->StoreApertureImpactsAll();
84 storeApertureImpactsIons = g->StoreApertureImpactsIons();
85 apertureImpactsMinimumKE = g->ApertureImpactsMinimumKE();
86 generateELossHits = g->StoreELoss() || g->StoreELossHistograms();
87 generateELossVacuumHits = g->StoreELossVacuum() || g->StoreELossVacuumHistograms(); generateELossTunnelHits = g->StoreELossTunnel() || g->StoreELossTunnelHistograms();
88
89 G4bool killedParticleMassAddedToEloss = g->KilledParticlesMassAddedToEloss();
90
91 generateELossWorldContents = g->UseImportanceSampling() || g->StoreELossWorldContents();
92
93 storeELossWorld = g->StoreELossWorld();
94 storeELossExtras = g->StoreELossTurn()
95 || g->StoreELossLinks()
96 || g->StoreELossLocal()
97 || g->StoreELossGlobal()
98 || g->StoreELossTime()
99 || g->StoreELossStepLength()
100 || g->StoreELossPreStepKineticEnergy()
101 || g->StoreELossModelID()
102 || g->StoreTrajectory() // if we store trajectories, we need the edep track id
103 || g->StoreELossPhysicsProcesses();
106 || g->StoreCollimatorInfo()
107 || g->StoreCollimatorHits()
108 || g->StoreCollimatorHitsLinks();
109
110 filters["primary"] = new BDSSDFilterPrimary("primary");
111 filters["ion"] = new BDSSDFilterIon("ion");
112 BDSSDFilterOr* primaryOrIon = new BDSSDFilterOr("primary_or_ion");
113 primaryOrIon->RegisterFilter(filters["primary"]);
114 primaryOrIon->RegisterFilter(filters["ion"]);
115 filters["primary_or_ion"] = primaryOrIon;
116
117 // aperture impact specifc filters
118 filters["aper_min_ke"] = new G4SDKineticEnergyFilter("aper_min_ke", apertureImpactsMinimumKE);
119 BDSSDFilterOr* primaryIonAperMinKE = new BDSSDFilterOr("primary_or_ion_aper_min_ke");
120 primaryIonAperMinKE->RegisterFilter(filters["primary_or_ion"]);
121 primaryIonAperMinKE->RegisterFilter(filters["aper_min_ke"]);
122 filters["primary_or_ion_aper_min_ke"] = primaryIonAperMinKE;
123 BDSSDFilterOr* primaryAperMinKE = new BDSSDFilterOr("primary_aper_min_ke");
124 primaryAperMinKE->RegisterFilter(filters["primary"]);
125 primaryAperMinKE->RegisterFilter(filters["aper_min_ke"]);
126
127 // collimator impact specific filters
128 filters["coll_min_ke"] = new G4SDKineticEnergyFilter("coll_min_ke", collimatorHitsMinimumKE);
129 BDSSDFilterOr* primaryIonCollMinKE = new BDSSDFilterOr("primary_or_ion_coll_min_ke");
130 primaryIonCollMinKE->RegisterFilter(filters["primary_or_ion"]);
131 primaryIonCollMinKE->RegisterFilter(filters["coll_min_ke"]);
132 filters["primary_or_ion_coll_min_ke"] = primaryIonCollMinKE;
133 BDSSDFilterOr* primaryCollMinKE = new BDSSDFilterOr("primary_coll_min_ke");
134 primaryCollMinKE->RegisterFilter(filters["primary"]);
135 primaryCollMinKE->RegisterFilter(filters["coll_min_ke"]);
136
137 G4SDManager* SDMan = G4SDManager::GetSDMpointer();
138
139 samplerPlane = new BDSSDSampler("plane");
140 SDMan->AddNewDetector(samplerPlane);
141
142 samplerCylinder = new BDSSDSamplerCylinder("cylinder");
143 SDMan->AddNewDetector(samplerCylinder);
144
145 samplerSphere = new BDSSDSamplerSphere("sphere");
146 SDMan->AddNewDetector(samplerSphere);
147
148 samplerLink = new BDSSDSamplerLink("link");
149 SDMan->AddNewDetector(samplerLink);
150
151 // Terminator sd to measure how many times that primary has passed through the terminator
152 terminator = new BDSSDTerminator("terminator");
153 SDMan->AddNewDetector(terminator);
154
155 energyDeposition = new BDSSDEnergyDeposition("general", storeELossExtras, killedParticleMassAddedToEloss);
156 SDMan->AddNewDetector(energyDeposition);
157
158 energyDepositionFull = new BDSSDEnergyDeposition("general_full", true, killedParticleMassAddedToEloss);
159 SDMan->AddNewDetector(energyDepositionFull);
160
161 energyDepositionVacuum = new BDSSDEnergyDeposition("vacuum", storeELossExtras, killedParticleMassAddedToEloss);
162 SDMan->AddNewDetector(energyDepositionVacuum);
163
164 energyDepositionTunnel = new BDSSDEnergyDeposition("tunnel", storeELossExtras, killedParticleMassAddedToEloss);
165 SDMan->AddNewDetector(energyDepositionTunnel);
166
167 energyDepositionWorld = new BDSSDEnergyDepositionGlobal("worldLoss", killedParticleMassAddedToEloss);
168 SDMan->AddNewDetector(energyDepositionWorld);
169
170 energyDepositionWorldContents = new BDSSDEnergyDepositionGlobal("worldLoss_contents", killedParticleMassAddedToEloss);
171 SDMan->AddNewDetector(energyDepositionWorldContents);
172
173 worldExit = new BDSSDVolumeExit("worldExit", true);
174 SDMan->AddNewDetector(worldExit);
175
176 apertureImpacts = new BDSSDApertureImpacts("aperture");
177 // set up a filter for the collimator sensitive detector - always store primary hits
178 G4VSDFilter* filterA = nullptr;
179 G4bool applyApertureImpactsKEFilter = apertureImpactsMinimumKE > 0;
180 if (storeApertureImpactsAll && applyApertureImpactsKEFilter)
181 {filterA = filters["aper_min_ke"];}
183 {filterA = nullptr;} // no filter -> store all
184 else if (storeApertureImpactsIons && applyApertureImpactsKEFilter) // primaries plus ion fragments
185 {filterA = filters["primary_or_ion_aper_min_ke"];}
187 {filterA = filters["primary_or_ion"];}
188 else if (applyApertureImpactsKEFilter)
189 {filterA = filters["primary_aper_min_ke"];} // primaries + KE filter
190 else
191 {filterA = filters["primary"];} // just primaries
192 apertureImpacts->SetFilter(filterA);
193 SDMan->AddNewDetector(apertureImpacts);
194
195#if G4VERSION_NUMBER > 1029
196 // only multiple SDs since 10.3
197 G4MultiSensitiveDetector* wcsd = new G4MultiSensitiveDetector("world_complete");
198 SDMan->AddNewDetector(wcsd);
199 wcsd->AddSD(energyDepositionWorld);
200 wcsd->AddSD(worldExit);
201 worldCompleteSD = wcsd;
202
203 G4MultiSensitiveDetector* acsd = new G4MultiSensitiveDetector("aperture_complete");
204 SDMan->AddNewDetector(acsd);
205 acsd->AddSD(energyDeposition);
206 acsd->AddSD(apertureImpacts);
207 apertureCompleteSD = acsd;
208#endif
209
210 collimatorSD = new BDSSDCollimator("collimator");
211 collimatorCompleteSD = new BDSMultiSensitiveDetectorOrdered("collimator_complete");
212 collimatorCompleteSD->AddSD(energyDepositionFull); // always generate full edep hits
213 collimatorCompleteSD->AddSD(collimatorSD);
214 // set up a filter for the collimator sensitive detector - always store primary hits
215 G4VSDFilter* filter = nullptr;
216 G4bool applyCollimatorHitsKEFilter = collimatorHitsMinimumKE > 0;
217 if (storeCollimatorHitsAll && applyCollimatorHitsKEFilter)
218 {filter = filters["coll_min_ke"];}
219 else if (storeCollimatorHitsAll)
220 {filter = nullptr;} // no filter -> store all
221 else if (storeCollimatorHitsIons && applyCollimatorHitsKEFilter) // primaries plus ion fragments
222 {filter = filters["primary_or_ion_coll_min_ke"];}
224 {filter = filters["primary_or_ion"];}
225 else if (applyCollimatorHitsKEFilter)
226 {filter = filters["primary_coll_min_ke"];} // primaries + KE filter
227 else
228 {filter = filters["primary"];} // just primaries
229
230 // we only want to attach the filter to the collimator SD and not the energy counter SD
231 // via the 'complete' SD. i.e. we always want energy deposition but collimator hits by
232 // the filter.
233 collimatorSD->SetFilter(filter);
234 SDMan->AddNewDetector(collimatorSD);
235 SDMan->AddNewDetector(collimatorCompleteSD);
236
237 // thin things
238 thinThingSD = new BDSSDThinThing("thinthing_general", g->StoreTrajectoryOptions());
239 thinThingSD->SetFilter(filters["primary"]);
240 SDMan->AddNewDetector(thinThingSD);
241
242 // wire scanner wires SD
243 wireCompleteSD = new BDSMultiSensitiveDetectorOrdered("wire_complete");
244 wireCompleteSD->AddSD(energyDepositionFull);
245 wireCompleteSD->AddSD(thinThingSD);
246}
247
248G4VSensitiveDetector* BDSSDManager::SensitiveDetector(const BDSSDType sdType,
249 G4bool applyOptions) const
250{
251 G4VSensitiveDetector* result = nullptr;
252 switch (sdType.underlying())
253 {
254 case BDSSDType::samplerplane:
255 {result = samplerPlane; break;}
256 case BDSSDType::samplercylinder:
257 {result = samplerCylinder; break;}
258 case BDSSDType::samplerlink:
259 {result = samplerLink; break;}
260 case BDSSDType::terminator:
261 {result = terminator; break;}
262 case BDSSDType::energydep:
263 {
264 if (applyOptions)
265 {result = generateELossHits ? energyDeposition : nullptr;}
266 else
267 {result = energyDeposition;}
268 break;
269 }
270 case BDSSDType::energydepvacuum:
271 {
272 if (applyOptions)
273 {result = generateELossVacuumHits ? energyDepositionVacuum : nullptr;}
274 else
275 {result = energyDepositionVacuum;}
276 break;
277 }
278 case BDSSDType::energydeptunnel:
279 {
280 if (applyOptions)
281 {result = generateELossTunnelHits ? energyDepositionTunnel : nullptr;}
282 else
283 {result = energyDepositionTunnel;}
284 break;
285 }
286 case BDSSDType::energydepworld:
287 {
288 if (applyOptions)
289 {result = storeELossWorld ? energyDepositionWorld : nullptr;}
290 else
291 {result = energyDepositionWorld;}
292 break;
293 }
294 case BDSSDType::worldexit:
295 {result = worldExit; break;}
296 case BDSSDType::worldcomplete:
297#if G4VERSION_NUMBER > 1029
298 {result = worldCompleteSD; break;}
299#else
300 {result = nullptr; break;}
301#endif
302 case BDSSDType::energydepworldcontents:
303 {
304 if (applyOptions)
306 else
308 break;
309 }
310 case BDSSDType::collimator:
311 {result = collimatorSD; break;}
312 case BDSSDType::collimatorcomplete:
313 {
314 if (applyOptions)
315 {// if we're not going to store collimator specific information, just use
316 // regular energy deposition hits to save memory (collimator hits require
317 // full energy deposition hits, plus use extra memory themselves).
319 {result = collimatorCompleteSD;}
320 else
321 {result = energyDeposition;}
322 }
323 else
324 {result = collimatorCompleteSD;}
325 break;
326 }
327 case BDSSDType::apertureimpacts:
328 {
329 if (applyOptions)
330 {result = generateApertureImpacts ? apertureImpacts : nullptr;}
331 else
332 {result = apertureImpacts;}
333 break;
334 }
335 case BDSSDType::aperturecomplete:
336 {
337#if G4VERSION_NUMBER > 1029
338 if (applyOptions)
339 {
341 {result = apertureCompleteSD;}
343 {result = apertureImpacts;}
344 }
345 else
346 {result = apertureCompleteSD;}
347 break;
348#else
349 if (applyOptions)
350 {result = generateApertureImpacts ? apertureImpacts : nullptr;}
351 else
352 {result = apertureImpacts;}
353 break;
354#endif
355 }
356 case BDSSDType::thinthing:
357 {result = thinThingSD; break;}
358 case BDSSDType::wirecomplete:
359 {
360 if (applyOptions)
361 {
362 result = generateELossHits
363 ? static_cast<G4VSensitiveDetector*>(wireCompleteSD)
364 : static_cast<G4VSensitiveDetector*>(thinThingSD);
365 }
366 else
367 {result = wireCompleteSD;}
368 break;
369 }
370 default:
371 {result = nullptr; break;}
372 }
373 return result;
374}
375
377 G4double unit)
378{
379 primitiveScorerNamesComplete.emplace_back(nameIn);
380
381 G4String primitivePartOnly = nameIn; // copy of full name
382 auto search = nameIn.rfind("/");
383 if (search != std::string::npos)
384 {primitivePartOnly = nameIn.substr(search+1);}
385 primitiveScorerNames.push_back(primitivePartOnly);
386 primitiveScorerNameToUnit[primitivePartOnly] = unit;
387}
388
389void BDSSDManager::RegisterPrimitiveScorerNames(const std::vector<G4String>& namesIn,
390 const std::vector<G4double>* units)
391{
392 if (units)
393 {
394 if (units->size() != namesIn.size())
395 {throw BDSException(__METHOD_NAME__, "mismatching size of names and units.");}
396 for (G4int i = 0; i < (G4int)namesIn.size(); i++)
397 {RegisterPrimitiveScorerName(namesIn[i], (*units)[i]);}
398 }
399 else
400 {
401 for (const auto& name : namesIn)
403 }
404}
405
407{
408 if (samplerLink)
409 {samplerLink->SetLinkRegistry(registry);}
410}
411
412void BDSSDManager::ConstructSamplerSDsForParticleSets(const std::map<int, std::set<int>>& samplerFilterIDtoPDGSetIn)
413{
414 G4SDManager* SDMan = G4SDManager::GetSDMpointer();
415
416 // we construct one of each sampler type per filter set as we don't know
417 // from the parser how they'll be used. very small overhead.
418
419 // plane sampler SDs
420 for (const auto& IDAndSet : samplerFilterIDtoPDGSetIn)
421 {
422 G4String samplerName = "plane_with_PDG_set_" + std::to_string(IDAndSet.first);
423 auto aSampler = new BDSSDSampler(samplerName);
424 extraSamplerWithFilterNamesComplete.emplace_back(samplerName);
425 auto filter = new BDSSDFilterPDGIDSet("pdgid_set_number_"+std::to_string(IDAndSet.first), IDAndSet.second);
426 aSampler->SetFilter(filter);
427 extraSamplersWithFilters[(G4int)IDAndSet.first] = aSampler;
428 extraSamplerFilters[(G4int)IDAndSet.first] = filter; // keep a map of them for deleting later
429 SDMan->AddNewDetector(aSampler);
430 }
431 // cylindrical sampler SDs
432 for (const auto& IDAndSet : samplerFilterIDtoPDGSetIn)
433 {
434 G4String samplerName = "cylinder_with_PDG_set_" + std::to_string(IDAndSet.first);
435 auto aSampler = new BDSSDSamplerCylinder(samplerName);
436 extraSamplerCylinderWithFilterNamesComplete.emplace_back(samplerName);
437 // filter already exists - reuse
438 aSampler->SetFilter(extraSamplerFilters[(G4int)IDAndSet.first] );
439 extraSamplerCylindersWithFilters[(G4int)IDAndSet.first] = aSampler;
440 SDMan->AddNewDetector(aSampler);
441 }
442 // spherical sampler SDs
443 for (const auto& IDAndSet : samplerFilterIDtoPDGSetIn)
444 {
445 G4String samplerName = "sphere_with_PDG_set_" + std::to_string(IDAndSet.first);
446 auto aSampler = new BDSSDSamplerSphere(samplerName);
447 extraSamplerSphereWithFilterNamesComplete.emplace_back(samplerName);
448 // filter already exists - reuse
449 aSampler->SetFilter(extraSamplerFilters[(G4int)IDAndSet.first] );
450 extraSamplerSpheresWithFilters[(G4int)IDAndSet.first] = aSampler;
451 SDMan->AddNewDetector(aSampler);
452 }
453}
454
456{
457 auto search = extraSamplersWithFilters.find(ID);
458 return search != extraSamplersWithFilters.end() ? search->second : nullptr;
459}
460
462{
463 auto search = extraSamplerCylindersWithFilters.find(ID);
464 return search != extraSamplerCylindersWithFilters.end() ? search->second : nullptr;
465}
466
468{
469 auto search = extraSamplerSpheresWithFilters.find(ID);
470 return search != extraSamplerSpheresWithFilters.end() ? search->second : nullptr;
471}
General exception with possible name of object and message.
Definition: BDSException.hh:35
A class that holds global options and constants.
static BDSGlobalConstants * Instance()
Access method.
BDS::TrajectoryOptions StoreTrajectoryOptions() const
options that require some implementation.
G4bool UseImportanceSampling() const
Is importance sampling being used.
Registry / map of components for tracker linkage.
Modified G4MultiSensitiveDetector that retains order and passes hits in sequence.
Generates BDSHitsEnergyDepositions from step information - uses curvilinear coords.
The sensitive detector class that provides sensitivity to collimators instances.
Generates BDSHitsEnergyDepositionGlobal from step information.
Generates BDSHitsEnergyDepositions from step information - uses curvilinear coords.
Filter for only ions.
Filter that applies OR to a vector of filters.
void RegisterFilter(G4VSDFilter *filterIn)
Register the filter.
Filter for a set of PDG IDs (ints).
Filter for only primary particles.
A singleton class that holds all required sensitive detector class instances.
Definition: BDSSDManager.hh:68
G4double collimatorHitsMinimumKE
Cache of global constant option.
BDSSDManager()
Private default constructor for singleton.
Definition: BDSSDManager.cc:76
BDSSDSamplerSphere * samplerSphere
SD instance.
G4bool storeCollimatorHitsAll
Cache of global constant option.
std::map< G4String, G4VSDFilter * > filters
Map of all filters used. This class owns a single instance of each.
BDSSDTerminator * terminator
SD instance.
BDSSDSamplerCylinder * SamplerCylinderWithFilter(G4int ID) const
Access the relevant SD for a given particle filter set ID. It will return nullptr if the ID is invali...
G4bool generateCollimatorHits
Cache of global constant option.
BDSSDVolumeExit * worldExit
SD instance.
G4bool generateELossVacuumHits
Cache of global constant option.
BDSSDEnergyDepositionGlobal * energyDepositionWorldContents
SD instance.
void RegisterPrimitiveScorerName(const G4String &nameIn, G4double unit=1.0)
BDSSDApertureImpacts * apertureImpacts
SD instance.
void ConstructSamplerSDsForParticleSets(const std::map< int, std::set< int > > &samplerFilterIDtoPDGSetIn)
G4bool generateELossWorldContents
Cache of global constant option.
G4bool generateELossTunnelHits
Cache of global constant option.
BDSSDSamplerLink * samplerLink
SD instance.
G4bool storeELossWorld
Cache of global constant option.
BDSSDSamplerSphere * SamplerSphereWithFilter(G4int ID) const
Access the relevant SD for a given particle filter set ID. It will return nullptr if the ID is invali...
BDSSDEnergyDeposition * energyDeposition
SD instance.
void RegisterPrimitiveScorerNames(const std::vector< G4String > &namesIn, const std::vector< G4double > *units=nullptr)
Loop over a vector and apply above single name function.
G4VSensitiveDetector * worldCompleteSD
SD instance.
G4bool storeApertureImpactsIons
Cache of global constant option.
G4VSensitiveDetector * apertureCompleteSD
SD instance.
G4bool storeApertureImpactsAll
Cache of global constant option.
BDSSDEnergyDeposition * energyDepositionTunnel
SD instance.
BDSSDSampler * samplerPlane
SD instance.
G4bool generateELossHits
Cache of global constant option.
G4VSensitiveDetector * SensitiveDetector(const BDSSDType sdType, G4bool applyOptions=false) const
BDSSDSamplerCylinder * samplerCylinder
SD instance.
std::map< G4String, G4double > primitiveScorerNameToUnit
Map of primitive scorer names to units.
BDSSDSampler * SamplerPlaneWithFilter(G4int ID) const
Access the relevant SD for a given particle filter set ID. It will return nullptr if the ID is invali...
G4bool generateApertureImpacts
Cache of global constant option.
G4bool storeCollimatorHitsIons
Cache of global constant option.
BDSSDEnergyDeposition * energyDepositionFull
SD instance.
G4double apertureImpactsMinimumKE
Cache of global constant option.
std::vector< G4String > primitiveScorerNamesComplete
Vector of complete (including mesh name) primitive scorer names.
G4bool storeELossExtras
Cache of global constant option.
std::vector< G4String > primitiveScorerNames
Just the primitive scorer part of the name.
void SetLinkRegistry(BDSLinkRegistry *registry)
If samplerLink member exists, set the registry to look up links for that SD.
BDSSDEnergyDepositionGlobal * energyDepositionWorld
SD instance.
BDSSDEnergyDeposition * energyDepositionVacuum
SD instance.
The sensitive detector class that provides sensitivity to BDSSamplerCylinder instances.
The sensitive detector class that provides sensitivity to BDSSamplerSphere instances.
The sensitive detector class that provides sensitivity to BDSSampler instances.
Definition: BDSSDSampler.hh:44
Sensitivity that measures primary particle turns for terminator.
The sensitive detector class that provides sensitivity to record thin thing hits.
Generates BDSHitEnergyDepositionGlobals if a particle is leaving a volume.
Improve type-safety of native enum data type in C++.
type underlying() const
return underlying value (can be used in switch statement)