Skip to content

Commit

Permalink
feat: particle killing 4 Geant4 and refactor user actions (#2057)
Browse files Browse the repository at this point in the history
Out Geant4 algorithm allows to pass `std::vector`s of user actions. However, multiple actions per type seem not to be supported by Geant4. This changes this to allow only one user action per type (run, event, particle, step).

I'm not sure if this was possible in former Geant4 versions, there is also a comment in the Geant4 source code that indicates it is possible to add multiple actions per type. However, this is not the case.
  • Loading branch information
benjaminhuth committed Apr 27, 2023
1 parent 2a90742 commit 9cff63a
Show file tree
Hide file tree
Showing 11 changed files with 219 additions and 59 deletions.
3 changes: 2 additions & 1 deletion Examples/Algorithms/Geant4/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ set(ACTS_EXAMPLES_G4SOURCES
src/ParticleTrackingAction.cpp
src/SensitiveSurfaceMapper.cpp
src/SensitiveSteppingAction.cpp
src/SimParticleTranslation.cpp)
src/SimParticleTranslation.cpp
src/ParticleKillAction.cpp)

if (ACTS_BUILD_EXAMPLES_DD4HEP)
list(APPEND ACTS_EXAMPLES_G4SOURCES src/DDG4DetectorConstruction.cpp)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// This file is part of the Acts project.
//
// Copyright (C) 2023 CERN for the benefit of the Acts project
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#pragma once

#include "Acts/Utilities/Logger.hpp"
#include "ActsExamples/EventData/SimHit.hpp"
#include "ActsExamples/EventData/SimParticle.hpp"

#include <memory>
#include <optional>
#include <string>

#include <G4UserSteppingAction.hh>

namespace ActsExamples {

/// Geant4 only allows one user action of each type. This simple wrapper
/// dispatches multiple actions to Geant4.
class ActsSteppingActionList : public G4UserSteppingAction {
public:
struct Config {
std::vector<G4UserSteppingAction *> actions;
};

ActsSteppingActionList(const Config &cfg) : m_cfg(cfg) {}

void UserSteppingAction(const G4Step *step) override {
for (auto action : m_cfg.actions) {
if (action) {
action->UserSteppingAction(step);
}
}
}

private:
Config m_cfg;
};

} // namespace ActsExamples
Original file line number Diff line number Diff line change
Expand Up @@ -77,17 +77,17 @@ class Geant4Simulation final : public IAlgorithm {
/// User Action: Primary generator action of the simulation
G4VUserPrimaryGeneratorAction* primaryGeneratorAction = nullptr;

/// User Actions: Run
std::vector<G4UserRunAction*> runActions = {};
/// User Action: Run
G4UserRunAction* runAction = nullptr;

/// User Actions: Event
std::vector<G4UserEventAction*> eventActions = {};
/// User Action: Event
G4UserEventAction* eventAction = nullptr;

/// User Actions: Tracking
std::vector<G4UserTrackingAction*> trackingActions = {};
/// User Action: Tracking
G4UserTrackingAction* trackingAction = nullptr;

/// User Actions: Stepping
std::vector<G4UserSteppingAction*> steppingActions = {};
/// User Action: Stepping
G4UserSteppingAction* steppingAction = nullptr;

/// Detector construction object.
G4VUserDetectorConstruction* detectorConstruction = nullptr;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// This file is part of the Acts project.
//
// Copyright (C) 2023 CERN for the benefit of the Acts project
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#pragma once

#include "Acts/Geometry/Volume.hpp"
#include "Acts/Utilities/Logger.hpp"

#include <memory>
#include <string>

#include <G4UserSteppingAction.hh>

namespace ActsExamples {

/// A G4SteppingAction that is called for every step in
/// the simulation process.
///
/// It checks whether the particle can be killed according when its position
/// exceeds the configured values for |z| or r.
class ParticleKillAction : public G4UserSteppingAction {
public:
/// Configuration of the Stepping action
struct Config {
std::shared_ptr<const Acts::Volume> volume;
};

/// Construct the stepping action
///
/// @param cfg the configuration struct
/// @param logger the ACTS logging instance
ParticleKillAction(const Config& cfg,
std::unique_ptr<const Acts::Logger> logger =
Acts::getDefaultLogger("ParticleKillAction",
Acts::Logging::INFO));
~ParticleKillAction() override = default;

/// @brief Called every step, conditionally sets the tracking state to `fStopAndKill`
/// @param step is the Geant4 step of the particle
void UserSteppingAction(const G4Step* step) override;

private:
const Acts::Logger& logger() const { return *m_logger; }

Config m_cfg;
std::unique_ptr<const Acts::Logger> m_logger;
};

} // namespace ActsExamples
22 changes: 4 additions & 18 deletions Examples/Algorithms/Geant4/src/Geant4Simulation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,6 @@
#include <G4VUserPhysicsList.hh>
#include <G4Version.hh>

namespace {
/// Helper method to add the user actions
/// @tparam manager_t the run manager type
/// @tparam actions_t the actions iterable list
template <typename manager_t, typename actions_t>
void setUserActions(manager_t& manager, actions_t& actions) {
for (const auto& action : actions) {
if (action != nullptr) {
manager.SetUserAction(action);
}
}
}
} // namespace

ActsExamples::Geant4Simulation::Geant4Simulation(
const ActsExamples::Geant4Simulation::Config& config,
Acts::Logging::Level level)
Expand Down Expand Up @@ -111,10 +97,10 @@ ActsExamples::Geant4Simulation::Geant4Simulation(
m_cfg.runManager->SetUserAction(m_cfg.primaryGeneratorAction);

// Set the configured user actions
setUserActions(*m_cfg.runManager, m_cfg.runActions);
setUserActions(*m_cfg.runManager, m_cfg.eventActions);
setUserActions(*m_cfg.runManager, m_cfg.trackingActions);
setUserActions(*m_cfg.runManager, m_cfg.steppingActions);
m_cfg.runManager->SetUserAction(m_cfg.runAction);
m_cfg.runManager->SetUserAction(m_cfg.eventAction);
m_cfg.runManager->SetUserAction(m_cfg.trackingAction);
m_cfg.runManager->SetUserAction(m_cfg.steppingAction);

// Initialize the Geant4 run manager
m_cfg.runManager->Initialize();
Expand Down
38 changes: 38 additions & 0 deletions Examples/Algorithms/Geant4/src/ParticleKillAction.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// This file is part of the Acts project.
//
// Copyright (C) 2021 CERN for the benefit of the Acts project
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include "ActsExamples/Geant4/ParticleKillAction.hpp"

#include "Acts/Definitions/Units.hpp"
#include "ActsExamples/Geant4/EventStoreRegistry.hpp"
#include "ActsExamples/Geant4/SensitiveSurfaceMapper.hpp"

#include <G4RunManager.hh>
#include <G4Step.hh>
#include <G4StepPoint.hh>
#include <G4Track.hh>
#include <G4UnitsTable.hh>
#include <G4VPhysicalVolume.hh>

ActsExamples::ParticleKillAction::ParticleKillAction(
const Config& cfg, std::unique_ptr<const Acts::Logger> logger)
: G4UserSteppingAction(), m_cfg(cfg), m_logger(std::move(logger)) {}

void ActsExamples::ParticleKillAction::UserSteppingAction(const G4Step* step) {
constexpr double convertLength = Acts::UnitConstants::mm / CLHEP::mm;
G4Track* track = step->GetTrack();

const auto pos = convertLength * track->GetPosition();

if (m_cfg.volume and
not m_cfg.volume->inside(Acts::Vector3{pos.x(), pos.y(), pos.z()})) {
ACTS_DEBUG("Kill track with internal track ID " << track->GetTrackID()
<< " at " << pos);
track->SetTrackStatus(G4TrackStatus::fStopAndKill);
}
}
4 changes: 4 additions & 0 deletions Examples/Python/python/acts/examples/simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,7 @@ def addGeant4(
outputDirCsv: Optional[Union[Path, str]] = None,
outputDirRoot: Optional[Union[Path, str]] = None,
logLevel: Optional[acts.logging.Level] = None,
killVolume: Optional[acts.Volume] = None,
) -> None:
"""This function steers the detector simulation using Geant4
Expand All @@ -595,6 +596,8 @@ def addGeant4(
the output folder for the Csv output, None triggers no output
outputDirRoot : Path|str, path, None
the output folder for the Root output, None triggers no output
killVolume: acts.Volume, None
if given, particles are killed when going outside of this volume.
"""

from acts.examples.geant4 import Geant4Simulation, makeGeant4SimulationConfig
Expand Down Expand Up @@ -627,6 +630,7 @@ def addGeant4(
magneticField=field,
volumeMappings=volumeMappings,
materialMappings=materialMappings,
killVolume=killVolume,
)
g4conf.outputSimHits = "simhits"
g4conf.outputParticlesInitial = "particles_initial"
Expand Down
65 changes: 37 additions & 28 deletions Examples/Python/src/Geant4Component.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@
#include "Acts/Plugins/Python/Utilities.hpp"
#include "Acts/Utilities/Logger.hpp"
#include "ActsExamples/Framework/IContextDecorator.hpp"
#include "ActsExamples/Geant4/ActsSteppingActionList.hpp"
#include "ActsExamples/Geant4/GdmlDetectorConstruction.hpp"
#include "ActsExamples/Geant4/Geant4Simulation.hpp"
#include "ActsExamples/Geant4/MagneticFieldWrapper.hpp"
#include "ActsExamples/Geant4/MaterialPhysicsList.hpp"
#include "ActsExamples/Geant4/MaterialSteppingAction.hpp"
#include "ActsExamples/Geant4/ParticleKillAction.hpp"
#include "ActsExamples/Geant4/ParticleTrackingAction.hpp"
#include "ActsExamples/Geant4/SensitiveSteppingAction.hpp"
#include "ActsExamples/Geant4/SensitiveSurfaceMapper.hpp"
Expand Down Expand Up @@ -79,12 +81,12 @@ PYBIND11_MODULE(ActsPythonBindingsGeant4, mod) {
ACTS_PYTHON_DECLARE_ALGORITHM(
Geant4Simulation, mod, "Geant4Simulation", outputSimHits,
outputParticlesInitial, outputParticlesFinal, outputMaterialTracks,
randomNumbers, runManager, primaryGeneratorAction, runActions,
eventActions, trackingActions, steppingActions, detectorConstruction,
magneticField, sensitiveSurfaceMapper);
randomNumbers, runManager, primaryGeneratorAction, runAction, eventAction,
trackingAction, steppingAction, detectorConstruction, magneticField,
sensitiveSurfaceMapper);

auto makeGeant4Config =
[](Acts::Logging::Level& level,
[](const Acts::Logger& logger,
std::shared_ptr<const ActsExamples::RandomNumbers> randomNumbers,
G4VUserDetectorConstruction* detector, G4VUserPhysicsList* physicsList,
const SimParticleTranslation::Config& prCfg)
Expand All @@ -99,7 +101,7 @@ PYBIND11_MODULE(ActsPythonBindingsGeant4, mod) {

// Set the primarty generator
g4Cfg.primaryGeneratorAction = new SimParticleTranslation(
prCfg, Acts::getDefaultLogger("SimParticleTranslation", level));
prCfg, logger.cloneWithSuffix("SimParticleTranslation"));
g4Cfg.detectorConstruction = detector;

return g4Cfg;
Expand All @@ -112,24 +114,25 @@ PYBIND11_MODULE(ActsPythonBindingsGeant4, mod) {
std::shared_ptr<const ActsExamples::RandomNumbers> randomNumbers,
const std::string& inputParticles,
const std::string& outputMaterialTracks) {
auto logger = Acts::getDefaultLogger("Geant4", level);
auto physicsList = new MaterialPhysicsList(
Acts::getDefaultLogger("MaterialPhysicsList", level));
logger->cloneWithSuffix("MaterialPhysicsList"));

// Read the particle from the generator
SimParticleTranslation::Config g4PrCfg;
g4PrCfg.forcedPdgCode = 0;
g4PrCfg.forcedCharge = 0.;
g4PrCfg.forcedMass = 0.;

auto g4Cfg = makeGeant4Config(level, std::move(randomNumbers), detector,
physicsList, g4PrCfg);
auto g4Cfg = makeGeant4Config(*logger, std::move(randomNumbers),
detector, physicsList, g4PrCfg);
g4Cfg.inputParticles = inputParticles;

MaterialSteppingAction::Config mStepCfg;
mStepCfg.excludeMaterials = {"Air", "Vacuum"};
auto steppingAction = new MaterialSteppingAction(
mStepCfg, Acts::getDefaultLogger("MaterialSteppingAction", level));
g4Cfg.steppingActions = {steppingAction};
mStepCfg, logger->cloneWithSuffix("MaterialSteppingAction"));
g4Cfg.steppingAction = steppingAction;

// Set the material tracks at output
g4Cfg.outputMaterialTracks = outputMaterialTracks;
Expand All @@ -142,34 +145,39 @@ PYBIND11_MODULE(ActsPythonBindingsGeant4, mod) {
mod.def(
"makeGeant4SimulationConfig",
[makeGeant4Config](
Acts::Logging::Level& level, G4VUserDetectorConstruction* detector,
Acts::Logging::Level level, G4VUserDetectorConstruction* detector,
std::shared_ptr<const ActsExamples::RandomNumbers> randomNumbers,
const std::string& inputParticles,
const std::shared_ptr<const Acts::TrackingGeometry>& trackingGeometry,
const std::shared_ptr<const Acts::MagneticFieldProvider>&
magneticField,
const std::vector<std::string>& volumeMappings,
const std::vector<std::string>& materialMappings) {
const std::vector<std::string>& materialMappings,
std::shared_ptr<const Acts::Volume> killVolume = nullptr) {
auto logger = Acts::getDefaultLogger("Geant4", level);
auto physicsList = new FTFP_BERT();
auto g4Cfg =
makeGeant4Config(*logger, std::move(randomNumbers), detector,
physicsList, SimParticleTranslation::Config{});
g4Cfg.inputParticles = inputParticles;

// Read the particle from the generator
SimParticleTranslation::Config g4PrCfg;
// Particle action
g4Cfg.trackingAction = new ParticleTrackingAction(
ParticleTrackingAction::Config{},
logger->cloneWithSuffix("ParticleTracking"));

auto g4Cfg = makeGeant4Config(level, std::move(randomNumbers), detector,
physicsList, g4PrCfg);
g4Cfg.inputParticles = inputParticles;
// Stepping actions
ActsSteppingActionList::Config steppingCfg;

steppingCfg.actions.push_back(new SensitiveSteppingAction(
SensitiveSteppingAction::Config{},
logger->cloneWithSuffix("SensitiveStepping")));

ParticleTrackingAction::Config g4TrackCfg;
ParticleTrackingAction* particleAction = new ParticleTrackingAction(
g4TrackCfg,
Acts::getDefaultLogger("ParticleTrackingAction", level));
g4Cfg.trackingActions.push_back(particleAction);
steppingCfg.actions.push_back(
new ParticleKillAction(ParticleKillAction::Config{killVolume},
logger->cloneWithSuffix("Killer")));

SensitiveSteppingAction::Config g4StepCfg;
G4UserSteppingAction* steppingAction = new SensitiveSteppingAction(
g4StepCfg,
Acts::getDefaultLogger("SensitiveSteppingAction", level));
g4Cfg.steppingActions.push_back(steppingAction);
g4Cfg.steppingAction = new ActsSteppingActionList(steppingCfg);

// An ACTS Magnetic field is provided
if (magneticField) {
Expand Down Expand Up @@ -203,7 +211,8 @@ PYBIND11_MODULE(ActsPythonBindingsGeant4, mod) {
"level"_a, "detector"_a, "randomNumbers"_a, "inputParticles"_a,
py::arg("trackingGeometry") = nullptr, py::arg("magneticField") = nullptr,
py::arg("volumeMappings") = std::vector<std::string>{},
py::arg("materialMappings") = std::vector<std::string>{});
py::arg("materialMappings") = std::vector<std::string>{},
py::arg("killVolume") = nullptr);

{
using Detector = ActsExamples::Telescope::TelescopeDetector;
Expand Down

0 comments on commit 9cff63a

Please sign in to comment.