Garfield++ 4.0
A toolkit for the detailed simulation of particle detectors based on ionisation measurement in gases and semiconductors
Loading...
Searching...
No Matches
TrackPAI.cc
Go to the documentation of this file.
1#include <algorithm>
2#include <cmath>
3#include <fstream>
4#include <iostream>
5
8#include "Garfield/Random.hh"
9#include "Garfield/Sensor.hh"
10#include "Garfield/TrackPAI.hh"
11
12namespace Garfield {
13
14TrackPAI::TrackPAI() : Track() { m_className = "TrackPAI"; }
15
16bool TrackPAI::NewTrack(const double x0, const double y0, const double z0,
17 const double t0, const double dx0, const double dy0,
18 const double dz0) {
19 m_ready = false;
20
21 // Make sure the sensor has been set.
22 if (!m_sensor) {
23 std::cerr << m_className << "::NewTrack: Sensor is not defined.\n";
24 return false;
25 }
26
27 // Get the medium at this location and check if it is "ionisable".
28 Medium* medium = nullptr;
29 if (!m_sensor->GetMedium(x0, y0, z0, medium)) {
30 std::cerr << m_className << "::NewTrack: No medium at initial position.\n";
31 return false;
32 }
33 if (!medium->IsIonisable()) {
34 std::cerr << m_className << "::NewTrack:\n"
35 << " Medium at initial position is not ionisable.\n";
36 return false;
37 }
38
39 if (medium->GetName() != m_mediumName ||
40 medium->GetNumberDensity() != m_mediumDensity) {
41 m_isChanged = true;
42 if (!SetupMedium(medium)) {
43 std::cerr << m_className << "::NewTrack:\n Properties of medium "
44 << medium->GetName() << " are not available.\n";
45 return false;
46 }
47 m_mediumName = medium->GetName();
48 m_mediumDensity = medium->GetNumberDensity();
49 }
50
51 m_ready = true;
52
53 if (m_isChanged) {
54 if (!SetupCrossSectionTable()) {
55 std::cerr << m_className << "::NewTrack:\n"
56 << " Calculation of ionisation cross-section failed.\n";
57 m_ready = false;
58 return false;
59 }
60 m_isChanged = false;
61 }
62
63 m_x = x0;
64 m_y = y0;
65 m_z = z0;
66 m_t = t0;
67 const double d = sqrt(dx0 * dx0 + dy0 * dy0 + dz0 * dz0);
68 if (d < Small) {
69 if (m_debug) {
70 std::cout << m_className << "::NewTrack:\n"
71 << " Direction vector has zero norm.\n"
72 << " Initial direction is randomized.\n";
73 }
74 RndmDirection(m_dx, m_dy, m_dz);
75 } else {
76 // Normalize the direction vector.
77 m_dx = dx0 / d;
78 m_dy = dy0 / d;
79 m_dz = dz0 / d;
80 }
81 return true;
82}
83
84bool TrackPAI::GetCluster(double& xcls, double& ycls, double& zcls,
85 double& tcls, int& ncls, double& edep,
86 double& extra) {
87 ncls = 0;
88 edep = extra = 0.;
89
90 // Clear the stack.
91 m_electrons.clear();
92 m_holes.clear();
93
94 if (!m_ready) {
95 std::cerr << m_className << "::GetCluster:\n";
96 std::cerr << " Track not initialized. Call NewTrack first.\n";
97 return false;
98 }
99
100 if (m_isChanged) {
101 if (SetupCrossSectionTable()) {
102 m_isChanged = false;
103 } else {
104 std::cerr << m_className << "::GetCluster:\n";
105 std::cerr << " Calculation of ionisation cross-section failed.\n";
106 return false;
107 }
108 }
109
110 // Draw a step length and propagate the particle.
111 const double d = -m_imfp * log(RndmUniformPos());
112 m_x += d * m_dx;
113 m_y += d * m_dy;
114 m_z += d * m_dz;
115 m_t += d / m_speed;
116
117 // Check the medium at this location.
118 Medium* medium = nullptr;
119 if (!m_sensor->GetMedium(m_x, m_y, m_z, medium)) {
120 m_ready = false;
121 return false;
122 }
123 if (medium->GetName() != m_mediumName ||
124 medium->GetNumberDensity() != m_mediumDensity || !medium->IsIonisable()) {
125 m_ready = false;
126 return false;
127 }
128
129 // Check if the particle is still inside the drift area.
130 if (!m_sensor->IsInArea(m_x, m_y, m_z)) {
131 m_ready = false;
132 return false;
133 }
134
135 xcls = m_x;
136 ycls = m_y;
137 zcls = m_z;
138 tcls = m_t;
139
140 // Sample the energy deposition.
141 double f = 0.;
142 edep = SampleEnergyDeposit(RndmUniform(), f);
143 // Update the particle energy.
144 m_e -= edep;
145
146 // Number of electron/hole (or electron/ion pairs) produced.
147 ncls = 1;
148
149 if (m_debug) {
150 std::cout << m_className << "::GetCluster:\n";
151 std::cout << " Fraction of Rutherford scattering: " << f << "\n";
152 }
153 return true;
154}
155
156double TrackPAI::SampleEnergyDeposit(const double u, double& f) const {
157 if (u > m_cdf.back()) {
158 // Use the free-electron differential cross-section.
159 f = 1.;
160 return SampleAsymptoticCs(u);
161 }
162
163 if (u <= m_cdf[0]) return m_energies[0];
164 if (u >= 1.) return m_energies.back();
165
166 // Find the energy loss by interpolation
167 // from the cumulative distribution table.
168 const auto begin = m_cdf.cbegin();
169 const auto it1 = std::upper_bound(begin, m_cdf.cend(), u);
170 if (it1 == m_cdf.cbegin()) return m_energies[0];
171 const auto it0 = std::prev(it1);
172 const double c0 = *it0;
173 const double c1 = *it1;
174 const double e0 = m_energies[it0 - begin];
175 const double e1 = m_energies[it1 - begin];
176 const double r0 = m_rutherford[it0 - begin];
177 const double r1 = m_rutherford[it1 - begin];
178 if (e0 < 100.) {
179 const double edep = e0 + (u - c0) * (e1 - e0) / (c1 - c0);
180 f = r0 + (edep - e0) * (r1 - r0) / (e1 - e0);
181 return edep;
182 }
183 const double loge0 = log(e0);
184 const double loge1 = log(e1);
185 const double logc0 = log(c0);
186 const double logc1 = log(c1);
187 double edep = loge0 + (log(u) - logc0) * (loge1 - loge0) / (logc1 - logc0);
188 f = r0 + (log(edep) - loge0) * (r1 - r0) / (loge1 - loge0);
189 edep = exp(edep);
190 return edep;
191}
192
193bool TrackPAI::SetupMedium(Medium* medium) {
194 // Make sure that the medium is defined.
195 if (!medium) {
196 std::cerr << m_className << "::SetupMedium: Null pointer.\n";
197 return false;
198 }
199
200 // Get the density and effective Z.
201 m_electronDensity = medium->GetNumberDensity() * medium->GetAtomicNumber();
202 if (m_electronDensity <= 0.) {
203 std::cerr << m_className << "::SetupMedium:\n"
204 << " Unphysical electron density (" << m_electronDensity
205 << ")\n";
206 return false;
207 }
208
209 // Get the dielectric function.
210 double emin, emax;
211 if (!medium->GetOpticalDataRange(emin, emax)) {
212 std::cerr << m_className << "::SetupMedium:\n";
213 std::cerr << " Could not load optical data for medium " << m_mediumName
214 << ".\n";
215 return false;
216 }
217
218 // Make sure the minimum energy is positive.
219 if (emin < Small) emin = Small;
220
221 // Reset the arrays.
222 m_energies.clear();
223 m_opticalDataTable.clear();
224 opticalData newEpsilon;
225
226 // Use logarithmically spaced energy steps.
227 const double r = pow(emax / emin, 1. / double(m_nSteps));
228 double eps1, eps2;
229
230 double eC = 0.5 * emin * (1. + r);
231 for (int i = 0; i < m_nSteps; ++i) {
232 medium->GetDielectricFunction(eC, eps1, eps2);
233 newEpsilon.eps1 = eps1;
234 newEpsilon.eps2 = eps2;
235 m_opticalDataTable.push_back(newEpsilon);
236 m_energies.push_back(eC);
237 eC *= r;
238 }
239
240 // Compute the integral of loss function times energy.
241 m_opticalDataTable[0].integral = 0.;
242 double integral = 0.;
243 double f1 = m_energies[0] * LossFunction(m_opticalDataTable[0].eps1,
244 m_opticalDataTable[0].eps2);
245 double f2 = f1;
246 for (int i = 1; i < m_nSteps; ++i) {
247 f2 = m_energies[i] *
248 LossFunction(m_opticalDataTable[i].eps1, m_opticalDataTable[i].eps2);
249 const double eM = 0.5 * (m_energies[i - 1] + m_energies[i]);
250 medium->GetDielectricFunction(eM, eps1, eps2);
251 const double fM = eM * LossFunction(eps1, eps2);
252 // Simpson's rule
253 integral += (f1 + 4 * fM + f2) * (m_energies[i] - m_energies[i - 1]) / 6.;
254 m_opticalDataTable[i].integral = integral;
255 f1 = f2;
256 }
257
258 // Check the consistency of the optical data by means of the TRK sum rule
259 const double trk = 2 * Pi2 * FineStructureConstant * pow(HbarC, 3) *
260 m_electronDensity / ElectronMass;
261 if (fabs(integral - trk) > 0.2 * trk) {
262 std::cerr << m_className << "::SetupMedium:\n";
263 std::cerr << " Deviation from Thomas-Reiche-Kuhn sum rule by > 20%.\n";
264 std::cerr << " Optical data are probably incomplete or erroneous!\n";
265 }
266
267 return true;
268}
269
271 if (!m_ready) {
272 std::cerr << m_className << "::GetClusterDensity:\n";
273 std::cerr << " Track has not been initialized.\n";
274 return 0.;
275 }
276
277 if (m_isChanged) {
278 if (SetupCrossSectionTable()) {
279 m_isChanged = false;
280 } else {
281 std::cerr << m_className << "::GetClusterDensity:\n";
282 std::cerr << " Ionisation cross-section could not be calculated.\n";
283 return 0.;
284 }
285 }
286
287 return 1. / m_imfp;
288}
289
291 if (!m_ready) {
292 std::cerr << m_className << "::GetStoppingPower:\n";
293 std::cerr << " Track has not been initialised.\n";
294 return 0.;
295 }
296
297 if (m_isChanged) {
298 if (SetupCrossSectionTable()) {
299 m_isChanged = false;
300 } else {
301 std::cerr << m_className << "::GetStoppingPower:\n";
302 std::cerr << " Ionisation cross-section could not be calculated.\n";
303 return 0.;
304 }
305 }
306
307 return m_dedx;
308}
309
310bool TrackPAI::SetupCrossSectionTable() {
311 if (!m_ready) {
312 std::cerr << m_className << "::SetupCrossSectionTable:\n"
313 << " Medium not set up.\n";
314 return false;
315 }
316
317 const double c1 = 2. * Pi2 * FineStructureConstant * pow(HbarC, 3) *
318 m_electronDensity / ElectronMass;
319 const double c2 = m_q * m_q * FineStructureConstant / (m_beta2 * Pi * HbarC);
320
321 // Get the max. allowed energy transfer.
322 m_emax = ComputeMaxTransfer();
323
324 std::ofstream outfile;
325 if (m_debug) outfile.open("dcs.txt", std::ios::out);
326
327 // Compute the differential cross-section.
328 std::vector<double> dcs;
329 m_rutherford.clear();
330
331 for (int i = 0; i < m_nSteps; ++i) {
332 // Define shorthand variables for photon energy and dielectric function.
333 const double egamma = m_energies[i];
334 const double eps1 = m_opticalDataTable[i].eps1;
335 const double eps2 = m_opticalDataTable[i].eps2;
336 const double integral = m_opticalDataTable[i].integral;
337
338 // First, calculate the distant-collision terms.
339 double dcsLog = 0., dcsDensity = 0., dcsCher = 0.;
340 if (eps2 > 0.) {
341 // Normal case (loss function > 0).
342 const double lf = LossFunction(eps1, eps2);
343 // Non-relativistic logarithmic term.
344 dcsLog = lf * log(2 * ElectronMass * m_beta2 / egamma);
345 // Relativistic logarithmic term (density effect)
346 const double u = 1. - m_beta2 * eps1;
347 const double v = m_beta2 * eps2;
348 dcsDensity = -0.5 * lf * log(u * u + v * v);
349 // "Cerenkov" term
350 dcsCher = (m_beta2 - eps1 / (eps1 * eps1 + eps2 * eps2)) *
351 (HalfPi - atan(u / v));
352 } else if (eps1 > 1. / m_beta2) {
353 // Imaginary part is zero, only the Cerenkov term contributes.
354 dcsCher = Pi * (m_beta2 - 1. / eps1);
355 }
356
357 // Calculate the close-collision term (quasi-free scattering)
358 double dcsRuth = 0.;
359 double f = 0.;
360 if (egamma > 0. && integral > 0.) {
361 dcsRuth = integral / (egamma * egamma);
362 f = dcsRuth / (dcsLog + dcsDensity + dcsCher);
363 }
364 m_rutherford.push_back(f);
365 dcs.push_back(dcsLog + dcsDensity + dcsCher + dcsRuth);
366 // If requested, write the cross-section terms to file.
367 if (m_debug) {
368 outfile << egamma << " " << eps1 << " " << eps2 << " " << dcsLog * c2
369 << " " << dcsDensity * c2 << " " << dcsCher * c2 << " "
370 << dcsRuth * c2 << "\n";
371 }
372 }
373 if (m_debug) outfile.close();
374
375 // Compute the cumulative distribution,
376 // total cross-section and stopping power.
377 m_cdf.clear();
378 m_cdf.push_back(0.);
379 m_dedx = 0.;
380 double cs = 0.;
381 for (int i = 1; i < m_nSteps; ++i) {
382 const double e0 = m_energies[i - 1];
383 const double e1 = m_energies[i];
384 const double de = e1 - e0;
385 const double dcs0 = dcs[i - 1];
386 const double dcs1 = dcs[i];
387 cs += 0.5 * (dcs0 + dcs1) * de;
388 m_cdf.push_back(cs);
389 m_dedx += 0.5 * (dcs0 * e0 + dcs1 * e1) * de;
390 }
391
392 // Add the contribution of high energy transfers to the stopping power
393 // and the total cross-section
394 const double elim = m_energies.back();
395 if (elim < m_emax) {
396 cs += c1 * ComputeCsTail(elim, m_emax);
397 m_dedx += c1 * ComputeDeDxTail(elim, m_emax);
398 } else {
399 std::cerr << m_className << "::SetupCrossSectionTable:\n";
400 std::cerr << " Max. energy transfer lower than optical data range.\n";
401 }
402
403 if (cs <= 0.) {
404 std::cerr << m_className << "::SetupCrossSectionTable:\n";
405 std::cerr << " Total cross-section <= 0.\n";
406 return false;
407 }
408
409 // Normalise the cumulative distribution.
410 for (int i = m_nSteps; i--;) m_cdf[i] /= cs;
411
412 cs *= c2;
413 m_dedx *= c2;
414
415 // Compute the inelastic mean free path
416 m_imfp = 1. / cs;
417
418 return true;
419}
420
421double TrackPAI::ComputeMaxTransfer() const {
422 if (m_isElectron) {
423 // Max. transfer for electrons is half the kinetic energy.
424 return 0.5 * (m_energy - m_mass);
425 }
426
427 // Other particles.
428 const double bg2 = m_beta2 / (1. - m_beta2);
429 const double mass2 = m_mass * m_mass;
430
431 return 2. * mass2 * ElectronMass * bg2 /
432 (mass2 + ElectronMass * ElectronMass + 2. * m_energy * ElectronMass);
433}
434
435double TrackPAI::ComputeCsTail(const double emin, const double emax) {
436 if (m_isElectron) {
437 // Electrons
438 const double ek = m_energy - m_mass;
439 return 1. / emin - 1. / emax - 2 * emin / (ek * ek) -
440 emin * emin / ((ek - emin) * ek * ek);
441 } else if (m_mass == ElectronMass) {
442 // Positrons
443 const double ek = m_energy - m_mass;
444 return 1. / emin - 1. / emax + 3 * (emax - emin) / (ek * ek) -
445 (emax - emin) * (ek * (emax + emin) +
446 (emin * emin + emin * emax + emax * emax) / 3.) /
447 pow(ek, 4) -
448 (2. / ek) * log(emax / emin);
449 }
450
451 switch (m_spin) {
452 case 0:
453 // Spin 0
454 return 1. / emin - 1. / emax - m_beta2 * log(emax / emin) / emax;
455 case 1:
456 // Spin 1/2
457 return 1. / emin - 1. / emax - m_beta2 * log(emax / emin) / emax +
458 (emax - emin) / (2 * m_energy * m_energy);
459 case 2: {
460 // Spin 1
461 const double e2 = 2 * m_energy * m_energy;
462 const double ec = m_mass * m_mass / ElectronMass;
463 const double a = 1. / (3 * ec);
464 const double b = (emax - emin);
465 return 1. / emin - 1. / emax + a * b * (emin + e2 + emax) / e2 -
466 m_beta2 * a * b / emax + (a - m_beta2 / emax) * log(emax / emin);
467 }
468 default:
469 break;
470 }
471 // Rutherford type cross-section
472 return 1. / emin - 1. / emax;
473}
474
475double TrackPAI::ComputeDeDxTail(const double emin, const double emax) {
476 if (m_isElectron) {
477 const double ek = m_energy - m_mass;
478 return -log(emin * (ek - emin) / (ek * ek)) +
479 (1. / (8 * (emin - ek) * ek * ek)) *
480 (-4 * pow(emin, 3) + 4 * emin * emin * ek +
481 emin * ek * ek * (17. - 16. * CLog2) +
482 pow(ek, 3) * (-9. + 16. * CLog2));
483 } else if (m_mass == ElectronMass) {
484 // Positron
485 const double ek = m_energy - m_mass;
486 return log(ek / emin) -
487 (ek - emin) * (ek - emin) *
488 (3. * emin * emin - 2. * emin * ek + 11. * ek * ek) /
489 (12. * pow(ek, 4));
490 }
491
492 switch (m_spin) {
493 case 0:
494 return log(emax / emin) - m_beta2 * (emax - emin) / emax;
495 case 1:
496 // Spin 1/2
497 return log(emax / emin) - m_beta2 * (emax - emin) / emax +
498 (emax * emax - emin * emin) / (2 * m_energy * m_energy);
499 case 2: {
500 // Spin 1
501 double e2 = m_energy * m_energy;
502 double ec = m_mass * m_mass / ElectronMass;
503 return log(emax / emin) + (pow(emax, 3) - pow(emin, 3)) / (9. * e2 * ec) +
504 (emax * emax - emin * emin) / (6. * e2) +
505 (emax - emin) *
506 (2. - (1. + emin / emax + 6 * ec / emax) * m_beta2) /
507 (6. * ec);
508 }
509 default:
510 break;
511 }
512
513 // Rutherford cross-section
514 return log(emax / emin);
515}
516
517double TrackPAI::SampleAsymptoticCs(double u) const {
518 const double emin = m_energies.back();
519 // Rescale the random number
520 u = (u - m_cdf.back()) / (1. - m_cdf.back());
521
522 if (m_isElectron) {
523 return SampleAsymptoticCsElectron(emin, u);
524 } else if (m_mass == ElectronMass) {
525 return SampleAsymptoticCsPositron(emin, u);
526 }
527
528 switch (m_spin) {
529 case 0:
530 // Spin 0
531 return SampleAsymptoticCsSpinZero(emin, u);
532 case 1:
533 // Spin 1/2
534 return SampleAsymptoticCsSpinHalf(emin, u);
535 case 2:
536 // Spin 1
537 return SampleAsymptoticCsSpinOne(emin, u);
538 default:
539 break;
540 }
541 // Rutherford cross-section (analytic inversion)
542 return emin * m_emax / (u * emin + (1. - u) * m_emax);
543}
544
545double TrackPAI::SampleAsymptoticCsSpinZero(const double emin, double u) const {
546 const double a = emin / m_emax;
547 const double b = m_beta2 * a;
548 u *= (1. - a + b * log(a));
549 double eLow = emin, eUp = m_emax;
550 while (eUp - eLow > 1.) {
551 const double eM = 0.5 * (eUp + eLow);
552 if (u >= 1. - emin / eM - b * log(eM / emin)) {
553 eLow = eM;
554 } else {
555 eUp = eM;
556 }
557 }
558
559 return 0.5 * (eLow + eUp);
560}
561
562double TrackPAI::SampleAsymptoticCsSpinHalf(const double emin, double u) const {
563 const double a = emin / m_emax;
564 const double b = m_beta2 * a;
565 const double c = emin / (2. * m_energy * m_energy);
566 u *= 1. - a + b * log(a) + (m_emax - emin) * c;
567 double eLow = emin, eUp = m_emax;
568 while (eUp - eLow > 1.) {
569 const double eM = 0.5 * (eUp + eLow);
570 if (u >= 1. - emin / eM - b * log(eM / emin) + (eM - emin) * c) {
571 eLow = eM;
572 } else {
573 eUp = eM;
574 }
575 }
576
577 return 0.5 * (eLow + eUp);
578}
579
580double TrackPAI::SampleAsymptoticCsSpinOne(const double emin, double u) const {
581 const double e2 = 2 * m_energy * m_energy;
582 const double ec = m_mass * m_mass / ElectronMass;
583 const double a = 2 * ec / e2 - m_beta2 / m_emax;
584 const double b = 1.5 * ec / emin;
585 const double c = 1. - 1.5 * ec * m_beta2 / m_emax;
586 u *= (m_emax - emin) * (0.5 * (emin + m_emax) / e2 + a + b / m_emax) +
587 c * log(m_emax / emin);
588 double eLow = emin, eUp = m_emax;
589 while (eUp - eLow > 1.) {
590 const double eM = 0.5 * (eUp + eLow);
591 if (u >=
592 (eM - emin) * ((emin + eM) / e2 + a + b / eM) + c * log(eM / emin)) {
593 eLow = eM;
594 } else {
595 eUp = eM;
596 }
597 }
598
599 return 0.5 * (eLow + eUp);
600}
601
602double TrackPAI::SampleAsymptoticCsElectron(const double emin, double u) const {
603 const double ek = m_energy - m_mass;
604 const double ek2 = ek * ek;
605 const double a = ek / (emin * (ek - emin));
606 const double norm = 1. / emin - 0.5 / ek - emin * emin / ((ek - emin) * ek2) -
607 2. * emin / ek2;
608 u *= norm;
609 double eLow = emin, eUp = m_emax;
610 while (eUp - eLow > 1.) {
611 const double eM = 0.5 * (eUp + eLow);
612 if (u >= a - 1. / eM + (eM - emin) / ek2 + 1. / (ek - eM)) {
613 eLow = eM;
614 } else {
615 eUp = eM;
616 }
617 }
618 return 0.5 * (eLow + eUp);
619}
620
621double TrackPAI::SampleAsymptoticCsPositron(const double emin, double u) const {
622 const double ek = m_energy - m_mass;
623 const double ek2 = ek * ek;
624 const double ek3 = ek2 * ek;
625 const double ek4 = 3 * ek3 * ek;
626 const double emin2 = emin * emin;
627 const double a = 1. / emin;
628 const double b = 3. / ek2;
629 const double c = 2. / ek;
630 u *= 1. / emin - 1. / m_emax + 3 * (m_emax - emin) / ek2 -
631 (m_emax - emin) * (m_emax + emin) / ek3 +
632 (m_emax - emin) * (emin * emin + emin * m_emax + m_emax * m_emax) / ek4 -
633 (2. / ek) * log(m_emax / emin);
634 double eLow = emin, eUp = m_emax;
635 while (eUp - eLow > 1.) {
636 const double eM = 0.5 * (eUp + eLow);
637 const double eM2 = eM * eM;
638 if (u >= a - 1. / eM + b * (eM - emin) - (eM2 - emin2) / ek3 +
639 (eM - emin) * (emin2 + emin * eM + eM2) / ek4 -
640 c * log(eM / emin)) {
641 eLow = eM;
642 } else {
643 eUp = eM;
644 }
645 }
646
647 return 0.5 * (eLow + eUp);
648}
649}
Abstract base class for media.
Definition: Medium.hh:13
virtual double GetNumberDensity() const
Get the number density [cm-3].
Definition: Medium.hh:60
const std::string & GetName() const
Get the medium name/identifier.
Definition: Medium.hh:23
bool IsIonisable() const
Is charge deposition by charged particles/photon enabled in this medium?
Definition: Medium.hh:78
bool IsInArea(const double x, const double y, const double z)
Check if a point is inside the user area.
Definition: Sensor.cc:254
bool GetMedium(const double x, const double y, const double z, Medium *&medium)
Get the medium at (x, y, z).
Definition: Sensor.cc:159
virtual bool GetCluster(double &xcls, double &ycls, double &zcls, double &tcls, int &ncls, double &ecls, double &extra)
Definition: TrackPAI.cc:84
virtual double GetStoppingPower()
Get the stopping power (mean energy loss [eV] per cm).
Definition: TrackPAI.cc:290
virtual bool NewTrack(const double x0, const double y0, const double z0, const double t0, const double dx0, const double dy0, const double dz0)
Definition: TrackPAI.cc:16
virtual double GetClusterDensity()
Definition: TrackPAI.cc:270
Abstract base class for track generation.
Definition: Track.hh:14
Sensor * m_sensor
Definition: Track.hh:112
bool m_debug
Definition: Track.hh:118
bool m_isElectron
Definition: Track.hh:109
bool m_isChanged
Definition: Track.hh:114
double m_q
Definition: Track.hh:104
double m_beta2
Definition: Track.hh:108
std::string m_className
Definition: Track.hh:102
double m_mass
Definition: Track.hh:106
double m_energy
Definition: Track.hh:107
double RndmUniform()
Draw a random number uniformly distributed in the range [0, 1).
Definition: Random.hh:14
void RndmDirection(double &dx, double &dy, double &dz, const double length=1.)
Draw a random (isotropic) direction vector.
Definition: Random.hh:107
double RndmUniformPos()
Draw a random number uniformly distributed in the range (0, 1).
Definition: Random.hh:17
DoubleAc pow(const DoubleAc &f, double p)
Definition: DoubleAc.cpp:337
DoubleAc exp(const DoubleAc &f)
Definition: DoubleAc.cpp:377
DoubleAc fabs(const DoubleAc &f)
Definition: DoubleAc.h:615