Belle II Software development
SensitiveDetector.cc
1/**************************************************************************
2 * basf2 (Belle II Analysis Software Framework) *
3 * Author: The Belle II Collaboration *
4 * *
5 * See git log for contributors and copyright holders. *
6 * This file is licensed under LGPL-3.0, see LICENSE.md. *
7 **************************************************************************/
8
9/* Own header. */
10#include <ecl/simulation/SensitiveDetector.h>
11
12/* ECL headers. */
13#include <ecl/geometry/ECLGeometryPar.h>
14
15/* Basf2 headers. */
16#include <framework/gearbox/Const.h>
17#include <framework/gearbox/Unit.h>
18#include <simulation/background/BkgNeutronWeight.h>
19
20using namespace std;
21using namespace Belle2::ECL;
22
23#define UNUSED(x)
24
25SensitiveDetector::SensitiveDetector(G4String name, G4double UNUSED(thresholdEnergyDeposit),
26 G4double UNUSED(thresholdKineticEnergy)):
27 Simulation::SensitiveDetectorBase(name, Const::ECL),
28 m_eclSimHitRel(m_mcParticles, m_eclSimHits),
29 m_eclHitRel(m_mcParticles, m_eclHits),
30 m_ECLHadronComponentEmissionFunction()
31 // m_thresholdEnergyDeposit(thresholdEnergyDeposit),
32 // m_thresholdKineticEnergy(thresholdKineticEnergy)
33{
34 m_trackID = 0;
35 m_WeightedTime = 0;
36 m_energyDeposit = 0;
37 m_hadronenergyDeposit = 0;
38
39 registerMCParticleRelation(m_eclSimHitRel);
40 registerMCParticleRelation(m_eclHitRel);
41
42 m_eclSimHits.registerInDataStore();
43 m_eclHits.registerInDataStore();
44
45 m_mcParticles.registerRelationTo(m_eclSimHits);
46 m_mcParticles.registerRelationTo(m_eclHits);
47
48 m_HadronEmissionFunction = m_ECLHadronComponentEmissionFunction->getHadronComponentEmissionFunction();
49}
50
52{
53}
54
55void SensitiveDetector::Initialize(G4HCofThisEvent*)
56{
57}
58
59//Returns percent of scintillation emission from hadron component for given ionization dEdx value.
60//See section 5 of S. Longo and J. M. Roney 2018 JINST 13 P03018 for additional details.
62{
63 if (x < 2) return 0;
64 if (x > 232) return m_HadronEmissionFunction->Eval(232);
65 return m_HadronEmissionFunction->Eval(x);
66}
67
68//Returns total scintillation efficiency, normalized to 1 for photons, for CsI(Tl) given ionization dEdx value.
69//See section 5 of S. Longo and J. M. Roney 2018 JINST 13 P03018 for additional details.
70double GetCsITlScintillationEfficiency(double x)
71{
72 const double p0 = 1.52;
73 const double p1 = 0.00344839;
74 const double p2 = 2.0;
75 double val = 1;
76 if (x > 3.9406) {
77 val = (x * p0) / (x + (p1 * x * x) + p2);
78 }
79 return val;
80}
81//-----------------------------------------------------
82// Method invoked for every step in sensitive detector
83//-----------------------------------------------------
84bool SensitiveDetector::step(G4Step* aStep, G4TouchableHistory*)
85{
86 //
87 double preKineticEnergy = aStep->GetPreStepPoint()->GetKineticEnergy();
88 double postKineticEnergy = aStep->GetPostStepPoint()->GetKineticEnergy();
89 double avgKineticEnergy = 0.5 * (preKineticEnergy + postKineticEnergy);
90 const G4ParticleDefinition* StepParticleDefinition = aStep->GetTrack()->GetParticleDefinition();
91 G4Material* StepMaterial = aStep->GetTrack()->GetMaterial();
92 const double CsIDensity = 4.51; //gcm^-3
93 double ELE_DEDX = m_emCal.ComputeDEDX(avgKineticEnergy, StepParticleDefinition, "eIoni",
94 StepMaterial) / CLHEP::MeV * CLHEP::cm / (CsIDensity);
95 double MU_DEDX = m_emCal.ComputeDEDX(avgKineticEnergy, StepParticleDefinition, "muIoni",
96 StepMaterial) / CLHEP::MeV * CLHEP::cm / (CsIDensity);
97 double HAD_DEDX = m_emCal.ComputeDEDX(avgKineticEnergy, StepParticleDefinition, "hIoni",
98 StepMaterial) / CLHEP::MeV * CLHEP::cm / (CsIDensity);
99 double ION_DEDX = m_emCal.ComputeDEDX(avgKineticEnergy, StepParticleDefinition, "ionIoni",
100 StepMaterial) / CLHEP::MeV * CLHEP::cm / (CsIDensity);
101 G4double DEDX_val = ELE_DEDX + MU_DEDX + HAD_DEDX + ION_DEDX; //Ionization dE/dx for any particle type
102
103 G4double edep = aStep->GetTotalEnergyDeposit();
104 double LightOutputCorrection = GetCsITlScintillationEfficiency(DEDX_val);
105 edep *= LightOutputCorrection;
106 const G4StepPoint& s0 = *aStep->GetPreStepPoint();
107 const G4StepPoint& s1 = *aStep->GetPostStepPoint();
108 const G4Track& track = *aStep->GetTrack();
109
110 if (m_trackID != track.GetTrackID()) { //TrackID changed, store track information
111 m_trackID = track.GetTrackID();
112 m_momentum = s0.GetMomentum(); // Get momentum
113 //Reset track parameters
114 m_energyDeposit = 0;
115 m_WeightedTime = 0;
116 m_WeightedPos.set(0, 0, 0);
118 }
119 //Update energy deposit
120 G4double hedep = 0.5 * edep; // half of energy deposition -- for averaging purpose
121 m_energyDeposit += edep;
122 m_WeightedTime += (s0.GetGlobalTime() + s1.GetGlobalTime()) * hedep;
123 m_WeightedPos += (s0.GetPosition() + s1.GetPosition()) * hedep;
124 //
125 //Calculate hadronic scintillation component intensity from dEdx
126 m_hadronenergyDeposit += (edep / CLHEP::GeV) * GetHadronIntensityFromDEDX(DEDX_val);
127 //
128 //Save Hit if track leaves volume or is killed
129 if (track.GetNextVolume() != track.GetVolume() || track.GetTrackStatus() >= fStopAndKill) {
130 if (m_energyDeposit > 0.0) {
132 G4int cellID = eclp->TouchableToCellID(s0.GetTouchable());
133 G4double dTotalEnergy = 1 / m_energyDeposit;
134 m_WeightedTime *= dTotalEnergy;
135 m_WeightedPos *= dTotalEnergy;
136 int pdgCode = track.GetDefinition()->GetPDGEncoding();
138 }
139 //Reset TrackID
140 m_trackID = 0;
141 }
142 return true;
143}
144
145void SensitiveDetector::EndOfEvent(G4HCofThisEvent*)
146{
147 struct hit_t { double e, t; };
148 map<int, hit_t> a;
149
150 struct thit_t { int tid, hid; };
151 vector<thit_t> tr;
152
154
155 for (const ECLSimHit& t : m_eclSimHits) {
156 double tof = t.getFlightTime();
157 if (tof >= 8000.0 || tof < -8000.0) continue;
158 int TimeIndex = (tof + 8000.0) * (1. / 100);
159 int cellId = t.getCellId() - 1;
160 int key = cellId * 160 + TimeIndex;
161 double edep = t.getEnergyDep();
162 double tsen = tof + eclp->time2sensor(cellId, t.getPosition()); // flight time to diode sensor
163
164 hit_t& h = a[key];
165
166 int trkid = t.getTrackId();
167 tr.push_back({trkid, key});
168
169 double old_edep = h.e, old_tsen = h.t;
170 double new_edep = old_edep + edep;
171
172 h.e = new_edep;
173 h.t = (old_edep * old_tsen + edep * tsen) / new_edep;
174 }
175
176 int hitNum = m_eclHits.getEntries();
177 // assert(hitNum == 0);
178 for (pair<int, hit_t> t : a) {
179 int key = t.first, cellId = key / 160;
180 for (const thit_t& s : tr)
181 if (s.hid == key) m_eclHitRel.add(s.tid, hitNum);
182 const hit_t& h = t.second;
183 m_eclHits.appendNew(cellId + 1, h.e, h.t); hitNum++;
184 }
185}
186
187int SensitiveDetector::saveSimHit(G4int cellId, G4int trackID, G4int pid, G4double tof, G4double edep,
188 const G4ThreeVector& mom, const G4ThreeVector& pos, double Hadronedep)
189{
190 int simhitNumber = m_eclSimHits.getEntries();
191 m_eclSimHitRel.add(trackID, simhitNumber);
192 tof *= 1 / CLHEP::ns;
193 edep *= 1 / CLHEP::GeV;
194 m_eclSimHits.appendNew(cellId + 1, trackID, pid, tof, edep, mom * (1 / CLHEP::GeV), pos * (1 / CLHEP::cm), Hadronedep);
195 return simhitNumber;
196}
197
198SensitiveDiode::SensitiveDiode(const G4String& name):
199 Simulation::SensitiveDetectorBase(name, Const::ECL)
200{
201 m_eclHits.registerInDataStore("ECLDiodeHits");
203 m_trackID = -1;
204 m_tsum = 0;
205 m_esum = 0;
206}
207
209{
210}
211
212void SensitiveDiode::Initialize(G4HCofThisEvent*)
213{
214 m_hits.clear();
215 m_cells.clear();
216}
217
218//-----------------------------------------------------
219// Method invoked for every step in sensitive detector
220//-----------------------------------------------------
221bool SensitiveDiode::step(G4Step* aStep, G4TouchableHistory*)
222{
223 const G4StepPoint& s0 = *aStep->GetPreStepPoint();
224 const G4StepPoint& s1 = *aStep->GetPostStepPoint();
225 const G4Track& track = *aStep->GetTrack();
226
227 if (m_trackID != track.GetTrackID()) { //TrackID changed, store track information
228 m_trackID = track.GetTrackID();
229 //Reset track parameters
230 m_esum = 0;
231 m_tsum = 0;
232 }
233 //Update energy deposit
234 G4double edep = aStep->GetTotalEnergyDeposit();
235 m_esum += edep;
236 m_tsum += (s0.GetGlobalTime() + s1.GetGlobalTime()) * edep;
237 //Save Hit if track leaves volume or is killed
238 if (track.GetNextVolume() != track.GetVolume() ||
239 track.GetTrackStatus() >= fStopAndKill) {
240 if (m_esum > 0.0) {
241 int cellID = m_eclp->TouchableDiodeToCellID(s0.GetTouchable());
242 m_tsum /= 2 * m_esum; // average
243 m_hits.push_back({cellID, m_esum, m_tsum});
244 auto it = find(m_cells.begin(), m_cells.end(), cellID);
245 if (it == m_cells.end())m_cells.push_back(cellID);
246#if 0
247 const G4ThreeVector& p = s0.GetPosition();
248 G4ThreeVector cp = 10 * m_eclp->getCrystalPos(cellID);
249 G4ThreeVector cv = m_eclp->getCrystalVec(cellID);
250 double dz = (p - cp) * cv;
251 double rho = ((p - cp) - dz * cv).perp();
252 cout << "diff " << cellID << " " << dz << " " << rho << endl;
253 if (abs(dz - 150) > 1) {
254 const G4VTouchable* touch = s0.GetTouchable();
255 const G4NavigationHistory* h = touch->GetHistory();
256 int hd = h->GetDepth();
257 int i1 = h->GetReplicaNo(hd - 1); // index of each volume is set at physical volume creation
258 int i2 = h->GetReplicaNo(hd - 2); // go up in volume hierarchy
259 const G4String& vname = touch->GetVolume()->GetName();
260 cout << vname << " " << hd << " " << i1 << " " << i2 << endl;
261 for (int i = 0; i <= hd; i++) {
262 G4VPhysicalVolume* v = h->GetVolume(i);
263 cout << v->GetName() << endl;
264 }
265 }
266#endif
267 }
268 //Reset TrackID
269 m_trackID = 0;
270 }
271 return true;
272}
273
274void SensitiveDiode::EndOfEvent(G4HCofThisEvent*)
275{
276 struct dep_t {double e, t;};
277 vector<dep_t> v;
278 for (int cellId : m_cells) {
279 v.clear();
280 for (const hit_t& h : m_hits) {
281 if (cellId == h.cellId) v.push_back({h.e, h.t});
282 }
283
284 double esum = 0, tsum = 0;
285 for (const dep_t& t : v) {
286 esum += t.e;
287 tsum += t.t * t.e;
288 }
289 tsum /= esum;
290
291 tsum *= 1 / CLHEP::ns;
292 esum *= 1 / CLHEP::GeV;
293 m_eclHits.appendNew(cellId + 1, esum, tsum);
294
295
296 // cout<<"DD: "<<cellId<<" "<<esum<<" "<<tsum<<" +- "<<sqrt(tsum2)<<endl;
297
298 // sort(v.begin(),v.end(),[](const dep_t &a, const dep_t &b){return a.t<b.t;});
299 // cout<<"DD: "<<cellId<<" ";
300 // for(const dep_t &t: v)cout<<t.e<<" MeV "<<t.t<<" nsec, ";
301 // cout<<"\n";
302
303 }
304}
305
307 Simulation::SensitiveDetectorBase(name, Const::invalidDetector),
308 m_eclBeamBkgHitRel(m_mcParticles, m_eclBeamBkgHits)
309{
311 m_eclBeamBkgHits.registerInDataStore();
313
315 m_energyDeposit = 0;
316 m_startEnergy = 0;
317 m_startTime = 0;
318 m_trackLength = 0;
319 m_trackID = -1;
320}
321
322//-----------------------------------------------------
323// Method invoked for every step in sensitive detector
324//-----------------------------------------------------
325bool BkgSensitiveDiode::step(G4Step* aStep, G4TouchableHistory*)
326{
327 const G4StepPoint& s0 = *aStep->GetPreStepPoint();
328 const G4Track& track = *aStep->GetTrack();
329
330 if (m_trackID != track.GetTrackID()) { //TrackID changed, store track information
331 m_trackID = track.GetTrackID();
332 //Get world position
333 const G4ThreeVector& worldPosition = s0.GetPosition();
334 const double mm2cm = Unit::mm / Unit::cm;
335 m_startPos.SetXYZ(worldPosition.x() * mm2cm, worldPosition.y() * mm2cm, worldPosition.z() * mm2cm);
336 //Get momentum
337 const G4ThreeVector& momentum = s0.GetMomentum() ;
338 m_startMom.SetXYZ(momentum.x() * Unit::MeV, momentum.y() * Unit::MeV, momentum.z() * Unit::MeV);
339 //Get time
340 m_startTime = s0.GetGlobalTime();
341 //Get energy
342 m_startEnergy = s0.GetKineticEnergy() * Unit::MeV;
343 //Reset energy deposit;
344 m_energyDeposit = 0;
345 //Reset track length;
346 m_trackLength = 0;
347 }
348 //Update energy deposit
349 m_energyDeposit += aStep->GetTotalEnergyDeposit() * Unit::MeV;
350 m_trackLength += aStep->GetStepLength() * Unit::mm;
351 //Save Hit if track leaves volume or is killed
352 if (track.GetNextVolume() != track.GetVolume() ||
353 track.GetTrackStatus() >= fStopAndKill) {
354 int pdgCode = track.GetDefinition()->GetPDGEncoding();
355 double endEnergy = track.GetKineticEnergy() * Unit::MeV;
356 double neutWeight = 0;
357 if (pdgCode == Const::neutron.getPDGCode()) {
359 neutWeight = wt.getWeight(m_startEnergy / Unit::MeV);
360 }
361
362 int bkgHitNumber = m_eclBeamBkgHits.getEntries();
363 m_eclBeamBkgHitRel.add(m_trackID, bkgHitNumber);
364
365 int cellID = m_eclp->TouchableDiodeToCellID(s0.GetTouchable());
366 m_eclBeamBkgHits.appendNew(6, cellID, pdgCode, m_trackID, m_startPos, m_startMom, m_startTime, endEnergy, m_startEnergy,
367 m_energyDeposit, m_trackLength, neutWeight);
368
369 //Reset TrackID
370 m_trackID = 0;
371 }
372 return true;
373}
The class to get the weighting factor for a 1-MeV-equivalent neutron flux on silicon.
This class provides a set of constants for the framework.
Definition: Const.h:34
static const ParticleType neutron
neutron particle
Definition: Const.h:675
ClassECLSimHit - Geant4 simulated hit for the ECL.
Definition: ECLSimHit.h:29
bool step(G4Step *aStep, G4TouchableHistory *history) override
Process each step and calculate variables defined in ECLHit.
double m_energyDeposit
energy deposited in volume
ROOT::Math::XYZVector m_startPos
particle position at the entrance in volume
BkgSensitiveDiode(const G4String &)
Constructor.
double m_startEnergy
particle energy at the entrance in volume
double m_trackLength
length of the track in the volume
ECLGeometryPar * m_eclp
pointer to ECLGeometryPar
StoreArray< MCParticle > m_mcParticles
MCParticle array.
RelationArray m_eclBeamBkgHitRel
MCParticle to BeamBackHit relation array.
ROOT::Math::XYZVector m_startMom
particle momentum at the entrance in volume
StoreArray< BeamBackHit > m_eclBeamBkgHits
BeamBackHit array.
The Class for ECL Geometry Parameters.
G4ThreeVector getCrystalVec(int cid)
The direction of crystal.
static ECLGeometryPar * Instance()
Static method to get a reference to the ECLGeometryPar instance.
int TouchableToCellID(const G4VTouchable *)
The same as above but without sanity checks.
G4ThreeVector getCrystalPos(int cid)
The Position of crystal.
double time2sensor(int cid, const G4ThreeVector &hit_pos)
function to calculate flight time to diode sensor
int TouchableDiodeToCellID(const G4VTouchable *)
Mapping from G4VTouchable copyNumbers to Crystal CellID.
bool step(G4Step *aStep, G4TouchableHistory *history) override
Process each step and calculate variables defined in ECLHit.
double m_energyDeposit
total energy deposited in a volume by a track
double GetHadronIntensityFromDEDX(double)
Evaluates hadron scintillation component emission function.
double m_hadronenergyDeposit
energy deposited resulting in hadronic scint component
TGraph * m_HadronEmissionFunction
Graph for hadron scintillation component emission function.
StoreArray< ECLSimHit > m_eclSimHits
ECLSimHit array.
G4ThreeVector m_momentum
initial momentum of track before energy deposition inside sensitive volume
G4EmCalculator m_emCal
Used to get dE/dx for pulse shape simulations.
void EndOfEvent(G4HCofThisEvent *eventHC) override
Do what you want to do at the end of each event.
void Initialize(G4HCofThisEvent *HCTE) override
Register ECL hits collection into G4HCofThisEvent.
double m_WeightedTime
average track time weighted by energy deposition
RelationArray m_eclHitRel
MCParticle to ECLHit relation array.
StoreArray< ECLHit > m_eclHits
ECLHit array.
SensitiveDetector(G4String, G4double, G4double)
Constructor.
int saveSimHit(G4int, G4int, G4int, G4double, G4double, const G4ThreeVector &, const G4ThreeVector &, double)
Create ECLSimHit and ECLHit and relations from MCParticle and put them in datastore.
G4ThreeVector m_WeightedPos
average track position weighted by energy deposition
RelationArray m_eclSimHitRel
MCParticle to ECLSimHit relation array.
bool step(G4Step *aStep, G4TouchableHistory *history) override
Process each step and calculate variables defined in ECLHit.
std::vector< hit_t > m_hits
array of hits
int m_trackID
current track id
double m_tsum
average track time weighted by energy deposition
std::vector< int > m_cells
array of hit crystals
SensitiveDiode(const G4String &)
Constructor.
double m_esum
total energy deposited in a volume by a track
void EndOfEvent(G4HCofThisEvent *eventHC) override
Do what you want to do at the end of each event.
void Initialize(G4HCofThisEvent *HCTE) override
Register ECL hits collection into G4HCofThisEvent.
ECLGeometryPar * m_eclp
pointer to ECLGeometryPar
StoreArray< ECLHit > m_eclHits
ECLHit array.
void add(index_type from, index_type to, weight_type weight=1.0)
Add a new element to the relation.
static void registerMCParticleRelation(const std::string &name, RelationArray::EConsolidationAction ignoreAction=RelationArray::c_negativeWeight)
Register an relation involving MCParticles.
bool registerRelationTo(const StoreArray< TO > &toArray, DataStore::EDurability durability=DataStore::c_Event, DataStore::EStoreFlags storeFlags=DataStore::c_WriteOut, const std::string &namedRelation="") const
Register a relation to the given StoreArray.
Definition: StoreArray.h:140
static const double mm
[millimeters]
Definition: Unit.h:70
static const double MeV
[megaelectronvolt]
Definition: Unit.h:114
static const double cm
Standard units with the value = 1.
Definition: Unit.h:47
double getWeight(double ke)
Get weighting factor to convert a neutron to its 1-MeV equivalent.
static BkgNeutronWeight & getInstance()
Return a reference to the singleton BkgNeutronWeight instance.
STL namespace.