Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Common/SimConfig/include/SimConfig/G4Params.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ struct G4Params : public o2::conf::ConfigurableParamHelper<G4Params> {

bool g4scoring = false;
bool g4fluenceweight = false;

// Fast simulation. Empty fastSimModels (the default) disables the feature
// entirely; see Detectors/gconfig/include/SimSetup/G4FastSimulation.h.
std::string fastSimModels = ""; // comma-separated model names to activate
std::string fastSimRegions = ""; // tracking media the models apply to (wildcards allowed)
float fastSimMinEnergy = 1.f; // GeV; below this the detailed transport runs
O2ParamDef(G4Params, "G4");
};

Expand Down
1 change: 1 addition & 0 deletions Detectors/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ add_subdirectory(ForwardAlign)


if(BUILD_SIMULATION)
add_subdirectory(FastSim)
add_subdirectory(gconfig)
o2_data_file(COPY gconfig DESTINATION Detectors)
endif()
Expand Down
17 changes: 17 additions & 0 deletions Detectors/FastSim/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Copyright 2019-2026 CERN and copyright holders of ALICE O2.
# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
# All rights not expressly granted are reserved.
#
# This software is distributed under the terms of the GNU General Public
# License v3 (GPL Version 3), copied verbatim in the file "COPYING".
#
# In applying this license CERN does not waive the privileges and immunities
# granted to it by virtue of its status as an Intergovernmental Organization
# or submit itself to any jurisdiction.

o2_add_library(FastSim
SOURCES src/FastSimModel.cxx
src/ToyAbsorberFastSim.cxx
src/G4FastSimulation.cxx
PUBLIC_LINK_LIBRARIES MC::Geant4VMC MC::Geant4 O2::SimConfig
)
82 changes: 82 additions & 0 deletions Detectors/FastSim/include/FastSim/FastSimModel.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Copyright 2019-2026 CERN and copyright holders of ALICE O2.
// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
// All rights not expressly granted are reserved.
//
// This software is distributed under the terms of the GNU General Public
// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
//
// In applying this license CERN does not waive the privileges and immunities
// granted to it by virtue of its status as an Intergovernmental Organization
// or submit itself to any jurisdiction.

#ifndef O2_FASTSIM_MODEL_H_
#define O2_FASTSIM_MODEL_H_

/// Base class for fast simulation models.
///
/// A fast simulation model replaces the detailed transport through a region of
/// the geometry by a function from the particle that enters it to the particles
/// that leave it. `DoIt()` below is the plumbing and is the same for every
/// model; a model implements `sample()` and nothing else.

#include "G4VFastSimulationModel.hh"

#include <vector>

class G4FastStep;
class G4FastTrack;
class G4ParticleDefinition;

namespace o2::fastsim
{

/// The particle entering the envelope, plus the geometric context a model needs.
/// Units are the O2/VMC ones: cm, GeV, ns.
struct FastSimInput {
int pdg = 0;
double position[3] = {}; ///< global, on the envelope surface
double direction[3] = {}; ///< unit vector
double kineticEnergy = 0.; ///< GeV
double mass = 0.; ///< GeV
double time = 0.; ///< ns
double exitDistance = 0.; ///< cm from `position` to the envelope surface along `direction`
};

/// One particle leaving the envelope.
struct FastSimOutput {
int pdg = 0;
double position[3] = {}; ///< global; put it outside the envelope surface
double momentum[3] = {}; ///< GeV/c
double time = 0.; ///< ns
};

/// A secondary created exactly on the envelope surface is located by the
/// navigator in whichever daughter owns that point, which costs two extra
/// zero-length steps before it gets out. Models should emit just beyond it.
constexpr double kSurfaceEpsilonCm = 1e-5;

class FastSimModel : public G4VFastSimulationModel
{
public:
FastSimModel(const G4String& name, double minEnergyGeV);

G4bool IsApplicable(const G4ParticleDefinition& particle) override;
G4bool ModelTrigger(const G4FastTrack& fastTrack) override;

/// Measures the distance to the envelope surface, asks `sample()` what comes
/// out, kills the incident particle, stacks the result and books the energy
/// difference as a deposit.
void DoIt(const G4FastTrack& fastTrack, G4FastStep& fastStep) final;

protected:
/// Given the particle that entered, return everything that leaves. This is
/// the function a trained model implements.
virtual std::vector<FastSimOutput> sample(const FastSimInput& input) const = 0;

private:
double mMinEnergy = 0.; ///< internal Geant4 units; below this the detailed transport runs
};

} // namespace o2::fastsim

#endif // O2_FASTSIM_MODEL_H_
63 changes: 63 additions & 0 deletions Detectors/FastSim/include/FastSim/G4FastSimulation.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright 2019-2026 CERN and copyright holders of ALICE O2.
// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
// All rights not expressly granted are reserved.
//
// This software is distributed under the terms of the GNU General Public
// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
//
// In applying this license CERN does not waive the privileges and immunities
// granted to it by virtue of its status as an Intergovernmental Organization
// or submit itself to any jurisdiction.

#ifndef O2_FASTSIM_G4_FAST_SIMULATION_H_
#define O2_FASTSIM_G4_FAST_SIMULATION_H_

/// Wiring of the fast simulation models into the Geant4 engine.
///
/// The feature is OFF unless `G4.fastSimModels` names a model.
///
/// o2-sim -n 10 -g pythia8pp -e TGeant4 -m PIPE ABSO
/// --configKeyValues "G4.fastSimModels=toyAbsorber;
/// G4.fastSimRegions=ABSO_AIR_ENVELOPE"
///
/// Regions are selected by TRACKING MEDIUM name (wildcards allowed), which
/// Geant4-VMC maps to the medium's MATERIAL; every volume of that material joins
/// the region. That is why the absorber mother volume AFaM carries a material of
/// its own (see Detectors/Passive/src/Absorber.cxx). Selection by volume is not
/// available: the VMC special cuts already root every logical volume in a
/// per-material region, and Geant4 allows a volume in only one region.

#include "TG4RunConfiguration.h"
#include "TG4VUserFastSimulation.h"

#include <string>
#include <vector>

namespace o2::fastsim
{

/// Creates and registers the models named in `G4.fastSimModels`.
class G4FastSimulation : public TG4VUserFastSimulation
{
public:
G4FastSimulation(std::vector<std::string> models, const std::string& regions,
double minEnergyGeV);
void Construct() override;

private:
std::vector<std::string> mModels;
double mMinEnergy = 1.;
};

/// The one hook O2 was missing. Returns nullptr when no model is configured,
/// which is exactly the behaviour before this file existed.
class G4RunConfiguration : public TG4RunConfiguration
{
public:
using TG4RunConfiguration::TG4RunConfiguration;
TG4VUserFastSimulation* CreateUserFastSimulation() override;
};

} // namespace o2::fastsim

#endif // O2_FASTSIM_G4_FAST_SIMULATION_H_
34 changes: 34 additions & 0 deletions Detectors/FastSim/include/FastSim/ToyAbsorberFastSim.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright 2019-2026 CERN and copyright holders of ALICE O2.
// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
// All rights not expressly granted are reserved.
//
// This software is distributed under the terms of the GNU General Public
// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
//
// In applying this license CERN does not waive the privileges and immunities
// granted to it by virtue of its status as an Intergovernmental Organization
// or submit itself to any jurisdiction.

#ifndef O2_FASTSIM_TOY_ABSORBER_H_
#define O2_FASTSIM_TOY_ABSORBER_H_

#include "FastSim/FastSimModel.h"

namespace o2::fastsim
{

/// A toy model: one particle out, continuing along the incident direction with
/// the energy exponentially attenuated over the path through the envelope.
/// It exists to exercise the machinery, not to describe an absorber.
class ToyAbsorberFastSim : public FastSimModel
{
public:
using FastSimModel::FastSimModel;

protected:
std::vector<FastSimOutput> sample(const FastSimInput& input) const override;
};

} // namespace o2::fastsim

#endif // O2_FASTSIM_TOY_ABSORBER_H_
115 changes: 115 additions & 0 deletions Detectors/FastSim/src/FastSimModel.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// Copyright 2019-2026 CERN and copyright holders of ALICE O2.
// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
// All rights not expressly granted are reserved.
//
// This software is distributed under the terms of the GNU General Public
// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
//
// In applying this license CERN does not waive the privileges and immunities
// granted to it by virtue of its status as an Intergovernmental Organization
// or submit itself to any jurisdiction.

#include "FastSim/FastSimModel.h"

#include <fairlogger/Logger.h>

#include <G4DynamicParticle.hh>
#include <G4FastStep.hh>
#include <G4FastTrack.hh>
#include <G4ParticleDefinition.hh>
#include <G4ParticleTable.hh>
#include <G4SystemOfUnits.hh>
#include <G4ThreeVector.hh>
#include <G4Track.hh>
#include <G4VSolid.hh>

#include <algorithm>

namespace o2::fastsim
{

//_____________________________________________________________________________
FastSimModel::FastSimModel(const G4String& name, double minEnergyGeV)
: G4VFastSimulationModel(name), mMinEnergy(minEnergyGeV * CLHEP::GeV)
{
}

//_____________________________________________________________________________
G4bool FastSimModel::IsApplicable(const G4ParticleDefinition&)
{
// Which particles a model sees is decided by the `setParticles` selection,
// not here.
return true;
}

//_____________________________________________________________________________
G4bool FastSimModel::ModelTrigger(const G4FastTrack& fastTrack)
{
// Below the threshold the detailed transport is cheap and a surrogate would
// be extrapolating.
return fastTrack.GetPrimaryTrack()->GetKineticEnergy() > mMinEnergy;
}

//_____________________________________________________________________________
void FastSimModel::DoIt(const G4FastTrack& fastTrack, G4FastStep& fastStep)
{
const G4Track* track = fastTrack.GetPrimaryTrack();
const G4ThreeVector& position = track->GetPosition();
const G4ThreeVector& direction = track->GetMomentumDirection();

FastSimInput input;
input.pdg = track->GetDefinition()->GetPDGEncoding();
input.position[0] = position.x() / CLHEP::cm;
input.position[1] = position.y() / CLHEP::cm;
input.position[2] = position.z() / CLHEP::cm;
input.direction[0] = direction.x();
input.direction[1] = direction.y();
input.direction[2] = direction.z();
input.kineticEnergy = track->GetKineticEnergy() / CLHEP::GeV;
input.mass = track->GetDefinition()->GetPDGMass() / CLHEP::GeV;
input.time = track->GetGlobalTime() / CLHEP::ns;
input.exitDistance = fastTrack.GetEnvelopeSolid()->DistanceToOut(
fastTrack.GetPrimaryTrackLocalPosition(),
fastTrack.GetPrimaryTrackLocalDirection()) /
CLHEP::cm;

const std::vector<FastSimOutput> outgoing = sample(input);

fastStep.KillPrimaryTrack();
fastStep.ProposePrimaryTrackPathLength(input.exitDistance * CLHEP::cm);

// NOTE: a fast step defaults to AvoidHitInvocation, so Geant4 does not call
// the sensitive detector and TVirtualMCApplication::Stepping() is not invoked
// for it. For a passive envelope that is what we want -- there are no hits to
// lose, and the steps disappearing from the step log is the saving. A model
// covering a region that scores would add
// fastStep.ProposeSteppingControl(NormalCondition);
// here.

double outgoingKineticEnergy = 0.;
fastStep.SetNumberOfSecondaryTracks(outgoing.size());
for (const auto& out : outgoing) {
const G4ParticleDefinition* definition =
G4ParticleTable::GetParticleTable()->FindParticle(out.pdg);
if (definition == nullptr) {
LOG(error) << "fast simulation: model " << GetName() << " returned unknown pdg " << out.pdg
<< "; particle dropped";
continue;
}
const G4ThreeVector momentum(out.momentum[0] * CLHEP::GeV, out.momentum[1] * CLHEP::GeV,
out.momentum[2] * CLHEP::GeV);
G4DynamicParticle particle(definition, momentum);
outgoingKineticEnergy += particle.GetKineticEnergy();
fastStep.CreateSecondaryTrack(particle,
G4ThreeVector(out.position[0] * CLHEP::cm,
out.position[1] * CLHEP::cm,
out.position[2] * CLHEP::cm),
out.time * CLHEP::ns, /*localCoordinates=*/false);
}

// Whatever did not come out stayed in.
fastStep.ProposeTotalEnergyDeposited(
std::max(0., track->GetKineticEnergy() - outgoingKineticEnergy));
}

} // namespace o2::fastsim
Loading
Loading