Belle II Software  release-06-01-15
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 #include <ecl/simulation/SensitiveDetector.h>
9 #include <ecl/geometry/ECLGeometryPar.h>
10 #include <framework/gearbox/Unit.h>
11 #include <framework/gearbox/Const.h>
12 #include <simulation/background/BkgNeutronWeight.h>
13 
14 using namespace std;
15 using namespace Belle2::ECL;
16 
17 #define UNUSED(x)
18 
19 SensitiveDetector::SensitiveDetector(G4String name, G4double UNUSED(thresholdEnergyDeposit),
20  G4double UNUSED(thresholdKineticEnergy)):
21  Simulation::SensitiveDetectorBase(name, Const::ECL),
22  m_eclSimHitRel(m_mcParticles, m_eclSimHits),
23  m_eclHitRel(m_mcParticles, m_eclHits),
24  m_ECLHadronComponentEmissionFunction()
25  // m_thresholdEnergyDeposit(thresholdEnergyDeposit),
26  // m_thresholdKineticEnergy(thresholdKineticEnergy)
27 {
28  m_trackID = 0;
29  m_WeightedTime = 0;
30  m_energyDeposit = 0;
31  m_hadronenergyDeposit = 0;
32 
33  registerMCParticleRelation(m_eclSimHitRel);
34  registerMCParticleRelation(m_eclHitRel);
35 
36  m_eclSimHits.registerInDataStore();
37  m_eclHits.registerInDataStore();
38 
39  m_mcParticles.registerRelationTo(m_eclSimHits);
40  m_mcParticles.registerRelationTo(m_eclHits);
41 
42  m_HadronEmissionFunction = m_ECLHadronComponentEmissionFunction->getHadronComponentEmissionFunction();
43 }
44 
45 SensitiveDetector::~SensitiveDetector()
46 {
47 }
48 
49 void SensitiveDetector::Initialize(G4HCofThisEvent*)
50 {
51 }
52 
53 //Returns percent of scintillation emission from hadron component for given ionization dEdx value.
54 //See section 5 of S. Longo and J. M. Roney 2018 JINST 13 P03018 for additional details.
56 {
57  if (x < 2) return 0;
58  if (x > 232) return m_HadronEmissionFunction->Eval(232);
59  return m_HadronEmissionFunction->Eval(x);
60 }
61 
62 //Returns total scintillation efficiency, normalized to 1 for photons, for CsI(Tl) given ionization dEdx value.
63 //See section 5 of S. Longo and J. M. Roney 2018 JINST 13 P03018 for additional details.
64 double GetCsITlScintillationEfficiency(double x)
65 {
66  const double p0 = 1.52;
67  const double p1 = 0.00344839;
68  const double p2 = 2.0;
69  double val = 1;
70  if (x > 3.9406) {
71  val = (x * p0) / (x + (p1 * x * x) + p2);
72  }
73  return val;
74 }
75 //-----------------------------------------------------
76 // Method invoked for every step in sensitive detector
77 //-----------------------------------------------------
78 bool SensitiveDetector::step(G4Step* aStep, G4TouchableHistory*)
79 {
80  //
81  double preKineticEnergy = aStep->GetPreStepPoint()->GetKineticEnergy();
82  double postKineticEnergy = aStep->GetPostStepPoint()->GetKineticEnergy();
83  double avgKineticEnergy = 0.5 * (preKineticEnergy + postKineticEnergy);
84  const G4ParticleDefinition* StepParticleDefinition = aStep->GetTrack()->GetParticleDefinition();
85  G4Material* StepMaterial = aStep->GetTrack()->GetMaterial();
86  const double CsIDensity = 4.51; //gcm^-3
87  double ELE_DEDX = m_emCal.ComputeDEDX(avgKineticEnergy, StepParticleDefinition, "eIoni" ,
88  StepMaterial) / CLHEP::MeV * CLHEP::cm / (CsIDensity);
89  double MU_DEDX = m_emCal.ComputeDEDX(avgKineticEnergy, StepParticleDefinition, "muIoni" ,
90  StepMaterial) / CLHEP::MeV * CLHEP::cm / (CsIDensity);
91  double HAD_DEDX = m_emCal.ComputeDEDX(avgKineticEnergy, StepParticleDefinition, "hIoni" ,
92  StepMaterial) / CLHEP::MeV * CLHEP::cm / (CsIDensity);
93  double ION_DEDX = m_emCal.ComputeDEDX(avgKineticEnergy, StepParticleDefinition, "ionIoni",
94  StepMaterial) / CLHEP::MeV * CLHEP::cm / (CsIDensity);
95  G4double DEDX_val = ELE_DEDX + MU_DEDX + HAD_DEDX + ION_DEDX; //Ionization dE/dx for any particle type
96 
97  G4double edep = aStep->GetTotalEnergyDeposit();
98  double LightOutputCorrection = GetCsITlScintillationEfficiency(DEDX_val);
99  edep *= LightOutputCorrection;
100  const G4StepPoint& s0 = *aStep->GetPreStepPoint();
101  const G4StepPoint& s1 = *aStep->GetPostStepPoint();
102  const G4Track& track = *aStep->GetTrack();
103 
104  if (m_trackID != track.GetTrackID()) { //TrackID changed, store track informations
105  m_trackID = track.GetTrackID();
106  m_momentum = s0.GetMomentum(); // Get momentum
107  //Reset track parameters
108  m_energyDeposit = 0;
109  m_WeightedTime = 0;
110  m_WeightedPos.set(0, 0, 0);
112  }
113  //Update energy deposit
114  G4double hedep = 0.5 * edep; // half of energy deposition -- for averaging purpose
115  m_energyDeposit += edep;
116  m_WeightedTime += (s0.GetGlobalTime() + s1.GetGlobalTime()) * hedep;
117  m_WeightedPos += (s0.GetPosition() + s1.GetPosition()) * hedep;
118  //
119  //Calculate hadronic scintillation component intensity from dEdx
120  m_hadronenergyDeposit += (edep / CLHEP::GeV) * GetHadronIntensityFromDEDX(DEDX_val);
121  //
122  //Save Hit if track leaves volume or is killed
123  if (track.GetNextVolume() != track.GetVolume() || track.GetTrackStatus() >= fStopAndKill) {
124  if (m_energyDeposit > 0.0) {
126  G4int cellID = eclp->TouchableToCellID(s0.GetTouchable());
127  G4double dTotalEnergy = 1 / m_energyDeposit;
128  m_WeightedTime *= dTotalEnergy;
129  m_WeightedPos *= dTotalEnergy;
130  int pdgCode = track.GetDefinition()->GetPDGEncoding();
132  }
133  //Reset TrackID
134  m_trackID = 0;
135  }
136  return true;
137 }
138 
139 void SensitiveDetector::EndOfEvent(G4HCofThisEvent*)
140 {
141  struct hit_t { double e, t; };
142  map<int, hit_t> a;
143 
144  struct thit_t { int tid, hid; };
145  vector<thit_t> tr;
146 
148 
149  for (const ECLSimHit& t : m_eclSimHits) {
150  double tof = t.getFlightTime();
151  if (tof >= 8000.0 || tof < -8000.0) continue;
152  int TimeIndex = (tof + 8000.0) * (1. / 100);
153  int cellId = t.getCellId() - 1;
154  int key = cellId * 160 + TimeIndex;
155  double edep = t.getEnergyDep();
156  double tsen = tof + eclp->time2sensor(cellId, t.getPosition()); // flight time to diode sensor
157 
158  hit_t& h = a[key];
159 
160  int trkid = t.getTrackId();
161  tr.push_back({trkid, key});
162 
163  double old_edep = h.e, old_tsen = h.t;
164  double new_edep = old_edep + edep;
165 
166  h.e = new_edep;
167  h.t = (old_edep * old_tsen + edep * tsen) / new_edep;
168  }
169 
170  int hitNum = m_eclHits.getEntries();
171  // assert(hitNum == 0);
172  for (pair<int, hit_t> t : a) {
173  int key = t.first, cellId = key / 160;
174  for (const thit_t& s : tr)
175  if (s.hid == key) m_eclHitRel.add(s.tid, hitNum);
176  const hit_t& h = t.second;
177  m_eclHits.appendNew(cellId + 1, h.e, h.t); hitNum++;
178  }
179 }
180 
181 int SensitiveDetector::saveSimHit(G4int cellId, G4int trackID, G4int pid, G4double tof, G4double edep,
182  const G4ThreeVector& mom, const G4ThreeVector& pos, double Hadronedep)
183 {
184  int simhitNumber = m_eclSimHits.getEntries();
185  m_eclSimHitRel.add(trackID, simhitNumber);
186  tof *= 1 / CLHEP::ns;
187  edep *= 1 / CLHEP::GeV;
188  m_eclSimHits.appendNew(cellId + 1, trackID, pid, tof, edep, mom * (1 / CLHEP::GeV), pos * (1 / CLHEP::cm), Hadronedep);
189  return simhitNumber;
190 }
191 
192 SensitiveDiode::SensitiveDiode(const G4String& name):
193  Simulation::SensitiveDetectorBase(name, Const::ECL)
194 {
195  m_eclHits.registerInDataStore("ECLDiodeHits");
197  m_trackID = -1;
198  m_tsum = 0;
199  m_esum = 0;
200 }
201 
203 {
204 }
205 
206 void SensitiveDiode::Initialize(G4HCofThisEvent*)
207 {
208  m_hits.clear();
209  m_cells.clear();
210 }
211 
212 //-----------------------------------------------------
213 // Method invoked for every step in sensitive detector
214 //-----------------------------------------------------
215 bool SensitiveDiode::step(G4Step* aStep, G4TouchableHistory*)
216 {
217  const G4StepPoint& s0 = *aStep->GetPreStepPoint();
218  const G4StepPoint& s1 = *aStep->GetPostStepPoint();
219  const G4Track& track = *aStep->GetTrack();
220 
221  if (m_trackID != track.GetTrackID()) { //TrackID changed, store track informations
222  m_trackID = track.GetTrackID();
223  //Reset track parameters
224  m_esum = 0;
225  m_tsum = 0;
226  }
227  //Update energy deposit
228  G4double edep = aStep->GetTotalEnergyDeposit();
229  m_esum += edep;
230  m_tsum += (s0.GetGlobalTime() + s1.GetGlobalTime()) * edep;
231  //Save Hit if track leaves volume or is killed
232  if (track.GetNextVolume() != track.GetVolume() ||
233  track.GetTrackStatus() >= fStopAndKill) {
234  if (m_esum > 0.0) {
235  int cellID = m_eclp->TouchableDiodeToCellID(s0.GetTouchable());
236  m_tsum /= 2 * m_esum; // average
237  m_hits.push_back({cellID, m_esum, m_tsum});
238  auto it = find(m_cells.begin(), m_cells.end(), cellID);
239  if (it == m_cells.end())m_cells.push_back(cellID);
240 #if 0
241  const G4ThreeVector& p = s0.GetPosition();
242  G4ThreeVector cp = 10 * m_eclp->getCrystalPos(cellID);
243  G4ThreeVector cv = m_eclp->getCrystalVec(cellID);
244  double dz = (p - cp) * cv;
245  double rho = ((p - cp) - dz * cv).perp();
246  cout << "diff " << cellID << " " << dz << " " << rho << endl;
247  if (abs(dz - 150) > 1) {
248  const G4VTouchable* touch = s0.GetTouchable();
249  const G4NavigationHistory* h = touch->GetHistory();
250  int hd = h->GetDepth();
251  int i1 = h->GetReplicaNo(hd - 1); // index of each volume is set at physical volume creation
252  int i2 = h->GetReplicaNo(hd - 2); // go up in volume hierarchy
253  const G4String& vname = touch->GetVolume()->GetName();
254  cout << vname << " " << hd << " " << i1 << " " << i2 << endl;
255  for (int i = 0; i <= hd; i++) {
256  G4VPhysicalVolume* v = h->GetVolume(i);
257  cout << v->GetName() << endl;
258  }
259  }
260 #endif
261  }
262  //Reset TrackID
263  m_trackID = 0;
264  }
265  return true;
266 }
267 
268 void SensitiveDiode::EndOfEvent(G4HCofThisEvent*)
269 {
270  struct dep_t {double e, t;};
271  vector<dep_t> v;
272  for (int cellId : m_cells) {
273  v.clear();
274  for (const hit_t& h : m_hits) {
275  if (cellId == h.cellId) v.push_back({h.e, h.t});
276  }
277 
278  double esum = 0, tsum = 0;
279  for (const dep_t& t : v) {
280  esum += t.e;
281  tsum += t.t * t.e;
282  }
283  tsum /= esum;
284 
285  double trms = 0;
286  for (const dep_t& t : v) trms += pow(t.t - tsum, 2) * t.e ;
287  trms /= esum;
288 
289  tsum *= 1 / CLHEP::ns;
290  esum *= 1 / CLHEP::GeV;
291  m_eclHits.appendNew(cellId + 1, esum, tsum);
292 
293 
294  // cout<<"DD: "<<cellId<<" "<<esum<<" "<<tsum<<" +- "<<sqrt(tsum2)<<endl;
295 
296  // sort(v.begin(),v.end(),[](const dep_t &a, const dep_t &b){return a.t<b.t;});
297  // cout<<"DD: "<<cellId<<" ";
298  // for(const dep_t &t: v)cout<<t.e<<" MeV "<<t.t<<" nsec, ";
299  // cout<<"\n";
300 
301  }
302 }
303 
305  Simulation::SensitiveDetectorBase(name, Const::invalidDetector),
306  m_eclBeamBkgHitRel(m_mcParticles, m_eclBeamBkgHits)
307 {
309  m_eclBeamBkgHits.registerInDataStore();
311 
313  m_energyDeposit = 0;
314  m_startEnergy = 0;
315  m_startTime = 0;
316  m_trackLength = 0;
317  m_trackID = -1;
318 }
319 
320 //-----------------------------------------------------
321 // Method invoked for every step in sensitive detector
322 //-----------------------------------------------------
323 bool BkgSensitiveDiode::step(G4Step* aStep, G4TouchableHistory*)
324 {
325  const G4StepPoint& s0 = *aStep->GetPreStepPoint();
326  const G4Track& track = *aStep->GetTrack();
327 
328  if (m_trackID != track.GetTrackID()) { //TrackID changed, store track informations
329  m_trackID = track.GetTrackID();
330  //Get world position
331  const G4ThreeVector& worldPosition = s0.GetPosition();
332  const double mm2cm = Unit::mm / Unit::cm;
333  m_startPos.SetXYZ(worldPosition.x() * mm2cm , worldPosition.y() * mm2cm, worldPosition.z() * mm2cm);
334  //Get momentum
335  const G4ThreeVector& momentum = s0.GetMomentum() ;
336  m_startMom.SetXYZ(momentum.x() * Unit::MeV, momentum.y() * Unit::MeV, momentum.z() * Unit::MeV);
337  //Get time
338  m_startTime = s0.GetGlobalTime();
339  //Get energy
340  m_startEnergy = s0.GetKineticEnergy() * Unit::MeV;
341  //Reset energy deposit;
342  m_energyDeposit = 0;
343  //Reset track lenght;
344  m_trackLength = 0;
345  }
346  //Update energy deposit
347  m_energyDeposit += aStep->GetTotalEnergyDeposit() * Unit::MeV;
348  m_trackLength += aStep->GetStepLength() * Unit::mm;
349  //Save Hit if track leaves volume or is killed
350  if (track.GetNextVolume() != track.GetVolume() ||
351  track.GetTrackStatus() >= fStopAndKill) {
352  int pdgCode = track.GetDefinition()->GetPDGEncoding();
353  double endEnergy = track.GetKineticEnergy() * Unit::MeV;
354  double neutWeight = 0;
355  if (pdgCode == Const::neutron.getPDGCode()) {
357  neutWeight = wt.getWeight(m_startEnergy / Unit::MeV);
358  }
359 
360  int bkgHitNumber = m_eclBeamBkgHits.getEntries();
361  m_eclBeamBkgHitRel.add(m_trackID, bkgHitNumber);
362 
363  int cellID = m_eclp->TouchableDiodeToCellID(s0.GetTouchable());
364  m_eclBeamBkgHits.appendNew(6, cellID, pdgCode, m_trackID, m_startPos, m_startMom, m_startTime, endEnergy, m_startEnergy,
365  m_energyDeposit, m_trackLength, neutWeight);
366 
367  //Reset TrackID
368  m_trackID = 0;
369  }
370  return true;
371 }
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:556
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
TVector3 m_startMom
particle momentum at the entrance in volume
BkgSensitiveDiode(const G4String &)
Constructor.
TVector3 m_startPos
particle position at the entrance in volume
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.
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.
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.
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 hitted 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.