CbmRoot
Loading...
Searching...
No Matches
CbmStsPhysics.cxx
Go to the documentation of this file.
1/* Copyright (C) 2014-2020 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
2 SPDX-License-Identifier: GPL-3.0-only
3 Authors: Volker Friese [committer] */
4
10#include "CbmStsPhysics.h"
11
12#include "CbmStsDefs.h" // for CbmSts, kProtonMass, kSiCharge, kSiDe...
13#include "CbmStsPhysics.h"
14
15#include <Logger.h> // for Logger, LOG
16
17#include <TDatabasePDG.h> // for TDatabasePDG
18#include <TMath.h> // for Log, Power
19#include <TParticlePDG.h> // for TParticlePDG
20#include <TRandom.h> // for TRandom, gRandom
21#include <TString.h> // for TString, operator<<, operator+
22#include <TSystem.h> // for TSystem, gSystem
23
24#include <cmath> // for log, sqrt
25#include <fstream> // for ifstream, basic_istream, right
26#include <iomanip> // for setw, __iom_t6
27#include <utility> // for pair
28
29using std::ifstream;
30using std::map;
31using std::right;
32using std::setw;
33using namespace CbmSts;
34
36
37 // ----- Initialisation of static variables ----------------------------
39// -------------------------------------------------------------------------
40
41
42// ----- Constructor ---------------------------------------------------
44{
45 // --- Read the energy loss data tables
46 LOG(info) << "Instantiating STS Physics... ";
49
50 // --- Initialise the constants for the Urban model
52}
53// -------------------------------------------------------------------------
54
55
56// ----- Destructor ----------------------------------------------------
58// -------------------------------------------------------------------------
59
60
61// ----- Diffusion width -----------------------------------------------
62Double_t CbmStsPhysics::DiffusionWidth(Double_t z, Double_t d, Double_t vBias, Double_t vFd, Double_t temperature,
63 Int_t chargeType)
64{
65
66 // --- Check parameters. A tolerance of 0.1 micrometer on the sensor borders
67 // --- is used to avoid crashes due to rounding errors.
68 if (z < 0. && z > -0.00001) z = 0.;
69 if (z > d && z < d + 0.00001) z = d;
70 if (z < 0. || z > d) {
71 LOG(error) << "StsPhysics: z coordinate " << z << " not inside sensor (d = " << d << ")";
72 return -1.;
73 }
74 if (temperature < 0.) {
75 LOG(error) << "StsPhysics: illegal temperature value " << temperature;
76 return -1.;
77 }
78
79 // --- Diffusion constant over mobility [J/C]
80 // --- The numerical factor is k_B/e in units of J/(KC).
81 Double_t diffConst = 8.61733e-5 * temperature;
82
83 // --- Drift time times mobility [cm**2 * C / J]
84 // For the formula, see the STS digitiser note.
85 Double_t tau = 0.;
86 if (chargeType == 0) { // electrons, drift to n (front) side
87 tau = 0.5 * d * d / vFd * log((vBias + (1. - 2. * z / d) * vFd) / (vBias - vFd));
88 }
89 else if (chargeType == 1) { // holes, drift to the p (back) side
90 tau = -0.5 * d * d / vFd * log(1. - 2. * vFd * z / d / (vBias + vFd));
91 }
92 else {
93 LOG(error) << "StsPhysics: Illegal charge type " << chargeType;
94 return -1.;
95 }
96
97 return sqrt(2. * diffConst * tau);
98}
99// -------------------------------------------------------------------------
100
101
102// ----- Electric field ------------------------------------------------
103Double_t CbmStsPhysics::ElectricField(Double_t vBias, Double_t vFd, Double_t dZ, Double_t z)
104{
105 return (vBias + vFd * (2. * z / dZ - 1.)) / dZ;
106}
107// -------------------------------------------------------------------------
108
109
110// ----- Energy loss from fluctuation model ----------------------------
111Double_t CbmStsPhysics::EnergyLoss(Double_t dz, Double_t mass, Double_t eKin, Double_t dedx) const
112{
113
114 // Gamma and beta
115 Double_t gamma = (eKin + mass) / mass;
116 Double_t beta2 = 1. - 1. / (gamma * gamma);
117
118 // Auxiliary
119 Double_t xAux = 2. * mass * beta2 * gamma * gamma;
120
121 // Mean energy losses (PHYS333 2.4 eqs. (2) and (3))
122 Double_t sigma1 = dedx * fUrbanF1 / fUrbanE1 * (TMath::Log(xAux / fUrbanE1) - beta2)
123 / (TMath::Log(xAux / fUrbanI) - beta2) * (1. - fUrbanR);
124 Double_t sigma2 = dedx * fUrbanF2 / fUrbanE2 * (TMath::Log(xAux / fUrbanE2) - beta2)
125 / (TMath::Log(xAux / fUrbanI) - beta2) * (1. - fUrbanR);
126 Double_t sigma3 =
127 dedx * fUrbanEmax * fUrbanR / (fUrbanI * (fUrbanEmax + fUrbanI)) / TMath::Log((fUrbanEmax + fUrbanI) / fUrbanI);
128
129 // Sample number of processes Poissonian energy loss distribution
130 // (PHYS333 2.4 eq. (6))
131 Int_t n1 = gRandom->Poisson(sigma1 * dz);
132 Int_t n2 = gRandom->Poisson(sigma2 * dz);
133 Int_t n3 = gRandom->Poisson(sigma3 * dz);
134
135 // Ion energy loss (PHYS333 2.4 eq. (12))
136 Double_t eLossIon = 0.;
137 for (Int_t j = 1; j <= n3; j++) {
138 Double_t uni = gRandom->Uniform(1.);
139 eLossIon += fUrbanI / (1. - uni * fUrbanEmax / (fUrbanEmax + fUrbanI));
140 }
141
142 // Total energy loss
143 return (n1 * fUrbanE1 + n2 * fUrbanE2 + eLossIon);
144}
145// -------------------------------------------------------------------------
146
147
148// ----- Get static instance ---------------------------------------------
154// -------------------------------------------------------------------------
155
156
157// ----- Interpolate a value from a data table -------------------------
158Double_t CbmStsPhysics::InterpolateDataTable(Double_t eEquiv, map<Double_t, Double_t>& table)
159{
160
161 std::map<Double_t, Double_t>::iterator it = table.lower_bound(eEquiv);
162
163 // Input value smaller than or equal to first table entry:
164 // return first value
165 if (it == table.begin()) return it->second;
166
167 // Input value larger than last table entry: return last value
168 if (it == table.end()) return (--it)->second;
169
170 // Else: interpolate from table values
171 Double_t e2 = it->first;
172 Double_t v2 = it->second;
173 it--;
174 Double_t e1 = it->first;
175 Double_t v1 = it->second;
176 return (v1 + (eEquiv - e1) * (v2 - v1) / (e2 - e1));
177}
178// -------------------------------------------------------------------------
179
180
181// ----- Landau Width ------------------------------------------------
182Double_t CbmStsPhysics::LandauWidth(Double_t mostProbableCharge)
183{
184
185 // --- Get interpolated value from the data table
186 return InterpolateDataTable(mostProbableCharge, fLandauWidth);
187}
188// -------------------------------------------------------------------------
189
190std::pair<std::vector<double>, double> CbmStsPhysics::GetLandauWidthTable() const
191{
192 std::vector<double> landauTableFlat;
193
194 auto landauEntry = fLandauWidth.begin();
195
196 landauTableFlat.push_back(landauEntry->second);
197
198 auto prevLandauEntry = landauEntry;
199 landauEntry++;
200
201 double stepSize = landauEntry->first - prevLandauEntry->first;
202
203 for (; landauEntry != fLandauWidth.end(); landauEntry++) {
204 LOG_IF(fatal, stepSize != landauEntry->first - prevLandauEntry->first)
205 << "StsLandau table doesn't have fixed step size.";
206
207 landauTableFlat.push_back(landauEntry->second);
208 prevLandauEntry = landauEntry;
209 }
210
211 return std::make_pair(std::move(landauTableFlat), stepSize);
212}
213
214
215// ----- Particle charge for PDG PID ----------------------------------
217{
218
219 Double_t charge = 0.;
220
221 // --- For particles in the TDatabasePDG. Note that TParticlePDG
222 // --- gives the charge in units of |e|/3, God knows why.
223 TParticlePDG* particle = TDatabasePDG::Instance()->GetParticle(pid);
224 if (particle) charge = particle->Charge() / 3.;
225
226 // --- For ions
227 else if (pid > 1000000000 && pid < 1010000000) {
228 Int_t myPid = pid / 10000;
229 charge = Double_t(myPid - (myPid / 1000) * 1000);
230 }
231
232 return charge;
233}
234// -------------------------------------------------------------------------
235
236
237// ----- Particle mass for PDG PID ------------------------------------
239{
240
241 Double_t mass = -1.;
242
243 // --- For particles in the TDatabasePDG
244 TParticlePDG* particle = TDatabasePDG::Instance()->GetParticle(pid);
245 if (particle) mass = particle->Mass();
246
247 // --- For ions
248 else if (pid > 1000000000 && pid < 1010000000) {
249 Int_t myPid = pid - 1e9;
250 myPid -= (myPid / 10000) * 10000;
251 mass = Double_t(myPid / 10);
252 }
253
254 return mass;
255}
256// -------------------------------------------------------------------------
257
258
259// ----- Read data tables for stopping power ---------------------------
261{
262
263 // The table with errors for Landau distribution:
264 // MP charge (e) --> half width of charge distribution (e)
265
266 TString dir = gSystem->Getenv("VMCWORKDIR");
267 TString errFileName = dir + "/parameters/sts/LandauWidthTable.txt";
268
269 ifstream inFile;
270 Double_t q, err;
271
272 // --- Read electron stopping power
273 inFile.open(errFileName);
274 if (inFile.is_open()) {
275 while (true) {
276 inFile >> q;
277 inFile >> err;
278 if (inFile.eof()) break;
279 fLandauWidth[q] = err;
280 }
281 inFile.close();
282 LOG(info) << "StsPhysics: " << setw(5) << right << fLandauWidth.size() << " values read from " << errFileName;
283 }
284 else
285 LOG(fatal) << "StsPhysics: Could not read from " << errFileName;
286}
287// -------------------------------------------------------------------------
288
289
290// ----- Read data tables for stopping power ---------------------------
292{
293
294 // The data tables are obtained from the NIST ESTAR and PSTAR databases:
295 // http://www.nist.gov/pml/data/star/index.cfm
296 // The first column in the tables is the kinetic energy in MeV, the second
297 // one is the specific stopping power in MeV*cm^2/g for Silicon.
298 // Internally, the values are stored in GeV and GeV*cm^2/g, respectively.
299 // Conversion MeV->GeV is done when reading from file.
300
301 TString dir = gSystem->Getenv("VMCWORKDIR");
302 TString eFileName = dir + "/parameters/sts/dEdx_Si_e.txt";
303 TString pFileName = dir + "/parameters/sts/dEdx_Si_p.txt";
304
305 ifstream inFile;
306 Double_t e, dedx;
307
308 // --- Read electron stopping power
309 inFile.open(eFileName);
310 if (inFile.is_open()) {
311 while (true) {
312 inFile >> e;
313 inFile >> dedx;
314 if (inFile.eof()) break;
315 e *= 1.e-3; // MeV -> GeV
316 dedx *= 1.e-3; // MeV -> GeV
317 fStoppingElectron[e] = dedx;
318 }
319 inFile.close();
320 LOG(info) << "StsPhysics: " << setw(5) << right << fStoppingElectron.size() << " values read from " << eFileName;
321 }
322 else
323 LOG(fatal) << "StsPhysics: Could not read from " << eFileName;
324
325 // --- Read proton stopping power
326 inFile.open(pFileName);
327 if (inFile.is_open()) {
328 while (true) {
329 inFile >> e;
330 inFile >> dedx;
331 if (inFile.eof()) break;
332 e *= 1.e-3; // MeV -> GeV
333 dedx *= 1.e-3; // MeV -> GeV
334 fStoppingProton[e] = dedx;
335 }
336 inFile.close();
337 LOG(info) << "StsPhysics: " << setw(5) << right << fStoppingProton.size() << " values read from " << pFileName;
338 }
339 else
340 LOG(fatal) << "StsPhysics: Could not read from " << pFileName;
341}
342// -------------------------------------------------------------------------
343
344
345// ----- Set the parameters for the Urban model ------------------------
347{
348
349 // --- Mean ionisation potential according to PHYS333 2.1
350 fUrbanI = 1.6e-8 * TMath::Power(z, 0.9); // in GeV
351
352 // --- Maximal energy loss (delta electron threshold) [GeV]
353 // TODO: 1 MeV is the default setting in our transport simulation.
354 // It would be desirable to obtain the actually used value automatically.
355 fUrbanEmax = 1.e-3;
356
357 // --- The following parameters were defined according the GEANT3 choice
358 // --- described in PHYS332 2.4 (p.264)
359
360 // --- Oscillator strengths of energy levels
361 fUrbanF1 = 1. - 2. / z;
362 fUrbanF2 = 2. / z;
363
364 // --- Energy levels [GeV]
365 fUrbanE2 = 1.e-8 * z * z;
366 fUrbanE1 = TMath::Power(fUrbanI / TMath::Power(fUrbanE2, fUrbanF2), 1. / fUrbanF1);
367
368 // --- Relative weight excitation / ionisation
369 fUrbanR = 0.4;
370
371 // --- Screen output
372 LOG(info) << "StsPhysics: Urban parameters for z = " << z << " :";
373 LOG(info) << "I = " << fUrbanI * 1.e9 << " eV, Emax = " << fUrbanEmax * 1.e9 << " eV, E1 = " << fUrbanE1 * 1.e9
374 << " eV, E2 = " << fUrbanE2 * 1.e9 << " eV, f1 = " << fUrbanF1 << ", f2 = " << fUrbanF2
375 << ", r = " << fUrbanR;
376}
377// -------------------------------------------------------------------------
378
379
380// ----- Stopping power ------------------------------------------------
381Double_t CbmStsPhysics::StoppingPower(Double_t eKin, Int_t pid)
382{
383
384 Double_t mass = ParticleMass(pid);
385 if (mass < 0.) return 0.;
386 Double_t charge = ParticleCharge(pid);
387 Bool_t isElectron = (pid == 11 || pid == -11);
388
389 return StoppingPower(eKin, mass, charge, isElectron);
390}
391// -------------------------------------------------------------------------
392
393
394// ----- Stopping power ------------------------------------------------
395Double_t CbmStsPhysics::StoppingPower(Double_t energy, Double_t mass, Double_t charge, Bool_t isElectron)
396{
397
398 // --- Get interpolated value from data table
399 Double_t stopPower = -1.;
400 if (isElectron) stopPower = InterpolateDataTable(energy, fStoppingElectron);
401 else {
402 Double_t eEquiv = energy * kProtonMass / mass; // equiv. proton energy
403 stopPower = InterpolateDataTable(eEquiv, fStoppingProton);
404 }
405
406 // --- Calculate stopping power (from specific SP and density of silicon)
407 stopPower *= kSiDensity; // density of silicon
408
409 // --- For non-electrons: scale with squared charge
410 if (!isElectron) stopPower *= (charge * charge);
411
412 return stopPower;
413}
414// -------------------------------------------------------------------------
ClassImp(CbmConverterManager)
friend fvec sqrt(const fvec &a)
friend fvec log(const fvec &a)
int Int_t
bool Bool_t
Auxiliary class for physics processes in Silicon.
void ReadDataTablesLandauWidth()
Read Landau width data table from file.
static Double_t ElectricField(Double_t vBias, Double_t vFd, Double_t dZ, Double_t z)
Electric field magnitude in a silicon sensor as function of z.
static Double_t ParticleCharge(Int_t pid)
Particle charge from PDG particle ID.
std::pair< std::vector< double >, double > GetLandauWidthTable() const
Raw values of landau width interpolation table.
Double_t fUrbanR
Urban model: weight parameter excitation/ionisation.
static Double_t ParticleMass(Int_t pid)
Particle mass from PDG particle ID.
Double_t InterpolateDataTable(Double_t eKin, std::map< Double_t, Double_t > &table)
Interpolate a value from the data tables.
static CbmStsPhysics * Instance()
Accessor to singleton instance.
static Double_t DiffusionWidth(Double_t z, Double_t d, Double_t vBias, Double_t vFd, Double_t temperature, Int_t chargeType)
Double_t StoppingPower(Double_t eKin, Int_t pid)
Stopping power (average specific energy loss) in Silicon.
Double_t LandauWidth(Double_t mostProbableCharge)
Half width at half max of Landau distribution in ultra-relativistic case.
static CbmStsPhysics * fgInstance
Singleton instance.
Double_t fUrbanE1
Urban model: first atomic energy level.
Double_t EnergyLoss(Double_t dz, Double_t mass, Double_t eKin, Double_t dedx) const
Energy loss in a Silicon layer.
std::map< Double_t, Double_t > fStoppingElectron
E [GeV] -> <-dE/dx> [GeV*g/cm^2].
void ReadDataTablesStoppingPower()
Read stopping power data table from file.
Double_t fUrbanE2
Urban model: second atomic energy level.
Double_t fUrbanI
Urban model: mean ionisation potential of Silicon.
CbmStsPhysics()
Constructor.
Double_t fUrbanF2
Urban model: oscillator strength second level.
std::map< Double_t, Double_t > fStoppingProton
E [GeV] -> <-dE/dx> [GeV*g/cm^2].
void SetUrbanParameters(Double_t z)
Calculate the parameters for the Urban model.
Double_t fUrbanF1
Urban model: oscillator strength first level.
std::map< Double_t, Double_t > fLandauWidth
q [e] -> width [e]
Double_t fUrbanEmax
Urban model: cut-off energy (delta-e threshold)
virtual ~CbmStsPhysics()
const Double_t kSiCharge
Silicon charge [e].
Definition CbmStsDefs.h:23
const Double_t kSiDensity
Silicon density [g/cm^3].
Definition CbmStsDefs.h:27
const Double_t kProtonMass
Proton mass [GeV].
Definition CbmStsDefs.h:31