Skip to content

Commit 4c63654

Browse files
author
Ankur Yadav
committed
TPC: add UseGeant4Edep flag with ionisation charge fluctuation for Kr-83m calibration
Adds a boolean flag TPCDetParam.UseGeant4Edep (default false) that switches ProcessHits' ionisation calculation from the standard Bethe-Bloch/NA49 collision-sampling model to the direct Geant4 energy deposit (fMC->Edep()), with Fano-limited Gamma-distributed smearing via ScaleFactorG4/FanoFactorG4 (TPCGasParam), matching the O2 Geant4-specific ionisation model used before commit 7860b87. A SetSpecialPhysicsCuts() override sets 1 keV VMC cuts on the TPC drift gas mediums before simcuts.dat is read, ensuring Auger electrons (1.921 keV minimum) are tracked and not killed early. The flag is off by default so the normal simulation chain is unaffected. Activate for Kr calibration via: --configKeyValues "TPCDetParam.UseGeant4Edep=1"
1 parent f2a59e7 commit 4c63654

3 files changed

Lines changed: 66 additions & 35 deletions

File tree

Detectors/TPC/base/include/TPCBase/ParameterDetector.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ struct ParameterDetector : public o2::conf::ConfigurableParamHelper<ParameterDet
3333
TimeBin TmaxTriggered = 550; ///< Maximum time bin in case of triggered readout mode
3434
float DriftTimeOffset = 7.3; ///< drift time offset in time bins (we observe ~2.4\mus before October 2023 and ~1.45 \mus after)
3535
bool ExcludeFCGap = true; ///< exclude electrons created in the gap between the IFC vessel and OFC vessel and FC strips
36+
bool UseGeant4Edep = false; ///< use Geant4 energy deposit directly for ionisation (for Kr-83m calibration runs)
3637

3738
O2ParamDef(ParameterDetector, "TPCDetParam");
3839
};

Detectors/TPC/simulation/include/TPCSimulation/Detector.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,8 @@ class Detector : public o2::base::DetImpl<Detector>
146146
void PostTrack() override { ; }
147147
void PreTrack() override { ; }
148148

149+
void SetSpecialPhysicsCuts() override;
150+
149151
void SetGeoFileName(const TString file) { mGeoFileName = file; }
150152
const TString& GetGeoFileName() const { return mGeoFileName; }
151153

Detectors/TPC/simulation/src/Detector.cxx

Lines changed: 63 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -193,41 +193,52 @@ Bool_t Detector::ProcessHits(FairVolume* vol)
193193
Int_t numberOfElectrons = 0;
194194
// I.H. - the type expected in addHit is short
195195

196-
// ---| Stepsize in cm |---
197-
const double stepSize = fMC->TrackStep();
198-
199-
double betaGamma = momentum.P() / fMC->TrackMass();
200-
betaGamma = TMath::Max(betaGamma, 7.e-3); // protection against too small bg
201-
202-
// ---| number of primary ionisations per cm |---
203-
const double primaryElectronsPerCM =
204-
gasParam.Nprim * BetheBlochAleph(static_cast<float>(betaGamma), gasParam.BetheBlochParam[0],
205-
gasParam.BetheBlochParam[1], gasParam.BetheBlochParam[2],
206-
gasParam.BetheBlochParam[3], gasParam.BetheBlochParam[4]);
207-
208-
// ---| mean number of collisions and random for this event |---
209-
const double meanNcoll = stepSize * trackCharge * trackCharge * primaryElectronsPerCM;
210-
const int nColl = static_cast<int>(fMC->GetRandom()->Poisson(meanNcoll));
211-
212-
// Variables needed to generate random powerlaw distributed energy loss
213-
const double alpha_p1 = 1. - gasParam.Exp; // NA49/G3 value
214-
const double oneOverAlpha_p1 = 1. / alpha_p1;
215-
const double eMin = gasParam.Ipot;
216-
const double eMax = gasParam.Eend;
217-
const double kMin = TMath::Power(eMin, alpha_p1);
218-
const double kMax = TMath::Power(eMax, alpha_p1);
219-
const double wIon = gasParam.Wion;
220-
221-
for (Int_t n = 0; n < nColl; n++) {
222-
// Use GEANT3 / NA49 expression:
223-
// P(eDep) ~ k * edep^-gasParam.getExp()
224-
// eMin(~I) < eDep < eMax(300 electrons)
225-
// k fixed so that Int_Emin^EMax P(Edep) = 1.
226-
const double rndm = fMC->GetRandom()->Rndm();
227-
const double eDep = TMath::Power((kMax - kMin) * rndm + kMin, oneOverAlpha_p1);
228-
int nel_step = static_cast<int>(((eDep - eMin) / wIon) + 1);
229-
nel_step = TMath::Min(nel_step, 300); // 300 electrons corresponds to 10 keV
230-
numberOfElectrons += nel_step;
196+
// use Geant4 energy deposit directly for ionisation (Kr-83m calibration simulations)
197+
if (detParam.UseGeant4Edep) {
198+
// We have multiple collisions and add fluctuations: smear nel using
199+
// gamma distr with mean = meanIon and variance = meanIon*FanoFactorG4.
200+
// These parameters were tuned for GEANT4.
201+
const double meanIon = fMC->Edep() / (gasParam.Wion * gasParam.ScaleFactorG4);
202+
if (meanIon > 0.) {
203+
numberOfElectrons = static_cast<int>(gasParam.FanoFactorG4 * Gamma(meanIon / gasParam.FanoFactorG4));
204+
}
205+
} else {
206+
// ---| Stepsize in cm |---
207+
const double stepSize = fMC->TrackStep();
208+
209+
double betaGamma = momentum.P() / fMC->TrackMass();
210+
betaGamma = TMath::Max(betaGamma, 7.e-3); // protection against too small bg
211+
212+
// ---| number of primary ionisations per cm |---
213+
const double primaryElectronsPerCM =
214+
gasParam.Nprim * BetheBlochAleph(static_cast<float>(betaGamma), gasParam.BetheBlochParam[0],
215+
gasParam.BetheBlochParam[1], gasParam.BetheBlochParam[2],
216+
gasParam.BetheBlochParam[3], gasParam.BetheBlochParam[4]);
217+
218+
// ---| mean number of collisions and random for this event |---
219+
const double meanNcoll = stepSize * trackCharge * trackCharge * primaryElectronsPerCM;
220+
const int nColl = static_cast<int>(fMC->GetRandom()->Poisson(meanNcoll));
221+
222+
// Variables needed to generate random powerlaw distributed energy loss
223+
const double alpha_p1 = 1. - gasParam.Exp; // NA49/G3 value
224+
const double oneOverAlpha_p1 = 1. / alpha_p1;
225+
const double eMin = gasParam.Ipot;
226+
const double eMax = gasParam.Eend;
227+
const double kMin = TMath::Power(eMin, alpha_p1);
228+
const double kMax = TMath::Power(eMax, alpha_p1);
229+
const double wIon = gasParam.Wion;
230+
231+
for (Int_t n = 0; n < nColl; n++) {
232+
// Use GEANT3 / NA49 expression:
233+
// P(eDep) ~ k * edep^-gasParam.getExp()
234+
// eMin(~I) < eDep < eMax(300 electrons)
235+
// k fixed so that Int_Emin^EMax P(Edep) = 1.
236+
const double rndm = fMC->GetRandom()->Rndm();
237+
const double eDep = TMath::Power((kMax - kMin) * rndm + kMin, oneOverAlpha_p1);
238+
int nel_step = static_cast<int>(((eDep - eMin) / wIon) + 1);
239+
nel_step = TMath::Min(nel_step, 300); // 300 electrons corresponds to 10 keV
240+
numberOfElectrons += nel_step;
241+
}
231242
}
232243

233244
// LOG(info) << "tpc::AddHit" << FairLogger::endl << "Eloss: "
@@ -3240,6 +3251,23 @@ std::string Detector::getHitBranchNames(int probe) const
32403251
return std::string();
32413252
}
32423253

3254+
void Detector::SetSpecialPhysicsCuts()
3255+
{
3256+
// lower energy threshold to track low-energy electrons for Kr-83m calibration
3257+
auto const& detParam = ParameterDetector::Instance();
3258+
LOG(info) << "TPC SetSpecialPhysicsCuts: UseGeant4Edep=" << detParam.UseGeant4Edep;
3259+
if (detParam.UseGeant4Edep) {
3260+
auto& matmgr = o2::base::MaterialManager::Instance();
3261+
for (int med : {(int)kDriftGas1, (int)kDriftGas2, (int)kCO2}) {
3262+
matmgr.SpecialCut(GetName(), med, o2::base::ECut::kCUTELE, 1e-6f);
3263+
matmgr.SpecialCut(GetName(), med, o2::base::ECut::kCUTGAM, 1e-6f);
3264+
matmgr.SpecialCut(GetName(), med, o2::base::ECut::kDCUTE, 1e-6f);
3265+
matmgr.SpecialCut(GetName(), med, o2::base::ECut::kBCUTE, 1e-6f);
3266+
}
3267+
}
3268+
o2::base::Detector::SetSpecialPhysicsCuts();
3269+
}
3270+
32433271
ClassImp(o2::tpc::Detector);
32443272

32453273
// Define Factory method for calling from the outside

0 commit comments

Comments
 (0)