Project
Loading...
Searching...
No Matches
FastSimModel.cxx
Go to the documentation of this file.
1// Copyright 2019-2026 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
13
14#include <fairlogger/Logger.h>
15
16#include <G4DynamicParticle.hh>
17#include <G4FastStep.hh>
18#include <G4FastTrack.hh>
19#include <G4ParticleDefinition.hh>
20#include <G4ParticleTable.hh>
21#include <G4SystemOfUnits.hh>
22#include <G4ThreeVector.hh>
23#include <G4Track.hh>
24#include <G4NavigationHistory.hh>
25#include <G4TouchableHistory.hh>
26#include <G4VPhysicalVolume.hh>
27#include <G4VSolid.hh>
28#include <G4VTouchable.hh>
29
30#include <algorithm>
31
32namespace o2::fastsim
33{
34
35//_____________________________________________________________________________
36FastSimModel::FastSimModel(const G4String& name, const G4String& envelopeVolume,
37 double minEnergyGeV)
39 mEnvelope(envelopeVolume),
40 mMinEnergy(minEnergyGeV * CLHEP::GeV)
41{
42}
43
44//_____________________________________________________________________________
45int FastSimModel::envelopeDepth(const G4Track* track) const
46{
55 const G4VTouchable* touchable = track->GetTouchable();
56 if (touchable == nullptr) {
57 return -1;
58 }
59 const G4int depth = touchable->GetHistoryDepth();
60 for (G4int level = 0; level <= depth; ++level) {
61 const G4VPhysicalVolume* volume = touchable->GetVolume(level);
62 if (volume != nullptr && volume->GetLogicalVolume()->GetName() == mEnvelope) {
63 return level;
64 }
65 }
66 return -1;
67}
68
69//_____________________________________________________________________________
70G4bool FastSimModel::IsApplicable(const G4ParticleDefinition&)
71{
72 // Which particles a model sees is decided by the `setParticles` selection,
73 // not here.
74 return true;
75}
77//_____________________________________________________________________________
78G4bool FastSimModel::ModelTrigger(const G4FastTrack& fastTrack)
79{
80 const G4Track* track = fastTrack.GetPrimaryTrack();
81
82 // Below the threshold the detailed transport is cheap and a surrogate would
83 // be extrapolating.
84 if (track->GetKineticEnergy() <= mMinEnergy) {
85 return false;
86 }
87
88 // Geometric containment rather than a name list. This is what excludes, for
89 // instance, the absorber's steel support cradle: it shares its material with
90 // parts of the absorber, so no selection by material can separate them, but
91 // it sits outside the envelope and so fails here.
92 if (envelopeDepth(track) < 0) {
93 if (!mWarned) {
94 mWarned = true;
95 LOG(warn) << "fast simulation: model " << GetName() << " was consulted for a track "
96 << "outside its envelope '" << mEnvelope << "'; the region selection is "
97 << "wider than the envelope, which is allowed but wasteful";
98 }
99 return false;
100 }
101 return true;
102}
103
104//_____________________________________________________________________________
105void FastSimModel::DoIt(const G4FastTrack& fastTrack, G4FastStep& fastStep)
106{
107 const G4Track* track = fastTrack.GetPrimaryTrack();
108 const G4ThreeVector& position = track->GetPosition();
109 const G4ThreeVector& direction = track->GetMomentumDirection();
110
111 FastSimInput input;
112 input.pdg = track->GetDefinition()->GetPDGEncoding();
113 input.position[0] = position.x() / CLHEP::cm;
114 input.position[1] = position.y() / CLHEP::cm;
115 input.position[2] = position.z() / CLHEP::cm;
116 input.direction[0] = direction.x();
117 input.direction[1] = direction.y();
118 input.direction[2] = direction.z();
119 input.kineticEnergy = track->GetKineticEnergy() / CLHEP::GeV;
120 input.mass = track->GetDefinition()->GetPDGMass() / CLHEP::GeV;
121 input.time = track->GetGlobalTime() / CLHEP::ns;
122 // Deliberately NOT GetEnvelopeSolid(): that is the region's root volume, i.e.
123 // one absorber piece. Use the envelope volume instead, with the transform the
124 // touchable already holds for that level.
125 const G4int level = envelopeDepth(track);
126 const G4VTouchable* touchable = track->GetTouchable();
127 const G4AffineTransform& toLocal =
128 touchable->GetHistory()->GetTransform(touchable->GetHistoryDepth() - level);
129 const G4VSolid* envelopeSolid = touchable->GetVolume(level)->GetLogicalVolume()->GetSolid();
130
131 input.exitDistance = envelopeSolid->DistanceToOut(toLocal.TransformPoint(position),
132 toLocal.TransformAxis(direction)) /
133 CLHEP::cm;
134
135 const std::vector<FastSimOutput> outgoing = sample(input);
136
137 fastStep.KillPrimaryTrack();
138 fastStep.ProposePrimaryTrackPathLength(input.exitDistance * CLHEP::cm);
139
140 // NOTE: a fast step defaults to AvoidHitInvocation, so Geant4 does not call
141 // the sensitive detector and TVirtualMCApplication::Stepping() is not invoked
142 // for it. For a passive envelope that is what we want -- there are no hits to
143 // lose, and the steps disappearing from the step log is the saving. A model
144 // covering a region that scores would add
145 // fastStep.ProposeSteppingControl(NormalCondition);
146 // here.
147
148 double outgoingKineticEnergy = 0.;
149 fastStep.SetNumberOfSecondaryTracks(outgoing.size());
150 for (const auto& out : outgoing) {
151 const G4ParticleDefinition* definition =
152 G4ParticleTable::GetParticleTable()->FindParticle(out.pdg);
153 if (definition == nullptr) {
154 LOG(error) << "fast simulation: model " << GetName() << " returned unknown pdg " << out.pdg
155 << "; particle dropped";
156 continue;
157 }
158 const G4ThreeVector momentum(out.momentum[0] * CLHEP::GeV, out.momentum[1] * CLHEP::GeV,
159 out.momentum[2] * CLHEP::GeV);
160 G4DynamicParticle particle(definition, momentum);
161 outgoingKineticEnergy += particle.GetKineticEnergy();
162 fastStep.CreateSecondaryTrack(particle,
163 G4ThreeVector(out.position[0] * CLHEP::cm,
164 out.position[1] * CLHEP::cm,
165 out.position[2] * CLHEP::cm),
166 out.time * CLHEP::ns, /*localCoordinates=*/false);
167 }
168
169 // Whatever did not come out stayed in.
170 fastStep.ProposeTotalEnergyDeposited(
171 std::max(0., track->GetKineticEnergy() - outgoingKineticEnergy));
172}
173
174} // namespace o2::fastsim
G4bool IsApplicable(const G4ParticleDefinition &particle) override
virtual std::vector< FastSimOutput > sample(const FastSimInput &input) const =0
void DoIt(const G4FastTrack &fastTrack, G4FastStep &fastStep) override
FastSimModel(const G4String &name, const G4String &envelopeVolume, double minEnergyGeV)
G4bool ModelTrigger(const G4FastTrack &fastTrack) override
GLuint const GLchar * name
Definition glcorearb.h:781
GLint GLint GLsizei GLsizei GLsizei depth
Definition glcorearb.h:470
GLint level
Definition glcorearb.h:275
double position[3]
global, on the envelope surface
double direction[3]
unit vector
double exitDistance
cm from position to the ENVELOPE surface along direction
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"