Skip to content

Commit

Permalink
feat: adding G4 stepping (#3812)
Browse files Browse the repository at this point in the history
This adds the possibility to use Geant4 as a propagation reference without the need of the SimHitConversion algorithm

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

## Release Notes

- **New Features**
	- Enhanced configurability in `Geant4Simulation` for tracking particle interactions with new parameters for recording hits and propagation summaries.
	- Added support for step logging in `SensitiveSteppingAction` to control logging behavior during simulations.
	- Introduced new member variables in various classes to improve tracking and output capabilities related to particle propagation.

- **Bug Fixes**
	- Improved error handling for missing physical volumes and inconsistencies in physics lists.

- **Refactor**
	- Streamlined methods in `SensitiveSteppingAction` for better clarity and maintainability.
	- Removed the `SimHitToSummaryConversion` algorithm to focus on core propagation functionalities.

- **Documentation**
	- Updated Python bindings to reflect changes in configuration options for simulation and mapping capabilities.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
  • Loading branch information
asalzburger authored Dec 4, 2024
1 parent 0476744 commit af6a7b7
Show file tree
Hide file tree
Showing 10 changed files with 160 additions and 137 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#pragma once

#include "Acts/Material/MaterialInteraction.hpp"
#include "ActsExamples/EventData/PropagationSummary.hpp"
#include "ActsExamples/EventData/SimHit.hpp"
#include "ActsExamples/EventData/SimParticle.hpp"
#include "ActsExamples/Framework/DataHandle.hpp"
Expand Down Expand Up @@ -56,6 +57,9 @@ struct EventStore {
/// Tracks recorded in material mapping
std::unordered_map<std::size_t, Acts::RecordedMaterialTrack> materialTracks;

/// Stepping records for step plotting
std::unordered_map<G4int, PropagationSummary> propagationRecords;

/// Particle hit count (for hit indexing)
std::unordered_map<SimBarcode, std::size_t> particleHitCount;
/// Particle status
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

#include "Acts/Material/MaterialInteraction.hpp"
#include "Acts/Utilities/Logger.hpp"
#include "ActsExamples/EventData/PropagationSummary.hpp"
#include "ActsExamples/EventData/SimHit.hpp"
#include "ActsExamples/EventData/SimParticle.hpp"
#include "ActsExamples/Framework/DataHandle.hpp"
Expand Down Expand Up @@ -123,6 +124,9 @@ class Geant4Simulation final : public Geant4SimulationBase {
/// Name of the output collection : simulated particles
std::string outputParticles = "particles_simulated";

/// Name of the output collection : propagation records (debugging)
std::string outputPropagationSummaries = "propagation_summaries";

/// The ACTS sensitive surfaces in a mapper, used for hit creation
std::shared_ptr<const Geant4::SensitiveSurfaceMapper>
sensitiveSurfaceMapper = nullptr;
Expand All @@ -137,11 +141,17 @@ class Geant4Simulation final : public Geant4SimulationBase {
double killAfterTime = std::numeric_limits<double>::infinity();
bool killSecondaries = false;

bool recordHitsOfCharged = true;

bool recordHitsOfNeutrals = false;

bool recordHitsOfPrimaries = true;

bool recordHitsOfSecondaries = true;

bool keepParticlesWithoutHits = true;

bool recordPropagationSummaries = false;
};

/// Simulation constructor
Expand Down Expand Up @@ -172,6 +182,9 @@ class Geant4Simulation final : public Geant4SimulationBase {
WriteDataHandle<SimParticleContainer> m_outputParticles{this,
"OutputParticles"};
WriteDataHandle<SimHitContainer> m_outputSimHits{this, "OutputSimHIts"};

WriteDataHandle<PropagationSummaries> m_outputPropagationSummaries{
this, "OutputPropagationSummaries"};
};

class Geant4MaterialRecording final : public Geant4SimulationBase {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ class SensitiveSteppingAction : public G4UserSteppingAction {
bool neutral = false;
bool primary = true;
bool secondary = true;

/// step logging mode
bool stepLogging = false;
};

/// Construct the stepping action
Expand Down
19 changes: 17 additions & 2 deletions Examples/Algorithms/Geant4/src/Geant4Simulation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -218,10 +218,11 @@ Geant4Simulation::Geant4Simulation(const Config& cfg,

Geant4::SensitiveSteppingAction::Config stepCfg;
stepCfg.eventStore = m_eventStore;
stepCfg.charged = true;
stepCfg.charged = cfg.recordHitsOfCharged;
stepCfg.neutral = cfg.recordHitsOfNeutrals;
stepCfg.primary = true;
stepCfg.primary = cfg.recordHitsOfPrimaries;
stepCfg.secondary = cfg.recordHitsOfSecondaries;
stepCfg.stepLogging = cfg.recordPropagationSummaries;

Geant4::SteppingActionList::Config steppingCfg;
steppingCfg.actions.push_back(std::make_unique<Geant4::ParticleKillAction>(
Expand Down Expand Up @@ -286,6 +287,10 @@ Geant4Simulation::Geant4Simulation(const Config& cfg,
m_inputParticles.initialize(cfg.inputParticles);
m_outputSimHits.initialize(cfg.outputSimHits);
m_outputParticles.initialize(cfg.outputParticles);

if (cfg.recordPropagationSummaries) {
m_outputPropagationSummaries.initialize(cfg.outputPropagationSummaries);
}
}

Geant4Simulation::~Geant4Simulation() = default;
Expand All @@ -312,6 +317,16 @@ ProcessCode Geant4Simulation::execute(const AlgorithmContext& ctx) const {
ctx, SimHitContainer(eventStore().hits.begin(), eventStore().hits.end()));
#endif

// Output the propagation summaries if requested
if (m_cfg.recordPropagationSummaries) {
PropagationSummaries summaries;
summaries.reserve(eventStore().propagationRecords.size());
for (auto& [trackId, summary] : eventStore().propagationRecords) {
summaries.push_back(std::move(summary));
}
m_outputPropagationSummaries(ctx, std::move(summaries));
}

return ProcessCode::SUCCESS;
}

Expand Down
164 changes: 114 additions & 50 deletions Examples/Algorithms/Geant4/src/SensitiveSteppingAction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@
#include "Acts/Definitions/Algebra.hpp"
#include "Acts/Definitions/Units.hpp"
#include "Acts/Geometry/GeometryIdentifier.hpp"
#include "Acts/Propagator/detail/SteppingLogger.hpp"
#include "Acts/Surfaces/Surface.hpp"
#include "Acts/Utilities/MultiIndex.hpp"
#include "ActsExamples/Geant4/EventStore.hpp"
#include "ActsExamples/Geant4/SensitiveSurfaceMapper.hpp"
#include "ActsFatras/EventData/Barcode.hpp"

#include <algorithm>
#include <array>
#include <cstddef>
#include <string>
#include <unordered_map>
Expand Down Expand Up @@ -50,46 +52,59 @@ BOOST_DESCRIBE_ENUM(G4TrackStatus, fAlive, fStopButAlive, fStopAndKill,

namespace {

ActsFatras::Hit hitFromStep(const G4StepPoint* preStepPoint,
const G4StepPoint* postStepPoint,
ActsFatras::Barcode particleId,
Acts::GeometryIdentifier geoId,
std::int32_t index) {
static constexpr double convertTime = Acts::UnitConstants::s / CLHEP::s;
std::array<Acts::Vector4, 4u> kinematicsOfStep(const G4Step* step) {
static constexpr double convertLength = Acts::UnitConstants::mm / CLHEP::mm;
static constexpr double convertEnergy = Acts::UnitConstants::GeV / CLHEP::GeV;
static constexpr double convertTime = Acts::UnitConstants::ns / CLHEP::ns;

const G4StepPoint* preStepPoint = step->GetPreStepPoint();
const G4StepPoint* postStepPoint = step->GetPostStepPoint();

Acts::Vector4 preStepPosition(convertLength * preStepPoint->GetPosition().x(),
convertLength * preStepPoint->GetPosition().y(),
convertLength * preStepPoint->GetPosition().z(),
convertTime * preStepPoint->GetGlobalTime());
Acts::Vector4 preStepMomentum(convertEnergy * preStepPoint->GetMomentum().x(),
convertEnergy * preStepPoint->GetMomentum().y(),
convertEnergy * preStepPoint->GetMomentum().z(),
convertEnergy * preStepPoint->GetTotalEnergy());
Acts::Vector4 postStepPosition(
convertLength * postStepPoint->GetPosition().x(),
convertLength * postStepPoint->GetPosition().y(),
convertLength * postStepPoint->GetPosition().z(),
convertTime * postStepPoint->GetGlobalTime());
Acts::Vector4 postStepMomentum(
convertEnergy * postStepPoint->GetMomentum().x(),
convertEnergy * postStepPoint->GetMomentum().y(),
convertEnergy * postStepPoint->GetMomentum().z(),
convertEnergy * postStepPoint->GetTotalEnergy());

return {preStepPosition, preStepMomentum, postStepPosition, postStepMomentum};
}

G4ThreeVector preStepPosition = convertLength * preStepPoint->GetPosition();
G4double preStepTime = convertTime * preStepPoint->GetGlobalTime();
G4ThreeVector postStepPosition = convertLength * postStepPoint->GetPosition();
G4double postStepTime = convertTime * postStepPoint->GetGlobalTime();

G4ThreeVector preStepMomentum = convertEnergy * preStepPoint->GetMomentum();
G4double preStepEnergy = convertEnergy * preStepPoint->GetTotalEnergy();
G4ThreeVector postStepMomentum = convertEnergy * postStepPoint->GetMomentum();
G4double postStepEnergy = convertEnergy * postStepPoint->GetTotalEnergy();

double hX = 0.5 * (preStepPosition[0] + postStepPosition[0]);
double hY = 0.5 * (preStepPosition[1] + postStepPosition[1]);
double hZ = 0.5 * (preStepPosition[2] + postStepPosition[2]);
double hT = 0.5 * (preStepTime + postStepTime);

double mXpre = preStepMomentum[0];
double mYpre = preStepMomentum[1];
double mZpre = preStepMomentum[2];
double mEpre = preStepEnergy;
double mXpost = postStepMomentum[0];
double mYpost = postStepMomentum[1];
double mZpost = postStepMomentum[2];
double mEpost = postStepEnergy;

Acts::Vector4 particlePosition(hX, hY, hZ, hT);
Acts::Vector4 beforeMomentum(mXpre, mYpre, mZpre, mEpre);
Acts::Vector4 afterMomentum(mXpost, mYpost, mZpost, mEpost);

return ActsFatras::Hit(geoId, particleId, particlePosition, beforeMomentum,
afterMomentum, index);
ActsFatras::Hit hitFromStep(const G4Step* step, ActsFatras::Barcode particleId,
Acts::GeometryIdentifier geoId,
std::int32_t index) {
auto [preStepPosition, preStepMomentum, postStepPosition, postStepMomentum] =
kinematicsOfStep(step);

return ActsFatras::Hit(geoId, particleId,
0.5 * (preStepPosition + postStepPosition),
preStepMomentum, postStepMomentum, index);
}

Acts::detail::Step stepFromG4Step(const G4Step* step) {
Acts::detail::Step pStep;
auto [preStepPosition, preStepMomentum, postStepPosition, postStepMomentum] =
kinematicsOfStep(step);

pStep.navDir = Acts::Direction::Forward;
pStep.position = 0.5 * (preStepPosition + postStepPosition).block<3, 1>(0, 0);
pStep.momentum = 0.5 * (preStepMomentum + postStepMomentum).block<3, 1>(0, 0);
pStep.nTotalTrials = 1;
return pStep;
}

} // namespace

namespace ActsExamples::Geant4 {
Expand All @@ -101,12 +116,17 @@ SensitiveSteppingAction::SensitiveSteppingAction(
void SensitiveSteppingAction::UserSteppingAction(const G4Step* step) {
// Unit conversions G4->::ACTS
static constexpr double convertLength = Acts::UnitConstants::mm / CLHEP::mm;
static constexpr double convertEnergy = Acts::UnitConstants::GeV / CLHEP::GeV;

// The particle after the step
G4Track* track = step->GetTrack();
G4PrimaryParticle* primaryParticle =
track->GetDynamicParticle()->GetPrimaryParticle();

// Get PreStepPoint and PostStepPoint
const G4StepPoint* preStepPoint = step->GetPreStepPoint();
const G4StepPoint* postStepPoint = step->GetPostStepPoint();

// Bail out if charged & configured to do so
G4double absCharge = std::abs(track->GetParticleDefinition()->GetPDGCharge());
if (!m_cfg.charged && absCharge > 0.) {
Expand Down Expand Up @@ -136,14 +156,11 @@ void SensitiveSteppingAction::UserSteppingAction(const G4Step* step) {
std::string volumeName = volume->GetName();

if (volumeName.find(SensitiveSurfaceMapper::mappingPrefix) ==
std::string_view::npos) {
std::string::npos &&
!m_cfg.stepLogging) {
return;
}

// Get PreStepPoint and PostStepPoint
const G4StepPoint* preStepPoint = step->GetPreStepPoint();
const G4StepPoint* postStepPoint = step->GetPostStepPoint();

// The G4Touchable for the matching
const G4VTouchable* touchable = track->GetTouchable();

Expand All @@ -157,7 +174,8 @@ void SensitiveSteppingAction::UserSteppingAction(const G4Step* step) {
ACTS_VERBOSE("Found " << nSurfaces << " candidate surfaces for volume "
<< volumeName);

if (nSurfaces == 0) {
const Acts::Surface* surface = nullptr;
if (nSurfaces == 0 && !m_cfg.stepLogging) {
ACTS_ERROR("No candidate surfaces found for volume " << volumeName);
return;
} else if (nSurfaces == 1u) {
Expand All @@ -167,7 +185,7 @@ void SensitiveSteppingAction::UserSteppingAction(const G4Step* step) {
// Find the closest surface to the current position
Acts::GeometryContext gctx;
for (; bsf != esf; ++bsf) {
const Acts::Surface* surface = bsf->second;
surface = bsf->second;
const G4ThreeVector& translation = touchable->GetTranslation();
Acts::Vector3 g4VolumePosition(convertLength * translation.x(),
convertLength * translation.y(),
Expand All @@ -185,9 +203,57 @@ void SensitiveSteppingAction::UserSteppingAction(const G4Step* step) {
return;
}

// Output is only strictly valid if step logging is not enabled
const auto particleId = eventStore().trackIdMapping.at(track->GetTrackID());

ACTS_VERBOSE("Step of " << particleId << " in sensitive volume " << geoId);
if (!m_cfg.stepLogging && surface != nullptr) {
ACTS_VERBOSE("Step of " << particleId << " in sensitive volume " << geoId);
} else if (m_cfg.stepLogging) {
if (!eventStore().propagationRecords.contains(track->GetTrackID())) {
// Create the propagation summary
double xVtx = track->GetVertexPosition().x() * convertLength;
double yVtx = track->GetVertexPosition().y() * convertLength;
double zVtx = track->GetVertexPosition().z() * convertLength;
double xDirVtx = track->GetVertexMomentumDirection().x();
double yDirVtx = track->GetVertexMomentumDirection().y();
double zDirVtx = track->GetVertexMomentumDirection().z();
double absMomentum = track->GetMomentum().mag() * convertEnergy;

PropagationSummary iSummary(Acts::CurvilinearTrackParameters(
Acts::Vector4(xVtx, yVtx, zVtx, 0.),
Acts::Vector3(xDirVtx, yDirVtx, zDirVtx), absCharge / absMomentum,
std::nullopt, Acts::ParticleHypothesis::pion()));

eventStore().propagationRecords.insert({track->GetTrackID(), iSummary});
}
PropagationSummary& pSummary =
eventStore().propagationRecords.at(track->GetTrackID());

// Increase the step counter
pSummary.nSteps += 1;

double currentTrackLength = track->GetTrackLength() * convertLength;
double currentStepLength = currentTrackLength - pSummary.pathLength;
pSummary.pathLength = currentTrackLength;

// Create a new step for the step logging
Acts::detail::Step pStep = stepFromG4Step(step);
pStep.geoID = geoId;
pStep.surface = surface != nullptr ? surface->getSharedPtr() : nullptr;
// Check if last step was on same surface
if (!pSummary.steps.empty() && pSummary.steps.back().geoID == geoId &&
pSummary.steps.back().surface != nullptr) {
auto& lastStep = pSummary.steps.back();
lastStep.stepSize = Acts::ConstrainedStep(currentStepLength);
lastStep.position = 0.5 * (pStep.position + lastStep.position);
lastStep.momentum = 0.5 * (pStep.momentum + lastStep.momentum);
} else {
// Record the propagation state
pStep.stepSize = Acts::ConstrainedStep(currentStepLength);
pSummary.steps.emplace_back(std::move(pStep));
}
// You have nothing to do from here
return;
}

// Set particle hit count to zero, so we have this entry in the map later
if (!eventStore().particleHitCount.contains(particleId)) {
Expand Down Expand Up @@ -228,7 +294,7 @@ void SensitiveSteppingAction::UserSteppingAction(const G4Step* step) {
ACTS_VERBOSE("-> merge single step to hit");
++eventStore().particleHitCount[particleId];
eventStore().hits.push_back(
hitFromStep(preStepPoint, postStepPoint, particleId, geoId,
hitFromStep(step, particleId, geoId,
eventStore().particleHitCount.at(particleId) - 1));

eventStore().numberGeantSteps += 1ul;
Expand All @@ -241,8 +307,7 @@ void SensitiveSteppingAction::UserSteppingAction(const G4Step* step) {
if (postOnBoundary || particleStopped || particleDecayed) {
ACTS_VERBOSE("-> merge buffer to hit");
auto& buffer = eventStore().hitBuffer;
buffer.push_back(
hitFromStep(preStepPoint, postStepPoint, particleId, geoId, -1));
buffer.push_back(hitFromStep(step, particleId, geoId, -1));

const auto pos4 =
0.5 * (buffer.front().fourPosition() + buffer.back().fourPosition());
Expand Down Expand Up @@ -270,8 +335,7 @@ void SensitiveSteppingAction::UserSteppingAction(const G4Step* step) {
// hit buffer.
if (!postOnBoundary) {
// ACTS_VERBOSE("-> add hit to buffer");
eventStore().hitBuffer.push_back(
hitFromStep(preStepPoint, postStepPoint, particleId, geoId, -1));
eventStore().hitBuffer.push_back(hitFromStep(step, particleId, geoId, -1));
return;
}

Expand Down
7 changes: 1 addition & 6 deletions Examples/Algorithms/Propagation/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
add_library(
ActsExamplesPropagation
SHARED
src/PropagationAlgorithm.cpp
src/SimHitToSummaryConversion.cpp
)
add_library(ActsExamplesPropagation SHARED src/PropagationAlgorithm.cpp)

target_include_directories(
ActsExamplesPropagation
Expand Down
Loading

0 comments on commit af6a7b7

Please sign in to comment.