Garfield++ v1r0
A toolkit for the detailed simulation of particle detectors based on ionisation measurement in gases and semiconductors
Loading...
Searching...
No Matches
TrackElectron.cc
Go to the documentation of this file.
1#include <iostream>
2#include <cmath>
3
4#include "Sensor.hh"
5#include "TrackElectron.hh"
8#include "Random.hh"
9
10namespace Garfield {
11
13 : ready(false),
14 x(0.),
15 y(0.),
16 z(0.),
17 t(0.),
18 dx(0.),
19 dy(0),
20 dz(1.),
21 nComponents(0),
22 nElectrons(0),
23 mediumName(""),
24 mediumDensity(0.),
25 mfp(0.) {
26
27 className = "TrackElectron";
28
29 // Setup the particle properties.
30 q = -1;
31 spin = 1;
32 mass = ElectronMass;
33 isElectron = true;
34 SetBetaGamma(3.);
35 particleName = "electron";
36
37 components.clear();
38 electrons.clear();
39}
40
41void TrackElectron::SetParticle(std::string particle) {
42
43 if (particle != "electron" && particle != "e" && particle != "e-") {
44 std::cerr << className << "::SetParticle:\n";
45 std::cerr << " Only electrons can be transported.\n";
46 }
47}
48
49bool TrackElectron::NewTrack(const double x0, const double y0, const double z0,
50 const double t0, const double dx0,
51 const double dy0, const double dz0) {
52
53 ready = false;
54
55 // Make sure the sensor has been set.
56 if (sensor == 0) {
57 std::cerr << className << "::NewTrack:\n";
58 std::cerr << " Sensor is not defined.\n";
59 return false;
60 }
61
62 // Get the medium at this location and check if it is "ionisable".
63 Medium* medium = 0;
64 if (!sensor->GetMedium(x0, y0, z0, medium)) {
65 std::cerr << className << "::NewTrack:\n";
66 std::cerr << " No medium at initial position.\n";
67 return false;
68 }
69 if (!medium->IsIonisable()) {
70 std::cerr << className << "::NewTrack:\n";
71 std::cerr << " Medium at initial position is not ionisable.\n";
72 return false;
73 }
74
75 // Check if the medium is a gas.
76 if (!medium->IsGas()) {
77 std::cerr << className << "::NewTrack:\n";
78 std::cerr << " Medium at initial position is not a gas.\n";
79 return false;
80 }
81
82 if (!SetupGas(medium)) {
83 std::cerr << className << "::NewTrack:\n";
84 std::cerr << " Properties of medium " << medium->GetName()
85 << " are not available.\n";
86 return false;
87 }
88
89 if (!UpdateCrossSection()) {
90 std::cerr << className << "::NewTrack:\n";
91 std::cerr << " Cross-sections could not be calculated.\n";
92 return false;
93 }
94
95 mediumName = medium->GetName();
96
97 x = x0;
98 y = y0;
99 z = z0;
100 t = t0;
101 const double dd = sqrt(dx0 * dx0 + dy0 * dy0 + dz0 * dz0);
102 if (dd < Small) {
103 if (debug) {
104 std::cout << className << "::NewTrack:\n";
105 std::cout << " Direction vector has zero norm.\n";
106 std::cout << " Initial direction is randomized.\n";
107 }
108 const double ctheta = 1. - 2. * RndmUniform();
109 const double stheta = sqrt(1. - ctheta * ctheta);
110 const double phi = TwoPi * RndmUniform();
111 dx = cos(phi) * stheta;
112 dy = sin(phi) * stheta;
113 dz = ctheta;
114 } else {
115 // Normalize the direction vector.
116 dx = dx0 / dd;
117 dy = dy0 / dd;
118 dz = dz0 / dd;
119 }
120
121 ready = true;
122 return true;
123}
124
125bool TrackElectron::GetCluster(double& xcls, double& ycls, double& zcls,
126 double& tcls, int& ncls, double& edep,
127 double& extra) {
128
129 edep = extra = 0.;
130 ncls = 0;
131
132 nElectrons = 0;
133 electrons.clear();
134
135 if (!ready) {
136 std::cerr << className << "::GetCluster:\n";
137 std::cerr << " Track not initialized.\n";
138 std::cerr << " Call NewTrack first.\n";
139 return false;
140 }
141
142 // Draw a step length and propagate the electron.
143 const double d = -mfp * log(RndmUniformPos());
144 x += d * dx;
145 y += d * dy;
146 z += d * dz;
147 t += d / (sqrt(beta2) * SpeedOfLight);
148
149 if (!sensor->IsInArea(x, y, z)) {
150 ready = false;
151 return false;
152 }
153
154 Medium* medium = 0;
155 if (!sensor->GetMedium(x, y, z, medium)) {
156 ready = false;
157 return false;
158 }
159
160 if (medium->GetName() != mediumName ||
161 medium->GetNumberDensity() != mediumDensity || !medium->IsIonisable()) {
162 ready = false;
163 return false;
164 }
165
166 xcls = x;
167 ycls = y;
168 zcls = z;
169 tcls = t;
170 const double r = RndmUniform();
171 int iComponent = 0;
172 for (int i = 0; i < nComponents; ++i) {
173 if (r <= RndmUniform()) {
174 iComponent = i;
175 break;
176 }
177 }
178
179 // Sample secondary electron energy according to
180 // Opal-Beaty-Peterson splitting function.
181 const double e0 = ElectronMass * (sqrt(1. / (1. - beta2)) - 1.);
182 double esec = components[iComponent].wSplit *
183 tan(RndmUniform() * atan((e0 - components[iComponent].ethr) /
184 (2. * components[iComponent].wSplit)));
185 esec = components[iComponent].wSplit *
186 pow(esec / components[iComponent].wSplit, 0.9524);
187 nElectrons = 1;
188 electrons.resize(nElectrons);
189 electrons[0].energy = esec;
190 electrons[0].x = xcls;
191 electrons[0].y = ycls;
192 electrons[0].z = zcls;
193
194 ncls = nElectrons;
195 edep = esec;
196
197 return true;
198}
199
201
202 if (!ready) {
203 std::cerr << className << "::GetClusterDensity:\n";
204 std::cerr << " Track has not been initialized.\n";
205 return 0.;
206 }
207
208 if (mfp <= 0.) {
209 std::cerr << className << "::GetClusterDensity:\n";
210 std::cerr << " Mean free path is not available.\n";
211 return 0.;
212 }
213
214 return 1. / mfp;
215}
216
218
219 if (!ready) {
220 std::cerr << className << "::GetStoppingPower:\n";
221 std::cerr << " Track has not been initialised.\n";
222 return 0.;
223 }
224
225 const double prefactor = 4 * Pi * pow(HbarC / ElectronMass, 2);
226 const double lnBg2 = log(beta2 / (1. - beta2));
227
228 double dedx = 0.;
229 // Primary energy
230 const double e0 = ElectronMass * (sqrt(1. / (1. - beta2)) - 1.);
231 for (int i = nComponents; i--;) {
232 // Calculate the mean number of clusters per cm.
233 const double cmean =
234 mediumDensity * components[i].fraction * (prefactor / beta2) *
235 (components[i].m2Ion * (lnBg2 - beta2) + components[i].cIon);
236 const double ew = (e0 - components[i].ethr) / (2 * components[i].wSplit);
237 // Calculate the mean secondary electron energy.
238 const double emean =
239 (components[i].wSplit / (2 * atan(ew))) * log(1. + ew * ew);
240 dedx += cmean * emean;
241 }
242
243 return dedx;
244}
245
246bool TrackElectron::SetupGas(Medium* gas) {
247
248 nComponents = 0;
249 components.clear();
250
251 if (gas == 0) {
252 std::cerr << className << "::SetupGas:\n";
253 std::cerr << " Medium is not defined.\n";
254 return false;
255 }
256
257 mediumDensity = gas->GetNumberDensity();
258 nComponents = gas->GetNumberOfComponents();
259 if (nComponents <= 0) {
260 std::cerr << className << "::SetupGas:\n";
261 std::cerr << " Medium composition is not defined.\n";
262 nComponents = 0;
263 return false;
264 }
265 components.resize(nComponents);
266
267 // Density correction parameters from
268 // R. M. Sternheimer, M. J. Berger, S. M. Seltzer,
269 // Atomic Data and Nuclear Data Tables 30 (1984), 261-271
270 bool ok = true;
271 for (int i = nComponents; i--;) {
272 std::string gasname = "";
273 double frac = 0.;
274 gas->GetComponent(i, gasname, frac);
275 components[i].fraction = frac;
276 components[i].p = 0.;
277 if (gasname == "CF4") {
278 components[i].m2Ion = 7.2;
279 components[i].cIon = 93.;
280 components[i].x0Dens = 1.;
281 components[i].x1Dens = 0.;
282 components[i].cDens = 0.;
283 components[i].aDens = 0.;
284 components[i].mDens = 0.;
285 components[i].ethr = 15.9;
286 components[i].wSplit = 19.5;
287 } else if (gasname == "Ar") {
288 components[i].m2Ion = 3.593;
289 components[i].cIon = 39.7;
290 components[i].x0Dens = 1.7635;
291 components[i].x1Dens = 4.4855;
292 components[i].cDens = 11.9480;
293 components[i].aDens = 0.19714;
294 components[i].mDens = 2.9618;
295 components[i].ethr = 15.75961;
296 components[i].wSplit = 15.;
297 } else if (gasname == "He") {
298 components[i].m2Ion = 0.489;
299 components[i].cIon = 5.5;
300 components[i].x0Dens = 2.2017;
301 components[i].x1Dens = 3.6122;
302 components[i].cDens = 11.1393;
303 components[i].aDens = 0.13443;
304 components[i].mDens = 5.8347;
305 components[i].ethr = 24.58739;
306 components[i].wSplit = 10.5;
307 } else if (gasname == "He-3") {
308 components[i].m2Ion = 0.489;
309 components[i].cIon = 5.5;
310 components[i].x0Dens = 2.2017;
311 components[i].x1Dens = 3.6122;
312 components[i].cDens = 11.1393;
313 components[i].aDens = 0.13443;
314 components[i].mDens = 5.8347;
315 components[i].ethr = 24.58739;
316 components[i].wSplit = 10.5;
317 } else if (gasname == "Ne") {
318 components[i].m2Ion = 1.69;
319 components[i].cIon = 17.8;
320 components[i].x0Dens = 2.0735;
321 components[i].x1Dens = 4.6421;
322 components[i].cDens = 11.9041;
323 components[i].aDens = 0.08064;
324 components[i].mDens = 3.5771;
325 components[i].ethr = 21.56454;
326 components[i].wSplit = 19.5;
327 } else if (gasname == "Kr") {
328 components[i].m2Ion = 5.5;
329 components[i].cIon = 56.9;
330 components[i].x0Dens = 1.7158;
331 components[i].x1Dens = 5.0748;
332 components[i].cDens = 12.5115;
333 components[i].aDens = 0.07446;
334 components[i].mDens = 3.4051;
335 components[i].ethr = 13.996;
336 components[i].wSplit = 21.;
337 } else if (gasname == "Xe") {
338 components[i].m2Ion = 8.04;
339 components[i].cIon = 75.25;
340 components[i].x0Dens = 1.5630;
341 components[i].x1Dens = 4.7371;
342 components[i].cDens = 12.7281;
343 components[i].aDens = 0.23314;
344 components[i].mDens = 2.7414;
345 components[i].ethr = 12.129843;
346 components[i].wSplit = 23.7;
347 } else if (gasname == "CH4") {
348 components[i].m2Ion = 3.75;
349 components[i].cIon = 42.5;
350 components[i].x0Dens = 1.6263;
351 components[i].x1Dens = 3.9716;
352 components[i].cDens = 9.5243;
353 components[i].aDens = 0.09253;
354 components[i].mDens = 3.6257;
355 components[i].ethr = 12.65;
356 components[i].wSplit = 8.;
357 } else if (gasname == "iC4H10") {
358 components[i].m2Ion = 15.5;
359 components[i].cIon = 160.;
360 components[i].x0Dens = 1.3788;
361 components[i].x1Dens = 3.7524;
362 components[i].cDens = 8.5633;
363 components[i].aDens = 0.10852;
364 components[i].mDens = 3.4884;
365 components[i].ethr = 10.67;
366 components[i].wSplit = 7.;
367 } else if (gasname == "CO2") {
368 components[i].m2Ion = 5.6;
369 components[i].cIon = 57.91;
370 components[i].x0Dens = 1.6294;
371 components[i].x1Dens = 4.1825;
372 components[i].aDens = 0.11768;
373 components[i].mDens = 3.3227;
374 components[i].ethr = 13.777;
375 components[i].wSplit = 13.;
376 } else if (gasname == "N2") {
377 components[i].m2Ion = 3.35;
378 components[i].cIon = 38.1;
379 components[i].x0Dens = 1.7378;
380 components[i].x1Dens = 4.1323;
381 components[i].cDens = 10.5400;
382 components[i].aDens = 0.15349;
383 components[i].mDens = 3.2125;
384 components[i].ethr = 15.581;
385 components[i].wSplit = 13.8;
386 } else {
387 std::cerr << className << "::SetupGas:\n";
388 std::cerr << " Cross-section for " << gasname
389 << " is not available.\n";
390 ok = false;
391 }
392 }
393
394 if (!ok) {
395 nComponents = 0;
396 components.clear();
397 }
398
399 return true;
400}
401
402bool TrackElectron::UpdateCrossSection() {
403
404 const double prefactor = 4 * Pi * pow(HbarC / ElectronMass, 2);
405 const double lnBg2 = log(beta2 / (1. - beta2));
406 // Parameter X in the Sternheimer fit formula
407 const double eta = mediumDensity / LoschmidtNumber;
408 const double x = 0.5 * (lnBg2 + log(eta)) / log(10.);
409 double csSum = 0.;
410 for (int i = nComponents; i--;) {
411 double delta = 0.;
412 if (components[i].x0Dens < components[i].x1Dens &&
413 x >= components[i].x0Dens) {
414 delta = 2 * log(10.) * x - components[i].cDens;
415 if (x < components[i].x1Dens) {
416 delta += components[i].aDens *
417 pow(components[i].x1Dens - x, components[i].mDens);
418 }
419 }
420 double cs =
421 (components[i].fraction * prefactor / beta2) *
422 (components[i].m2Ion * (lnBg2 - beta2 - delta) + components[i].cIon);
423 components[i].p = cs;
424 csSum += cs;
425 }
426
427 if (csSum <= 0.) {
428 std::cerr << className << "::UpdateCrossSection:\n";
429 std::cerr << " Total cross-section <= 0.\n";
430 return false;
431 }
432
433 mfp = 1. / (csSum * mediumDensity);
434
435 for (int i = 0; i < nComponents; ++i) {
436 components[i].p /= csSum;
437 if (i > 0) components[i].p += components[i - 1].p;
438 }
439
440 return true;
441}
442}
DoubleAc cos(const DoubleAc &f)
Definition: DoubleAc.cpp:431
DoubleAc pow(const DoubleAc &f, double p)
Definition: DoubleAc.cpp:336
DoubleAc sqrt(const DoubleAc &f)
Definition: DoubleAc.cpp:313
DoubleAc sin(const DoubleAc &f)
Definition: DoubleAc.cpp:383
virtual double GetNumberDensity() const
Definition: Medium.hh:47
virtual void GetComponent(const unsigned int &i, std::string &label, double &f)
Definition: Medium.cc:155
unsigned int GetNumberOfComponents() const
Definition: Medium.hh:37
virtual bool IsGas() const
Definition: Medium.hh:23
bool IsIonisable() const
Definition: Medium.hh:59
std::string GetName() const
Definition: Medium.hh:22
bool IsInArea(const double x, const double y, const double z)
Definition: Sensor.cc:254
bool GetMedium(const double x, const double y, const double z, Medium *&medium)
Definition: Sensor.cc:141
bool NewTrack(const double x0, const double y0, const double z0, const double t0, const double dx0, const double dy0, const double dz0)
bool GetCluster(double &xcls, double &ycls, double &zcls, double &tcls, int &ncls, double &ecls, double &extra)
void SetParticle(std::string particle)
void SetBetaGamma(const double bg)
Definition: Track.cc:116
std::string className
Definition: Track.hh:61
double mass
Definition: Track.hh:65
std::string particleName
Definition: Track.hh:69
double q
Definition: Track.hh:63
double beta2
Definition: Track.hh:67
bool isElectron
Definition: Track.hh:68
bool debug
Definition: Track.hh:78
Sensor * sensor
Definition: Track.hh:71
double RndmUniform()
Definition: Random.hh:16
double RndmUniformPos()
Definition: Random.hh:19