Skip to content

Commit 83d048f

Browse files
asalzburgerEleniXoch
authored andcommitted
feat: Robust surface matching for Geant4 (acts-project#3169)
This PR fixes: acts-project#3165 It first creates a `std::multimap` that relates the `G4VPhysVolume` to the possible `Acts::Surface` objects (there can be more due to the replica mechanism). In the Stepping action, the right one will then be picked. The output performance change in geant_hits is expected. Digitisation configuration file: [odd-digi-geometric-config.json](https://github.com/acts-project/acts/files/15188041/odd-digi-geometric-config.json) Example how to run full simulation with this: ```sh python <path_to_your_acts_source_dir>Examples/Scripts/Python/sim_digi_odd.py --digi-config odd-digi-geometric-config.json --geant4 --gun-multiplicity 10 --gun-pt-range 100 100 -n100 -o g4_mu_100GeV_m10 ``` Options are: ```sh usage: sim_digi_odd.py [-h] [--output OUTPUT] [--events EVENTS] [--skip SKIP] [--edm4hep EDM4HEP] [--geant4] [--ttbar] [--ttbar-pu TTBAR_PU] [--gun-multiplicity GUN_MULTIPLICITY] [--gun-eta-range GUN_ETA_RANGE [GUN_ETA_RANGE ...]] [--gun-pt-range GUN_PT_RANGE [GUN_PT_RANGE ...]] [--rnd-seed RND_SEED] [--digi-config DIGI_CONFIG] Sim-digi chain with the OpenDataDetector options: -h, --help show this help message and exit --output OUTPUT, -o OUTPUT Output directory --events EVENTS, -n EVENTS Number of events --skip SKIP, -s SKIP Number of events --edm4hep EDM4HEP Use edm4hep inputs --geant4 Use Geant4 instead of fatras --ttbar Use Pythia8 (ttbar, pile-up 200) instead of particle gun --ttbar-pu TTBAR_PU Number of pile-up events for ttbar --gun-multiplicity GUN_MULTIPLICITY Multiplicity of the particle gun --gun-eta-range GUN_ETA_RANGE [GUN_ETA_RANGE ...] Eta range of the particle gun --gun-pt-range GUN_PT_RANGE [GUN_PT_RANGE ...] Pt range of the particle gun (GeV) --rnd-seed RND_SEED Random seed --digi-config DIGI_CONFIG Digitization configuration file ```
1 parent a9acd94 commit 83d048f

File tree

7 files changed

+340
-34
lines changed

7 files changed

+340
-34
lines changed

Examples/Algorithms/Geant4/include/ActsExamples/Geant4/SensitiveSteppingAction.hpp

+17
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,13 @@
1616

1717
#include <G4UserSteppingAction.hh>
1818

19+
class G4VPhysicalVolume;
1920
class G4Step;
2021

22+
namespace Acts {
23+
class Surface;
24+
}
25+
2126
namespace ActsExamples {
2227

2328
/// The G4SteppingAction that is called for every step in
@@ -52,6 +57,15 @@ class SensitiveSteppingAction : public G4UserSteppingAction {
5257
/// @param step is the Geant4 step of the particle
5358
void UserSteppingAction(const G4Step* step) override;
5459

60+
/// Set the multimap that correlates G4VPhysicalVolumes to Acts::Surfaces
61+
///
62+
/// @param surfaceMapping the multimap of physical volumes to surfaces
63+
void assignSurfaceMapping(
64+
const std::multimap<const G4VPhysicalVolume*, const Acts::Surface*>&
65+
surfaceMapping) {
66+
m_surfaceMapping = surfaceMapping;
67+
}
68+
5569
protected:
5670
Config m_cfg;
5771

@@ -64,6 +78,9 @@ class SensitiveSteppingAction : public G4UserSteppingAction {
6478

6579
/// The looging instance
6680
std::unique_ptr<const Acts::Logger> m_logger;
81+
82+
std::multimap<const G4VPhysicalVolume*, const Acts::Surface*>
83+
m_surfaceMapping;
6784
};
6885

6986
} // namespace ActsExamples

Examples/Algorithms/Geant4/include/ActsExamples/Geant4/SensitiveSurfaceMapper.hpp

+17-6
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@
1010

1111
#include "Acts/Definitions/Algebra.hpp"
1212
#include "Acts/Geometry/GeometryContext.hpp"
13+
#include "Acts/Geometry/GeometryIdentifier.hpp"
1314
#include "Acts/Geometry/TrackingGeometry.hpp"
1415
#include "Acts/Utilities/GridAccessHelpers.hpp"
1516
#include "Acts/Utilities/Logger.hpp"
1617

18+
#include <map>
1719
#include <memory>
1820
#include <string>
1921
#include <vector>
@@ -67,11 +69,12 @@ struct SensitiveCandidates {
6769
/// elements of the Acts::TrackingGeoemtry w/o map lookup.
6870
class SensitiveSurfaceMapper {
6971
public:
72+
/// This prefix is used to indicate a sensitive volume that is matched
73+
constexpr static std::string_view mappingPrefix = "ActsSensitive#";
74+
7075
using SensitiveCandidates = std::function<std::vector<const Acts::Surface*>(
7176
const Acts::GeometryContext&, const Acts::Vector3&)>;
7277

73-
constexpr static std::string_view mappingPrefix = "ActsGeoID#";
74-
7578
/// Configuration struct for the surface mapper
7679
struct Config {
7780
/// For which G4 material names we try to find a mapping
@@ -84,6 +87,15 @@ class SensitiveSurfaceMapper {
8487
SensitiveCandidates candidateSurfaces;
8588
};
8689

90+
/// State object that coutns the assignments and makes
91+
/// a replica save copy association map
92+
struct State {
93+
/// The map of G4 physical volumes to the mapped surfaces (can be many as
94+
/// there can be replicas)
95+
std::multimap<const G4VPhysicalVolume*, const Acts::Surface*>
96+
g4VolumeToSurfaces;
97+
};
98+
8799
/// Constructor with:
88100
///
89101
/// @param cfg the configuration struct
@@ -98,14 +110,13 @@ class SensitiveSurfaceMapper {
98110
/// hierarchy and applies name remapping to the Physical volumes
99111
/// of the Geant4 geometry.
100112
///
113+
/// @param state is the state object (for caching)
101114
/// @param gctx the geometry context
102115
/// @param g4PhysicalVolume the current physical volume in process
103116
/// @param motherPosition the absolute position of the mother
104-
/// @param sCounter a counter of how many volumes have been remapped
105-
void remapSensitiveNames(const Acts::GeometryContext& gctx,
117+
void remapSensitiveNames(State& state, const Acts::GeometryContext& gctx,
106118
G4VPhysicalVolume* g4PhysicalVolume,
107-
const Acts::Transform3& motherTransform,
108-
int& sCounter) const;
119+
const Acts::Transform3& motherTransform) const;
109120

110121
protected:
111122
/// Configuration object

Examples/Algorithms/Geant4/src/Geant4Simulation.cpp

+13-6
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ ActsExamples::Geant4Simulation::Geant4Simulation(const Config& cfg,
215215
}
216216

217217
// Stepping actions
218+
SensitiveSteppingAction* sensitiveSteppingActionAccess = nullptr;
218219
{
219220
// Clear stepping action if it exists
220221
if (runManager().GetUserSteppingAction() != nullptr) {
@@ -237,8 +238,12 @@ ActsExamples::Geant4Simulation::Geant4Simulation(const Config& cfg,
237238
SteppingActionList::Config steppingCfg;
238239
steppingCfg.actions.push_back(std::make_unique<ParticleKillAction>(
239240
particleKillCfg, m_logger->cloneWithSuffix("Killer")));
240-
steppingCfg.actions.push_back(std::make_unique<SensitiveSteppingAction>(
241-
stepCfg, m_logger->cloneWithSuffix("SensitiveStepping")));
241+
242+
auto sensitiveSteppingAction = std::make_unique<SensitiveSteppingAction>(
243+
stepCfg, m_logger->cloneWithSuffix("SensitiveStepping"));
244+
sensitiveSteppingActionAccess = sensitiveSteppingAction.get();
245+
246+
steppingCfg.actions.push_back(std::move(sensitiveSteppingAction));
242247

243248
// G4RunManager will take care of deletion
244249
auto steppingAction = new SteppingActionList(steppingCfg);
@@ -271,14 +276,16 @@ ActsExamples::Geant4Simulation::Geant4Simulation(const Config& cfg,
271276

272277
// ACTS sensitive surfaces are provided, so hit creation is turned on
273278
if (cfg.sensitiveSurfaceMapper != nullptr) {
279+
SensitiveSurfaceMapper::State sState;
274280
ACTS_INFO(
275281
"Remapping selected volumes from Geant4 to Acts::Surface::GeometryID");
276-
int sCounter = 0;
277282
cfg.sensitiveSurfaceMapper->remapSensitiveNames(
278-
Acts::GeometryContext{}, g4World, Acts::Transform3::Identity(),
279-
sCounter);
283+
sState, Acts::GeometryContext{}, g4World, Acts::Transform3::Identity());
284+
ACTS_INFO("Remapping successful for " << sState.g4VolumeToSurfaces.size()
285+
<< " selected volumes.");
280286

281-
ACTS_INFO("Remapping successful for " << sCounter << " selected volumes.");
287+
sensitiveSteppingActionAccess->assignSurfaceMapping(
288+
sState.g4VolumeToSurfaces);
282289
}
283290

284291
m_inputParticles.initialize(cfg.inputParticles);

Examples/Algorithms/Geant4/src/SensitiveSteppingAction.cpp

+44-11
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "Acts/Definitions/Algebra.hpp"
1212
#include "Acts/Definitions/Units.hpp"
1313
#include "Acts/Geometry/GeometryIdentifier.hpp"
14+
#include "Acts/Surfaces/Surface.hpp"
1415
#include "Acts/Utilities/MultiIndex.hpp"
1516
#include "ActsExamples/EventData/SimHit.hpp"
1617
#include "ActsExamples/Geant4/EventStore.hpp"
@@ -28,6 +29,7 @@
2829
#include <G4Track.hh>
2930
#include <G4UnitsTable.hh>
3031
#include <G4VPhysicalVolume.hh>
32+
#include <G4VTouchable.hh>
3133

3234
class G4PrimaryParticle;
3335

@@ -97,6 +99,7 @@ ActsExamples::SensitiveSteppingAction::SensitiveSteppingAction(
9799
void ActsExamples::SensitiveSteppingAction::UserSteppingAction(
98100
const G4Step* step) {
99101
// Unit conversions G4->::ACTS
102+
static constexpr double convertLength = Acts::UnitConstants::mm / CLHEP::mm;
100103

101104
// The particle after the step
102105
G4Track* track = step->GetTrack();
@@ -125,19 +128,53 @@ void ActsExamples::SensitiveSteppingAction::UserSteppingAction(
125128
}
126129

127130
// Get the physical volume & check if it has the sensitive string name
128-
std::string_view volumeName = track->GetVolume()->GetName();
131+
const G4VPhysicalVolume* volume = track->GetVolume();
132+
std::string volumeName = volume->GetName();
129133

130134
if (volumeName.find(SensitiveSurfaceMapper::mappingPrefix) ==
131135
std::string_view::npos) {
132136
return;
133137
}
134138

135-
// Cast out the GeometryIdentifier
136-
volumeName = volumeName.substr(SensitiveSurfaceMapper::mappingPrefix.size(),
137-
std::string_view::npos);
138-
char* end = nullptr;
139-
const Acts::GeometryIdentifier geoId(
140-
std::strtoul(volumeName.data(), &end, 10));
139+
// Get PreStepPoint and PostStepPoint
140+
const G4StepPoint* preStepPoint = step->GetPreStepPoint();
141+
const G4StepPoint* postStepPoint = step->GetPostStepPoint();
142+
143+
// The G4Touchable for the matching
144+
const G4VTouchable* touchable = track->GetTouchable();
145+
146+
Acts::GeometryIdentifier geoId{};
147+
148+
// Find the range of candidate surfaces for the current position in the
149+
// mapping multimap
150+
auto [bsf, esf] = m_surfaceMapping.equal_range(volume);
151+
std::size_t nSurfaces = std::distance(bsf, esf);
152+
153+
ACTS_VERBOSE("Found " << nSurfaces << " candidate surfaces for volume "
154+
<< volumeName);
155+
156+
if (nSurfaces == 0) {
157+
ACTS_ERROR("No candidate surfaces found for volume " << volumeName);
158+
return;
159+
} else if (nSurfaces == 1u) {
160+
geoId = bsf->second->geometryId();
161+
ACTS_VERBOSE("Unique assignment successful -> to surface " << geoId);
162+
} else {
163+
// Find the closest surface to the current position
164+
Acts::GeometryContext gctx;
165+
for (; bsf != esf; ++bsf) {
166+
const Acts::Surface* surface = bsf->second;
167+
const G4ThreeVector& translation = touchable->GetTranslation();
168+
Acts::Vector3 g4VolumePosition(convertLength * translation.x(),
169+
convertLength * translation.y(),
170+
convertLength * translation.z());
171+
if (surface->center(gctx).isApprox(g4VolumePosition)) {
172+
geoId = surface->geometryId();
173+
break;
174+
}
175+
}
176+
ACTS_VERBOSE("Replica assignment successful -> to surface " << geoId);
177+
}
141178

142179
// This is not the case if we have a particle-ID collision
143180
if (eventStore().trackIdMapping.find(track->GetTrackID()) ==
@@ -149,10 +186,6 @@ void ActsExamples::SensitiveSteppingAction::UserSteppingAction(
149186

150187
ACTS_VERBOSE("Step of " << particleId << " in sensitive volume " << geoId);
151188

152-
// Get PreStepPoint and PostStepPoint
153-
const G4StepPoint* preStepPoint = step->GetPreStepPoint();
154-
const G4StepPoint* postStepPoint = step->GetPostStepPoint();
155-
156189
// Set particle hit count to zero, so we have this entry in the map later
157190
if (eventStore().particleHitCount.find(particleId) ==
158191
eventStore().particleHitCount.end()) {

Examples/Algorithms/Geant4/src/SensitiveSurfaceMapper.cpp

+14-10
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@ ActsExamples::SensitiveSurfaceMapper::SensitiveSurfaceMapper(
2424
: m_cfg(cfg), m_logger(std::move(logger)) {}
2525

2626
void ActsExamples::SensitiveSurfaceMapper::remapSensitiveNames(
27-
const Acts::GeometryContext& gctx, G4VPhysicalVolume* g4PhysicalVolume,
28-
const Acts::Transform3& motherTransform, int& sCounter) const {
27+
State& state, const Acts::GeometryContext& gctx,
28+
G4VPhysicalVolume* g4PhysicalVolume,
29+
const Acts::Transform3& motherTransform) const {
30+
// Make sure the unit conversion is correct
2931
constexpr double convertLength = CLHEP::mm / Acts::UnitConstants::mm;
3032

3133
auto g4LogicalVolume = g4PhysicalVolume->GetLogicalVolume();
@@ -53,8 +55,8 @@ void ActsExamples::SensitiveSurfaceMapper::remapSensitiveNames(
5355
if (G4int nDaughters = g4LogicalVolume->GetNoDaughters(); nDaughters > 0) {
5456
// Step down to all daughters
5557
for (G4int id = 0; id < nDaughters; ++id) {
56-
remapSensitiveNames(gctx, g4LogicalVolume->GetDaughter(id), transform,
57-
sCounter);
58+
remapSensitiveNames(state, gctx, g4LogicalVolume->GetDaughter(id),
59+
transform);
5860
}
5961
return;
6062
}
@@ -86,15 +88,17 @@ void ActsExamples::SensitiveSurfaceMapper::remapSensitiveNames(
8688

8789
// A mapped surface was found, a new name will be set that G4PhysVolume
8890
if (mappedSurface != nullptr) {
89-
++sCounter;
90-
std::string mappedVolumeName(SensitiveSurfaceMapper::mappingPrefix);
91-
mappedVolumeName += std::to_string(mappedSurface->geometryId().value());
9291
ACTS_VERBOSE("Found matching surface " << mappedSurface->geometryId()
9392
<< " at position "
9493
<< g4RelPosition.transpose());
95-
ACTS_VERBOSE("Remap: " << g4PhysicalVolume->GetName() << " -> "
96-
<< mappedVolumeName);
97-
g4PhysicalVolume->SetName(mappedVolumeName.c_str());
94+
// Check if the prefix is not yet assigned
95+
if (volumeName.find(mappingPrefix) == std::string::npos) {
96+
// Set the new name
97+
std::string mappedName = std::string(mappingPrefix) + volumeName;
98+
g4PhysicalVolume->SetName(mappedName);
99+
}
100+
// Insert into the multi-map
101+
state.g4VolumeToSurfaces.insert({g4PhysicalVolume, mappedSurface});
98102
} else {
99103
ACTS_VERBOSE("No mapping found for '"
100104
<< volumeName << "' with material '" << volumeMaterialName

Examples/Python/tests/root_file_hashes.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ test_pythia8__pythia8_particles.root: 49b89c3458a51aa9407f887be50e6bbcd9a2a0c897
22
test_fatras__particles_simulation.root: bc970873fef0c2efd86ed5413623802353d2cd04abea72de14e8cdfc0e40076f
33
test_fatras__hits.root: 821d9f3ce364bfea6c08df86b0204d7257cc1810f5b0bdfda45950e697ef5c52
44
test_geant4__particles_simulation.root: a51f15555abefec7e9d9de31f9d856a3026af1b991db73fefaec5ae160a4af2f
5-
test_geant4__hits.root: 794fbae710b8ec68089efb53c5722025bd2cfd961f2f1dc7102c55e65e75dc5f
5+
test_geant4__hits.root: adf5dcdf000a580412dc5089e17460897d6535c978eafa021584ba4281d0a1ac
66
test_seeding__estimatedparams.root: 1fa2e879142059344e32ab62e3763dce14b5138b591c6e919073cb985782075f
77
test_seeding__performance_seeding.root: 992f9c611d30dde0d3f3ab676bab19ada61ab6a4442828e27b65ec5e5b7a2880
88
test_seeding__particles.root: 7855b021f39ad238bca098e4282667be0666f2d1630e5bcb9d51d3b5ee39fa14

0 commit comments

Comments
 (0)