Garfield++ v1r0
A toolkit for the detailed simulation of particle detectors based on ionisation measurement in gases and semiconductors
Loading...
Searching...
No Matches
AvalancheMicroscopic.cc
Go to the documentation of this file.
1#include <iostream>
2#include <cmath>
3#include <string>
4
8#include "Random.hh"
9
10namespace Garfield {
11
13 : m_sensor(NULL),
14 m_nPhotons(0),
15 m_nElectrons(0),
16 m_nHoles(0),
17 m_nIons(0),
18 m_nElectronEndpoints(0),
19 m_nHoleEndpoints(0),
20 m_usePlotting(false),
21 m_viewer(NULL),
22 m_plotExcitations(true),
23 m_plotIonisations(true),
24 m_plotAttachments(true),
25 m_histElectronEnergy(NULL),
26 m_histHoleEnergy(NULL),
27 m_hasElectronEnergyHistogram(false),
28 m_hasHoleEnergyHistogram(false),
29 m_histDistance(NULL),
30 m_hasDistanceHistogram(false),
31 m_distanceOption('r'),
32 m_histSecondary(NULL),
33 m_hasSecondaryHistogram(false),
34 m_useSignal(false),
35 m_useInducedCharge(false),
36 m_useDriftLines(false),
37 m_usePhotons(false),
38 m_useBandStructureDefault(true),
39 m_useNullCollisionSteps(false),
40 m_useBfield(false),
41 m_rb11(1.),
42 m_rb12(0.),
43 m_rb13(0.),
44 m_rb21(0.),
45 m_rb22(1.),
46 m_rb23(0.),
47 m_rb31(0.),
48 m_rb32(0.),
49 m_rb33(1.),
50 m_rx22(1.),
51 m_rx23(0.),
52 m_rx32(0.),
53 m_rx33(1.),
54 m_deltaCut(0.),
55 m_gammaCut(0.),
56 m_sizeCut(-1),
57 m_nCollSkip(100),
58 m_hasTimeWindow(false),
59 m_tMin(0.),
60 m_tMax(0.),
61 m_hasUserHandleStep(false),
62 m_hasUserHandleAttachment(false),
63 m_hasUserHandleInelastic(false),
64 m_hasUserHandleIonisation(false),
65 m_userHandleStep(0),
66 m_userHandleAttachment(0),
67 m_userHandleInelastic(0),
68 m_userHandleIonisation(0),
69 m_debug(false) {
70
71 m_className = "AvalancheMicroscopic";
72
73 m_stack.reserve(1000);
74 m_endpointsElectrons.reserve(1000);
75 m_endpointsHoles.reserve(1000);
76 m_photons.reserve(100);
77
78}
79
81
82 if (!s) {
83 std::cerr << m_className << "::SetSensor:\n";
84 std::cerr << " Sensor pointer is a null pointer.\n";
85 return;
86 }
87 m_sensor = s;
88}
89
91
92 if (!view) {
93 std::cerr << m_className << "::EnablePlotting:\n";
94 std::cerr << " Viewer pointer is a null pointer.\n";
95 return;
96 }
97
98 m_viewer = view;
99 m_usePlotting = true;
100 if (!m_useDriftLines) {
101 std::cout << m_className << "::EnablePlotting:\n";
102 std::cout << " Enabling storage of drift line.\n";
104 }
105}
106
108
109 m_viewer = NULL;
110 m_usePlotting = false;
111}
112
114
115 if (!histo) {
116 std::cerr << m_className << "::EnableElectronEnergyHistogramming:\n";
117 std::cerr << " Histogram pointer is a null pointer.\n";
118 return;
119 }
120
121 m_histElectronEnergy = histo;
122 m_hasElectronEnergyHistogram = true;
123}
124
126
127 m_hasElectronEnergyHistogram = false;
128}
129
131
132 if (!histo) {
133 std::cerr << m_className << "::EnableHoleEnergyHistogramming:\n";
134 std::cerr << " Histogram pointer is a null pointer.\n";
135 return;
136 }
137
138 m_histHoleEnergy = histo;
139 m_hasHoleEnergyHistogram = true;
140}
141
143
144 m_hasHoleEnergyHistogram = false;
145}
146
147void AvalancheMicroscopic::SetDistanceHistogram(TH1* histo, const char opt) {
148
149 if (!histo) {
150 std::cerr << m_className << "::SetDistanceHistogram:\n";
151 std::cerr << " Histogram pointer is a null pointer.\n";
152 return;
153 }
154
155 m_histDistance = histo;
156 m_hasDistanceHistogram = true;
157
158 if (opt == 'x' || opt == 'y' || opt == 'z' || opt == 'r') {
159 m_distanceOption = opt;
160 } else {
161 std::cerr << m_className << "::SetDistanceHistogram:";
162 std::cerr << " Unknown option " << opt << ".\n";
163 std::cerr << " Valid options are x, y, z, r.\n";
164 std::cerr << " Using default value (r).\n";
165 m_distanceOption = 'r';
166 }
167
168 if (m_distanceHistogramType.empty()) {
169 std::cout << m_className << "::SetDistanceHistogram:\n";
170 std::cout << " Don't forget to call EnableDistanceHistogramming.\n";
171 }
172}
173
175
176 // Check if this type of collision is already registered
177 // for histogramming.
178 const unsigned int nDistanceHistogramTypes = m_distanceHistogramType.size();
179 if (nDistanceHistogramTypes > 0) {
180 for (int i = nDistanceHistogramTypes; i--;) {
181 if (m_distanceHistogramType[i] == type) {
182 std::cout << m_className << "::EnableDistanceHistogramming:\n";
183 std::cout << " Collision type " << type
184 << " is already histogrammed.\n";
185 return;
186 }
187 }
188 }
189
190 m_distanceHistogramType.push_back(type);
191 std::cout << m_className << "::EnableDistanceHistogramming:\n";
192 std::cout << " Histogramming of collision type " << type << " enabled.\n";
193 if (!m_hasDistanceHistogram) {
194 std::cout << " Don't forget to set the histogram.\n";
195 }
196}
197
199
200 if (m_distanceHistogramType.empty()) {
201 std::cerr << m_className << "::DisableDistanceHistogramming:\n";
202 std::cerr << " Collision type " << type << " is not histogrammed.\n";
203 return;
204 }
205 const unsigned int nDistanceHistogramTypes = m_distanceHistogramType.size();
206 for (int i = nDistanceHistogramTypes; i--;) {
207 if (m_distanceHistogramType[i] == type) {
208 m_distanceHistogramType.erase(m_distanceHistogramType.begin() + i);
209 std::cout << " Histogramming of collision type " << type
210 << " disabled.\n";
211 return;
212 }
213 }
214
215 std::cerr << m_className << "::DisableDistanceHistogramming:\n";
216 std::cerr << " Collision type " << type << " is not histogrammed.\n";
217}
218
220
221 m_hasDistanceHistogram = false;
222 m_distanceHistogramType.clear();
223}
224
226
227 if (!histo) {
228 std::cerr << m_className << "::EnableSecondaryEnergyHistogramming:\n";
229 std::cerr << " Histogram pointer is a null pointer.\n";
230 return;
231 }
232
233 m_histSecondary = histo;
234 m_hasSecondaryHistogram = true;
235}
236
238
239 m_hasSecondaryHistogram = false;
240}
241
243
244 if (n <= 0) {
245 std::cerr << m_className << "::SetCollisionSteps:\n";
246 std::cerr << " Number of collisions to be skipped set to"
247 << " default value (100).\n";
248 m_nCollSkip = 100;
249 return;
250 }
251
252 m_nCollSkip = n;
253}
254
255void AvalancheMicroscopic::SetTimeWindow(const double t0, const double t1) {
256
257 if (fabs(t1 - t0) < Small) {
258 std::cerr << m_className << "::SetTimeWindow:\n";
259 std::cerr << " Time interval must be greater than zero.\n";
260 return;
261 }
262
263 m_tMin = std::min(t0, t1);
264 m_tMax = std::max(t0, t1);
265 m_hasTimeWindow = true;
266}
267
268void AvalancheMicroscopic::UnsetTimeWindow() { m_hasTimeWindow = false; }
269
270void AvalancheMicroscopic::GetElectronEndpoint(const unsigned int i, double& x0,
271 double& y0, double& z0,
272 double& t0, double& e0,
273 double& x1, double& y1,
274 double& z1, double& t1,
275 double& e1, int& status) const {
276
277 if (i >= m_nElectronEndpoints) {
278 std::cerr << m_className << "::GetElectronEndpoint:\n";
279 std::cerr << " Endpoint index " << i << " out of range.\n";
280 x0 = y0 = z0 = t0 = e0 = 0.;
281 x1 = y1 = z1 = t1 = e1 = 0.;
282 status = 0;
283 return;
284 }
285
286 x0 = m_endpointsElectrons[i].x0;
287 y0 = m_endpointsElectrons[i].y0;
288 z0 = m_endpointsElectrons[i].z0;
289 t0 = m_endpointsElectrons[i].t0;
290 e0 = m_endpointsElectrons[i].e0;
291 x1 = m_endpointsElectrons[i].x;
292 y1 = m_endpointsElectrons[i].y;
293 z1 = m_endpointsElectrons[i].z;
294 t1 = m_endpointsElectrons[i].t;
295 e1 = m_endpointsElectrons[i].energy;
296 status = m_endpointsElectrons[i].status;
297}
298
300 const unsigned int i, double& x0, double& y0, double& z0, double& t0, double& e0,
301 double& x1, double& y1, double& z1, double& t1, double& e1, double& dx1,
302 double& dy1, double& dz1, int& status) const {
303
304 if (i >= m_nElectronEndpoints) {
305 std::cerr << m_className << "::GetElectronEndpoint:\n";
306 std::cerr << " Endpoint index " << i << " out of range.\n";
307 x0 = y0 = z0 = t0 = e0 = 0.;
308 x1 = y1 = z1 = t1 = e1 = 0.;
309 dx1 = dy1 = dz1 = 0.;
310 status = 0;
311 return;
312 }
313
314 x0 = m_endpointsElectrons[i].x0;
315 y0 = m_endpointsElectrons[i].y0;
316 z0 = m_endpointsElectrons[i].z0;
317 t0 = m_endpointsElectrons[i].t0;
318 e0 = m_endpointsElectrons[i].e0;
319 x1 = m_endpointsElectrons[i].x;
320 y1 = m_endpointsElectrons[i].y;
321 z1 = m_endpointsElectrons[i].z;
322 t1 = m_endpointsElectrons[i].t;
323 e1 = m_endpointsElectrons[i].energy;
324 dx1 = m_endpointsElectrons[i].kx;
325 dy1 = m_endpointsElectrons[i].ky;
326 dz1 = m_endpointsElectrons[i].kz;
327 status = m_endpointsElectrons[i].status;
328}
329
330void AvalancheMicroscopic::GetHoleEndpoint(const unsigned int i, double& x0, double& y0,
331 double& z0, double& t0, double& e0,
332 double& x1, double& y1, double& z1,
333 double& t1, double& e1,
334 int& status) const {
335
336 if (i >= m_nHoleEndpoints) {
337 std::cerr << m_className << "::GetHoleEndpoint:\n";
338 std::cerr << " Endpoint index " << i << " out of range.\n";
339 x0 = y0 = z0 = t0 = e0 = 0.;
340 x1 = y1 = z1 = t1 = e1 = 0.;
341 status = 0;
342 return;
343 }
344
345 x0 = m_endpointsHoles[i].x0;
346 y0 = m_endpointsHoles[i].y0;
347 z0 = m_endpointsHoles[i].z0;
348 t0 = m_endpointsHoles[i].t0;
349 e0 = m_endpointsHoles[i].e0;
350 x1 = m_endpointsHoles[i].x;
351 y1 = m_endpointsHoles[i].y;
352 z1 = m_endpointsHoles[i].z;
353 t1 = m_endpointsHoles[i].t;
354 e1 = m_endpointsHoles[i].energy;
355 status = m_endpointsHoles[i].status;
356}
357
359 const {
360
361 if (i >= m_nElectronEndpoints) {
362 std::cerr << m_className << "::GetNumberOfElectronDriftLinePoints:\n";
363 std::cerr << " Endpoint index (" << i << ") out of range.\n";
364 return 0;
365 }
366
367 if (!m_useDriftLines) return 2;
368
369 return m_endpointsElectrons[i].driftLine.size() + 2;
370}
371
372unsigned int AvalancheMicroscopic::GetNumberOfHoleDriftLinePoints(const unsigned int i) const {
373
374 if (i >= m_nHoleEndpoints) {
375 std::cerr << m_className << "::GetNumberOfHoleDriftLinePoints:\n";
376 std::cerr << " Endpoint index (" << i << ") out of range.\n";
377 return 0;
378 }
379
380 if (!m_useDriftLines) return 2;
381
382 return m_endpointsHoles[i].driftLine.size() + 2;
383}
384
386 double& z, double& t,
387 const int ip,
388 const unsigned int iel) const {
389
390 if (iel >= m_nElectronEndpoints) {
391 std::cerr << m_className << "::GetElectronDriftLinePoint:\n";
392 std::cerr << " Endpoint index (" << iel << ") out of range.\n";
393 return;
394 }
395
396 if (ip <= 0) {
397 x = m_endpointsElectrons[iel].x0;
398 y = m_endpointsElectrons[iel].y0;
399 z = m_endpointsElectrons[iel].z0;
400 t = m_endpointsElectrons[iel].t0;
401 return;
402 }
403
404 const int np = m_endpointsElectrons[iel].driftLine.size();
405 if (ip > np) {
406 x = m_endpointsElectrons[iel].x;
407 y = m_endpointsElectrons[iel].y;
408 z = m_endpointsElectrons[iel].z;
409 t = m_endpointsElectrons[iel].t;
410 return;
411 }
412
413 x = m_endpointsElectrons[iel].driftLine[ip - 1].x;
414 y = m_endpointsElectrons[iel].driftLine[ip - 1].y;
415 z = m_endpointsElectrons[iel].driftLine[ip - 1].z;
416 t = m_endpointsElectrons[iel].driftLine[ip - 1].t;
417}
418
420 double& z, double& t,
421 const int ip,
422 const unsigned int ih) const {
423
424 if (ih >= m_nHoleEndpoints) {
425 std::cerr << m_className << "::GetHoleDriftLinePoint:\n";
426 std::cerr << " Endpoint index (" << ih << ") out of range.\n";
427 return;
428 }
429
430 if (ip <= 0) {
431 x = m_endpointsHoles[ih].x0;
432 y = m_endpointsHoles[ih].y0;
433 z = m_endpointsHoles[ih].z0;
434 t = m_endpointsHoles[ih].t0;
435 return;
436 }
437
438 const int np = m_endpointsHoles[ih].driftLine.size();
439 if (ip > np) {
440 x = m_endpointsHoles[ih].x;
441 y = m_endpointsHoles[ih].y;
442 z = m_endpointsHoles[ih].z;
443 t = m_endpointsHoles[ih].t;
444 return;
445 }
446
447 x = m_endpointsHoles[ih].driftLine[ip - 1].x;
448 y = m_endpointsHoles[ih].driftLine[ip - 1].y;
449 z = m_endpointsHoles[ih].driftLine[ip - 1].z;
450 t = m_endpointsHoles[ih].driftLine[ip - 1].t;
451}
452
453void AvalancheMicroscopic::GetPhoton(const unsigned int i, double& e, double& x0,
454 double& y0, double& z0, double& t0,
455 double& x1, double& y1, double& z1,
456 double& t1, int& status) const {
457
458 if (i >= m_nPhotons) {
459 std::cerr << m_className << "::GetPhoton:\n";
460 std::cerr << " Photon " << i << " does not exist.\n";
461 return;
462 }
463
464 x0 = m_photons[i].x0;
465 x1 = m_photons[i].x1;
466 y0 = m_photons[i].y0;
467 y1 = m_photons[i].y1;
468 z0 = m_photons[i].z0;
469 z1 = m_photons[i].z1;
470 t0 = m_photons[i].t0;
471 t1 = m_photons[i].t1;
472 status = m_photons[i].status;
473 e = m_photons[i].energy;
474}
475
477 void (*f)(double x, double y, double z, double t, double e, double dx,
478 double dy, double dz, bool hole)) {
479
480 if (!f) {
481 std::cerr << m_className << "::SetUserHandleStep:\n";
482 std::cerr << " Function pointer is a null pointer.\n";
483 return;
484 }
485 m_userHandleStep = f;
486 m_hasUserHandleStep = true;
487}
488
490
491 m_userHandleStep = 0;
492 m_hasUserHandleStep = false;
493}
494
496 double x, double y, double z, double t, int type, int level, Medium* m)) {
497
498 m_userHandleAttachment = f;
499 m_hasUserHandleAttachment = true;
500}
501
503
504 m_userHandleAttachment = 0;
505 m_hasUserHandleAttachment = false;
506}
507
509 double x, double y, double z, double t, int type, int level, Medium* m)) {
510
511 m_userHandleInelastic = f;
512 m_hasUserHandleInelastic = true;
513}
514
516
517 m_userHandleInelastic = 0;
518 m_hasUserHandleInelastic = false;
519}
520
522 double x, double y, double z, double t, int type, int level, Medium* m)) {
523
524 m_userHandleIonisation = f;
525 m_hasUserHandleIonisation = true;
526}
527
529
530 m_userHandleIonisation = 0;
531 m_hasUserHandleIonisation = false;
532}
533
534bool AvalancheMicroscopic::DriftElectron(const double x0, const double y0,
535 const double z0, const double t0,
536 const double e0, const double dx0,
537 const double dy0, const double dz0) {
538
539 // Clear the list of electrons and photons.
540 m_endpointsElectrons.clear();
541 m_endpointsHoles.clear();
542 m_photons.clear();
543
544 // Reset the particle counters.
545 m_nPhotons = m_nElectrons = m_nHoles = m_nIons = 0;
546 m_nElectronEndpoints = m_nHoleEndpoints = 0;
547
548 return TransportElectron(x0, y0, z0, t0, e0, dx0, dy0, dz0, false, false);
549}
550
551bool AvalancheMicroscopic::AvalancheElectron(const double x0, const double y0,
552 const double z0, const double t0,
553 const double e0, const double dx0,
554 const double dy0,
555 const double dz0) {
556
557 // Clear the list of electrons, holes and photons.
558 m_endpointsElectrons.clear();
559 m_endpointsHoles.clear();
560 m_photons.clear();
561
562 // Reset the particle counters.
563 m_nPhotons = m_nElectrons = m_nHoles = m_nIons = 0;
564 m_nElectronEndpoints = m_nHoleEndpoints = 0;
565
566 return TransportElectron(x0, y0, z0, t0, e0, dx0, dy0, dz0, true, false);
567}
568
569bool AvalancheMicroscopic::TransportElectron(const double x0, const double y0,
570 const double z0, const double t0,
571 const double e0, const double dx0,
572 const double dy0, const double dz0,
573 const bool aval, bool hole) {
574
575 // Make sure that the sensor is defined.
576 if (!m_sensor) {
577 std::cerr << m_className << "::TransportElectron:\n";
578 std::cerr << " Sensor is not defined.\n";
579 return false;
580 }
581
582 // Make sure that the starting point is inside a medium.
583 Medium* medium = NULL;
584 if (!m_sensor->GetMedium(x0, y0, z0, medium)) {
585 std::cerr << m_className << "::TransportElectron:\n";
586 std::cerr << " No medium at initial position.\n";
587 return false;
588 }
589 if (!medium) {
590 std::cerr << m_className << "::TransportElectron:\n";
591 std::cerr << " No medium at initial position.\n";
592 return false;
593 }
594
595 // Make sure that the medium is "driftable" and microscopic.
596 if (!medium->IsDriftable() || !medium->IsMicroscopic()) {
597 std::cerr << m_className << "::TransportElectron:\n";
598 std::cerr << " Medium at initial position does not provide "
599 << " microscopic tracking data.\n";
600 return false;
601 }
602
603 // If the medium is a semiconductor, use "band structure" stepping.
604 bool m_useBandStructure = m_useBandStructureDefault;
605 if (medium->IsSemiconductor() && m_useBandStructureDefault) {
606 m_useBandStructure = true;
607 } else {
608 m_useBandStructure = false;
609 }
610 if (m_debug) {
611 std::cout << m_className << "::TransportElectron:\n";
612 std::cout << " Starting to drift in medium " << medium->GetName()
613 << ".\n";
614 }
615
616 // Get the id number of the drift medium.
617 int id = medium->GetId();
618
619 // Numerical prefactors in equation of motion
620 const double c1 = SpeedOfLight * sqrt(2. / ElectronMass);
621 const double c2 = c1 * c1 / 4.;
622
623 // Temporary stack of photons produced in the de-excitation cascade.
624 std::vector<double> stackPhotonsTime;
625 std::vector<double> stackPhotonsEnergy;
626
627 // Electric and magnetic field
628 double ex = 0., ey = 0., ez = 0., emag = 0.;
629 double bx = 0., by = 0., bz = 0., bmag = 0.;
630 int status = 0;
631 // Cyclotron frequency
632 double cwt = 1., swt = 0.;
633 double wb = 0.;
634 // Flag indicating if magnetic field is usable
635 bool bOk = true;
636
637 // Current position, direction, velocity and energy
638 double x = x0, y = y0, z = z0, t = t0;
639 double kx = dx0, ky = dy0, kz = dz0;
640 double vx = dx0, vy = dy0, vz = dz0;
641 double energy = e0;
642 // Index of the conduction band (irrelevant for gases)
643 int band = -1;
644
645 // Timestep
646 double dt = 0.;
647 // Direction, velocity and energy after a step
648 double newKx = 0., newKy = 0., newKz = 0.;
649 double newVx = 0., newVy = 0., newVz = 0.;
650 double newEnergy = 0.;
651 // Collision type (elastic, ionisation, attachment, inelastic, ...)
652 int cstype;
653 // Cross-section term
654 int level;
655
656 // Number of secondaries
657 int nion = 0, ndxc = 0;
658
659 // Random number
660 double r;
661 // Numerical factors
662 double a1 = 0., a2 = 0., a3 = 0., a4 = 0.;
663
664 // Clear the stack.
665 m_stack.clear();
666 // Add the initial electron to the stack.
667 electron newElectron;
668 newElectron.status = 0;
669 if (hole) {
670 newElectron.hole = true;
671 } else {
672 newElectron.hole = false;
673 }
674 newElectron.x0 = x0;
675 newElectron.x = x0;
676 newElectron.y0 = y0;
677 newElectron.y = y0;
678 newElectron.z0 = z0;
679 newElectron.z = z0;
680 newElectron.t0 = t0;
681 newElectron.t = t0;
682 newElectron.kx = dx0;
683 newElectron.ky = dy0;
684 newElectron.kz = dz0;
685 newElectron.e0 = std::max(e0, Small);
686 newElectron.energy = newElectron.e0;
687 newElectron.band = band;
688 // Previous coordinates for distance histogramming.
689 newElectron.xLast = x0;
690 newElectron.yLast = y0;
691 newElectron.zLast = z0;
692 newElectron.driftLine.clear();
693 m_stack.push_back(newElectron);
694 if (hole) {
695 ++m_nHoles;
696 } else {
697 ++m_nElectrons;
698 }
699
700 if (m_useBandStructure) {
701 // With band structure, (kx, ky, kz) represents the momentum.
702 // No normalization in this case.
703 medium->GetElectronMomentum(std::max(e0, Small), kx, ky, kz, band);
704 m_stack[0].kx = kx;
705 m_stack[0].ky = ky;
706 m_stack[0].kz = kz;
707 m_stack[0].band = band;
708 } else {
709 m_stack[0].band = 0;
710 band = 0;
711 // Check the given initial direction.
712 const double k = sqrt(dx0 * dx0 + dy0 * dy0 + dz0 * dz0);
713 if (fabs(k) < Small) {
714 // Direction has zero norm, draw a random direction.
715 const double phi = TwoPi * RndmUniform();
716 const double ctheta = 2 * RndmUniform() - 1.;
717 const double stheta = sqrt(1. - ctheta * ctheta);
718 m_stack[0].kx = cos(phi) * stheta;
719 m_stack[0].ky = sin(phi) * stheta;
720 m_stack[0].kz = ctheta;
721 } else {
722 // Normalise the direction to 1.
723 m_stack[0].kx /= k;
724 m_stack[0].ky /= k;
725 m_stack[0].kz /= k;
726 }
727 }
728
729 // Get the null-collision rate.
730 double fLim = medium->GetElectronNullCollisionRate(band);
731 if (fLim <= 0.) {
732 std::cerr << m_className << "::TransportElectron:\n";
733 std::cerr << " Got null-collision rate <= 0.\n";
734 return false;
735 }
736
737 // Status flag
738 bool ok = true;
739 while (1) {
740 // If the list of electrons/holes is exhausted, we're done.
741 const int nSize = m_stack.size();
742 if (nSize <= 0) break;
743 // Loop over all electrons/holes in the avalanche.
744 for (int iE = nSize; iE--;) {
745 // Get an electron/hole from the stack.
746 x = m_stack[iE].x;
747 y = m_stack[iE].y;
748 z = m_stack[iE].z;
749 t = m_stack[iE].t;
750 energy = m_stack[iE].energy;
751 band = m_stack[iE].band;
752 kx = m_stack[iE].kx;
753 ky = m_stack[iE].ky;
754 kz = m_stack[iE].kz;
755 hole = m_stack[iE].hole;
756
757 ok = true;
758
759 // Count number of collisions between updates.
760 int nCollTemp = 0;
761
762 // Get the local electric field and medium.
763 m_sensor->ElectricField(x, y, z, ex, ey, ez, medium, status);
764 // Sign change for electrons.
765 if (!hole) {
766 ex = -ex;
767 ey = -ey;
768 ez = -ez;
769 }
770
771 if (m_debug) {
772 std::cout << m_className << "::TransportElectron:\n";
773 if (hole) {
774 std::cout << " Drifting hole " << iE << ".\n";
775 } else {
776 std::cout << " Drifting electron " << iE << ".\n";
777 }
778 std::cout << " Field [V/cm] at (" << x << ", " << y << ", " << z
779 << "): " << ex << ", " << ey << ", " << ez << "\n";
780 std::cout << " Status: " << status << "\n";
781 std::cout << " Medium: " << medium->GetName() << "\n";
782 }
783
784 if (status != 0) {
785 // Electron/hole is not inside a drift medium.
786 m_stack[iE].x = x;
787 m_stack[iE].y = y;
788 m_stack[iE].z = z;
789 m_stack[iE].t = t;
790 m_stack[iE].energy = energy;
791 m_stack[iE].band = band;
792 m_stack[iE].kx = kx;
793 m_stack[iE].ky = ky;
794 m_stack[iE].kz = kz;
795 m_stack[iE].status = StatusLeftDriftMedium;
796 if (hole) {
797 m_endpointsHoles.push_back(m_stack[iE]);
798 } else {
799 m_endpointsElectrons.push_back(m_stack[iE]);
800 }
801 m_stack.erase(m_stack.begin() + iE);
802 if (m_debug) {
803 std::cout << m_className << "::TransportElectron:\n";
804 if (hole) {
805 std::cout << " Hole left the drift medium.\n";
806 } else {
807 std::cout << " Electron left the drift medium.\n";
808 }
809 std::cout << " At " << x << ", " << y << "," << z << "\n";
810 }
811 continue;
812 }
813
814 // If switched on, get the local magnetic field.
815 if (m_useBfield) {
816 m_sensor->MagneticField(x, y, z, bx, by, bz, status);
817 if (hole) {
818 bx *= Tesla2Internal;
819 by *= Tesla2Internal;
820 bz *= Tesla2Internal;
821 } else {
822 bx *= -Tesla2Internal;
823 by *= -Tesla2Internal;
824 bz *= -Tesla2Internal;
825 }
826 // Make sure that neither E nor B are zero.
827 bmag = sqrt(bx * bx + by * by + bz * bz);
828 emag = sqrt(ex * ex + ey * ey + ez * ez);
829 if (bmag > Small && emag > Small)
830 bOk = true;
831 else
832 bOk = false;
833 }
834
835 // Trace the electron/hole.
836 while (1) {
837
838 bool isNullCollision = false;
839
840 // Make sure the electron energy exceeds the transport cut.
841 if (energy < m_deltaCut) {
842 m_stack[iE].x = x;
843 m_stack[iE].y = y;
844 m_stack[iE].z = z;
845 m_stack[iE].t = t;
846 m_stack[iE].energy = energy;
847 m_stack[iE].band = band;
848 m_stack[iE].kx = kx;
849 m_stack[iE].ky = ky;
850 m_stack[iE].kz = kz;
851 m_stack[iE].status = StatusBelowTransportCut;
852 if (hole) {
853 m_endpointsHoles.push_back(m_stack[iE]);
854 } else {
855 m_endpointsElectrons.push_back(m_stack[iE]);
856 }
857 m_stack.erase(m_stack.begin() + iE);
858 if (m_debug) {
859 std::cout << m_className << "::TransportElectron:\n";
860 std::cout << " Kinetic energy (" << energy << ")"
861 << " below transport cut.\n";
862 }
863 ok = false;
864 break;
865 }
866
867 // Fill the energy distribution histogram.
868 if (hole && m_hasHoleEnergyHistogram) {
869 m_histHoleEnergy->Fill(energy);
870 } else if (!hole && m_hasElectronEnergyHistogram) {
871 m_histElectronEnergy->Fill(energy);
872 }
873
874 // Check if the electrons is within the specified time window.
875 if (m_hasTimeWindow && (t < m_tMin || t > m_tMax)) {
876 m_stack[iE].x = x;
877 m_stack[iE].y = y;
878 m_stack[iE].z = z;
879 m_stack[iE].t = t;
880 m_stack[iE].energy = energy;
881 m_stack[iE].band = band;
882 m_stack[iE].kx = kx;
883 m_stack[iE].ky = ky;
884 m_stack[iE].kz = kz;
885 m_stack[iE].status = StatusOutsideTimeWindow;
886 if (hole) {
887 m_endpointsHoles.push_back(m_stack[iE]);
888 } else {
889 m_endpointsElectrons.push_back(m_stack[iE]);
890 }
891 m_stack.erase(m_stack.begin() + iE);
892 if (m_debug) {
893 std::cout << m_className << "::TransportElectron:\n";
894 if (hole) {
895 std::cout << " Hole left the time window.\n";
896 } else {
897 std::cout << " Electron left the time window.\n";
898 }
899 std::cout << " Time: " << t << "\n";
900 }
901 ok = false;
902 break;
903 }
904
905 if (medium->GetId() != id) {
906 // Medium has changed.
907 if (!medium->IsMicroscopic()) {
908 // Electron/hole has left the microscopic drift medium.
909 m_stack[iE].x = x;
910 m_stack[iE].y = y;
911 m_stack[iE].z = z;
912 m_stack[iE].t = t;
913 m_stack[iE].energy = energy;
914 m_stack[iE].band = band;
915 m_stack[iE].kx = kx;
916 m_stack[iE].ky = ky;
917 m_stack[iE].kz = kz;
918 m_stack[iE].status = StatusLeftDriftMedium;
919 if (hole) {
920 m_endpointsHoles.push_back(m_stack[iE]);
921 } else {
922 m_endpointsElectrons.push_back(m_stack[iE]);
923 }
924 m_stack.erase(m_stack.begin() + iE);
925 ok = false;
926 if (m_debug) {
927 std::cout << m_className << "::TransportElectron:\n";
928 std::cout << " Medium at " << x << ", " << y << ", " << z
929 << " does not have microscopic data.\n";
930 }
931 break;
932 }
933 id = medium->GetId();
934 if (medium->IsSemiconductor() && m_useBandStructureDefault) {
935 m_useBandStructure = true;
936 } else {
937 m_useBandStructure = false;
938 }
939 // Update the null-collision rate.
941 if (fLim <= 0.) {
942 std::cerr << m_className << "::TransportElectron:\n";
943 std::cerr << " Got null-collision rate <= 0.\n";
944 return false;
945 }
946 }
947
948 if (m_useBfield && bOk) {
949 // Calculate the cyclotron frequency.
950 wb = OmegaCyclotronOverB * bmag;
951 // Rotate the direction vector into the local coordinate system.
952 ComputeRotationMatrix(bx, by, bz, bmag, ex, ey, ez);
953 RotateGlobal2Local(kx, ky, kz);
954 // Calculate the electric field in the rotated system.
955 RotateGlobal2Local(ex, ey, ez);
956 // Calculate the velocity vector in the local frame.
957 const double v = c1 * sqrt(energy);
958 vx = v * kx;
959 vy = v * ky;
960 vz = v * kz;
961 a1 = vx * ex;
962 a2 = c2 * ex * ex;
963 a3 = ez / bmag - vy;
964 a4 = (ez / wb);
965 } else if (m_useBandStructure) {
966 energy = medium->GetElectronEnergy(kx, ky, kz, vx, vy, vz, band);
967 } else {
968 // No band structure, no magnetic field.
969 // Calculate the velocity vector.
970 const double v = c1 * sqrt(energy);
971 vx = v * kx;
972 vy = v * ky;
973 vz = v * kz;
974
975 a1 = vx * ex + vy * ey + vz * ez;
976 a2 = c2 * (ex * ex + ey * ey + ez * ez);
977 }
978
979 if (m_hasUserHandleStep) {
980 m_userHandleStep(x, y, z, t, energy, kx, ky, kz, hole);
981 }
982
983 // Determine the timestep.
984 dt = 0.;
985 while (1) {
986 // Sample the flight time.
987 r = RndmUniformPos();
988 dt += -log(r) / fLim;
989 // Calculate the energy after the proposed step.
990 if (m_useBfield && bOk) {
991 cwt = cos(wb * dt);
992 swt = sin(wb * dt);
993 newEnergy = std::max(energy + (a1 + a2 * dt) * dt +
994 a4 * (a3 * (1. - cwt) + vz * swt),
995 Small);
996 } else if (m_useBandStructure) {
997 newEnergy = std::max(
999 kx + ex * dt * SpeedOfLight, ky + ey * dt * SpeedOfLight,
1000 kz + ez * dt * SpeedOfLight, newVx, newVy, newVz, band),
1001 Small);
1002 } else {
1003 newEnergy = std::max(energy + (a1 + a2 * dt) * dt, Small);
1004 }
1005 // Get the real collision rate at the updated energy.
1006 double fReal = medium->GetElectronCollisionRate(newEnergy, band);
1007 if (fReal <= 0.) {
1008 std::cerr << m_className << "::TransportElectron:\n";
1009 std::cerr << " Got collision rate <= 0.\n";
1010 std::cerr << " At " << newEnergy << " eV (band " << band
1011 << ").\n";
1012 return false;
1013 }
1014 if (fReal > fLim) {
1015 // Real collision rate is higher than null-collision rate.
1016 dt += log(r) / fLim;
1017 // Increase the null collision rate and try again.
1018 std::cerr << m_className << "::TransportElectron:\n";
1019 std::cerr << " Increasing null-collision rate by 5%.\n";
1020 if (m_useBandStructure) std::cerr << " Band " << band << "\n";
1021 fLim *= 1.05;
1022 continue;
1023 }
1024 // Check for real or null collision.
1025 if (RndmUniform() <= fReal / fLim) break;
1026 if (m_useNullCollisionSteps) {
1027 isNullCollision = true;
1028 break;
1029 }
1030 }
1031 if (!ok) break;
1032
1033 // Increase the collision counter.
1034 ++nCollTemp;
1035
1036 // Update the directions (at instant before collision)
1037 // and calculate the proposed new position.
1038 if (m_useBfield && bOk) {
1039 // Calculate the new velocity.
1040 newVx = vx + 2. * c2 * ex * dt;
1041 newVy = vz * swt - a3 * cwt + ez / bmag;
1042 newVz = vz * cwt + a3 * swt;
1043 // Normalise and rotate back to the lab frame.
1044 const double v = sqrt(newVx * newVx + newVy * newVy + newVz * newVz);
1045 newKx = newVx / v;
1046 newKy = newVy / v;
1047 newKz = newVz / v;
1048 RotateLocal2Global(newKx, newKy, newKz);
1049 // Calculate the step in coordinate space.
1050 vx += c2 * ex * dt;
1051 ky = (vz * (1. - cwt) - a3 * swt) / (wb * dt) + ez / bmag;
1052 kz = (vz * swt + a3 * (1. - cwt)) / (wb * dt);
1053 vy = ky;
1054 vz = kz;
1055 // Rotate back to the lab frame.
1056 RotateLocal2Global(vx, vy, vz);
1057 } else if (m_useBandStructure) {
1058 // Update the wave-vector.
1059 newKx = kx + ex * dt * SpeedOfLight;
1060 newKy = ky + ey * dt * SpeedOfLight;
1061 newKz = kz + ez * dt * SpeedOfLight;
1062 // Average velocity over the step.
1063 vx = 0.5 * (vx + newVx);
1064 vy = 0.5 * (vy + newVy);
1065 vz = 0.5 * (vz + newVz);
1066 } else {
1067 // Update the direction.
1068 a1 = sqrt(energy / newEnergy);
1069 a2 = 0.5 * c1 * dt / sqrt(newEnergy);
1070 newKx = kx * a1 + ex * a2;
1071 newKy = ky * a1 + ey * a2;
1072 newKz = kz * a1 + ez * a2;
1073
1074 // Calculate the step in coordinate space.
1075 a1 = c1 * sqrt(energy);
1076 a2 = dt * c2;
1077 vx = kx * a1 + ex * a2;
1078 vy = ky * a1 + ey * a2;
1079 vz = kz * a1 + ez * a2;
1080 }
1081
1082 // Get the electric field and medium at the proposed new position.
1083 m_sensor->ElectricField(x + vx * dt, y + vy * dt, z + vz * dt, ex, ey, ez,
1084 medium, status);
1085 if (!hole) {
1086 ex = -ex;
1087 ey = -ey;
1088 ez = -ez;
1089 }
1090
1091 // Check if the electron is still inside a drift medium.
1092 if (status != 0) {
1093 // Try to terminate the drift line close to the boundary
1094 // by means of iterative bisection.
1095 m_stack[iE].x = x;
1096 m_stack[iE].y = y;
1097 m_stack[iE].z = z;
1098 m_stack[iE].t = t;
1099 m_stack[iE].energy = energy;
1100 double dx = vx * dt, dy = vy * dt, dz = vz * dt;
1101 double d = sqrt(dx * dx + dy * dy + dz * dz);
1102 if (d > 0) {
1103 dx /= d;
1104 dy /= d;
1105 dz /= d;
1106 }
1107 // Mid-point
1108 double xM = x, yM = y, zM = z;
1109 while (d > BoundaryDistance) {
1110 d *= 0.5;
1111 dt *= 0.5;
1112 xM = x + d * dx;
1113 yM = y + d * dy;
1114 zM = z + d * dz;
1115 // Check if the mid-point is inside the drift medium.
1116 m_sensor->ElectricField(xM, yM, zM, ex, ey, ez, medium, status);
1117 if (status == 0) {
1118 x = xM;
1119 y = yM;
1120 z = zM;
1121 t += dt;
1122 }
1123 }
1124 // Place the endpoint OUTSIDE the drift medium
1125 x += d * dx;
1126 y += d * dy;
1127 z += d * dz;
1128 if (m_useSignal) {
1129 if (hole) {
1130 m_sensor->AddSignal(
1131 +1, m_stack[iE].t, t - m_stack[iE].t, 0.5 * (x + m_stack[iE].x),
1132 0.5 * (y + m_stack[iE].y), 0.5 * (z + m_stack[iE].z), vx, vy, vz);
1133 } else {
1134 m_sensor->AddSignal(
1135 -1, m_stack[iE].t, t - m_stack[iE].t, 0.5 * (x + m_stack[iE].x),
1136 0.5 * (y + m_stack[iE].y), 0.5 * (z + m_stack[iE].z), vx, vy, vz);
1137 }
1138 }
1139 m_stack[iE].x = x;
1140 m_stack[iE].y = y;
1141 m_stack[iE].z = z;
1142 m_stack[iE].t = t;
1143 m_stack[iE].kx = newKx;
1144 m_stack[iE].ky = newKy;
1145 m_stack[iE].kz = newKz;
1146 m_stack[iE].status = StatusLeftDriftMedium;
1147 if (hole) {
1148 m_endpointsHoles.push_back(m_stack[iE]);
1149 } else {
1150 m_endpointsElectrons.push_back(m_stack[iE]);
1151 }
1152 m_stack.erase(m_stack.begin() + iE);
1153 ok = false;
1154 if (m_debug) {
1155 std::cout << m_className << "::TransportElectron:\n";
1156 if (hole) {
1157 std::cout << " Hole left the drift medium.\n";
1158 } else {
1159 std::cout << " Electron left the drift medium.\n";
1160 }
1161 std::cout << " At " << x << ", " << y << "," << z << "\n";
1162 }
1163 break;
1164 }
1165
1166 // Check if the new position is inside the user area.
1167 if (!m_sensor->IsInArea(x + vx * dt, y + vy * dt, z + vz * dt)) {
1168 // Try to terminate the drift line close to the boundary
1169 // by means of iterative bisection.
1170 m_stack[iE].x = x;
1171 m_stack[iE].y = y;
1172 m_stack[iE].z = z;
1173 m_stack[iE].t = t;
1174 m_stack[iE].energy = energy;
1175 double dx = vx * dt, dy = vy * dt, dz = vz * dt;
1176 double d = sqrt(dx * dx + dy * dy + dz * dz);
1177 if (d > 0) {
1178 dx /= d;
1179 dy /= d;
1180 dz /= d;
1181 }
1182 // Mid-point
1183 double xM = x, yM = y, zM = z;
1184 while (d > BoundaryDistance) {
1185 d *= 0.5;
1186 dt *= 0.5;
1187 xM = x + d * dx;
1188 yM = y + d * dy;
1189 zM = z + d * dz;
1190 // Check if the mid-point is inside the drift area.
1191 if (m_sensor->IsInArea(xM, yM, zM)) {
1192 x = xM;
1193 y = yM;
1194 z = zM;
1195 t += dt;
1196 }
1197 }
1198 // Place the endpoint OUTSIDE the drift area.
1199 x += d * dx;
1200 y += d * dy;
1201 z += d * dz;
1202
1203 // If switched on, calculate the induced signal over this step.
1204 if (m_useSignal) {
1205 if (hole) {
1206 m_sensor->AddSignal(
1207 +1, m_stack[iE].t, t - m_stack[iE].t, 0.5 * (x + m_stack[iE].x),
1208 0.5 * (y + m_stack[iE].y), 0.5 * (z + m_stack[iE].z), vx, vy, vz);
1209 } else {
1210 m_sensor->AddSignal(
1211 -1, m_stack[iE].t, t - m_stack[iE].t, 0.5 * (x + m_stack[iE].x),
1212 0.5 * (y + m_stack[iE].y), 0.5 * (z + m_stack[iE].z), vx, vy, vz);
1213 }
1214 }
1215 m_stack[iE].x = x;
1216 m_stack[iE].y = y;
1217 m_stack[iE].z = z;
1218 m_stack[iE].t = t;
1219 m_stack[iE].kx = newKx;
1220 m_stack[iE].ky = newKy;
1221 m_stack[iE].kz = newKz;
1222 m_stack[iE].status = StatusLeftDriftArea;
1223 if (hole) {
1224 m_endpointsHoles.push_back(m_stack[iE]);
1225 } else {
1226 m_endpointsElectrons.push_back(m_stack[iE]);
1227 }
1228 m_stack.erase(m_stack.begin() + iE);
1229 ok = false;
1230 if (m_debug) {
1231 std::cout << m_className << "::TransportElectron:\n";
1232 if (hole) {
1233 std::cout << " Hole left the drift area.\n";
1234 } else {
1235 std::cout << " Electron left the drift area.\n";
1236 }
1237 std::cout << " At " << x << ", " << y << ", " << z << "\n";
1238 }
1239 break;
1240 }
1241
1242 // Check if the electron/hole has crossed a wire.
1243 double xCross = x, yCross = y, zCross = z;
1244 if (m_sensor->IsWireCrossed(x, y, z, x + vx * dt, y + vy * dt,
1245 z + vz * dt, xCross, yCross, zCross)) {
1246 // If switched on, calculated the induced signal over this step.
1247 if (m_useSignal) {
1248 dt = sqrt(pow(xCross - x, 2) + pow(yCross - y, 2) +
1249 pow(zCross - z, 2)) /
1250 sqrt(vx * vx + vy * vy + vz * vz);
1251 if (hole) {
1252 m_sensor->AddSignal(+1, t, dt, 0.5 * (x + xCross),
1253 0.5 * (y + yCross), 0.5 * (z + zCross), vx, vy,
1254 vz);
1255 } else {
1256 m_sensor->AddSignal(-1, t, dt, 0.5 * (x + xCross),
1257 0.5 * (y + yCross), 0.5 * (z + zCross), vx, vy,
1258 vz);
1259 }
1260 }
1261 m_stack[iE].x = xCross;
1262 m_stack[iE].y = yCross;
1263 m_stack[iE].z = zCross;
1264 m_stack[iE].t = t + dt;
1265 m_stack[iE].kx = newKx;
1266 m_stack[iE].ky = newKy;
1267 m_stack[iE].kz = newKz;
1268 m_stack[iE].status = StatusLeftDriftMedium;
1269 if (hole) {
1270 m_endpointsHoles.push_back(m_stack[iE]);
1271 } else {
1272 m_endpointsElectrons.push_back(m_stack[iE]);
1273 }
1274 m_stack.erase(m_stack.begin() + iE);
1275 ok = false;
1276 if (m_debug) {
1277 std::cout << m_className << "::TransportElectron:\n";
1278 std::cout << " Electron/hole hit a wire.\n";
1279 std::cout << " At " << x << ", " << y << "," << z << "\n";
1280 }
1281 break;
1282 }
1283
1284 // If switched on, calculate the induced signal.
1285 if (m_useSignal) {
1286 if (hole) {
1287 m_sensor->AddSignal(+1, t, dt, x + 0.5 * vx * dt, y + 0.5 * vy * dt,
1288 z + 0.5 * vz * dt, vx, vy, vz);
1289 } else {
1290 m_sensor->AddSignal(-1, t, dt, x + 0.5 * vx * dt, y + 0.5 * vy * dt,
1291 z + 0.5 * vy * dt, vx, vy, vz);
1292 }
1293 }
1294
1295 // Update the coordinates.
1296 x += vx * dt;
1297 y += vy * dt;
1298 z += vz * dt;
1299 t += dt;
1300
1301 // If switched on, get the magnetic field at the new location.
1302 if (m_useBfield) {
1303 m_sensor->MagneticField(x, y, z, bx, by, bz, status);
1304 if (hole) {
1305 bx *= Tesla2Internal;
1306 by *= Tesla2Internal;
1307 bz *= Tesla2Internal;
1308 } else {
1309 bx *= -Tesla2Internal;
1310 by *= -Tesla2Internal;
1311 bz *= -Tesla2Internal;
1312 }
1313 // Make sure that neither E nor B are zero.
1314 bmag = sqrt(bx * bx + by * by + bz * bz);
1315 emag = sqrt(ex * ex + ey * ey + ez * ez);
1316 if (bmag > Small && emag > Small)
1317 bOk = true;
1318 else
1319 bOk = false;
1320 }
1321
1322 if (isNullCollision) {
1323 energy = newEnergy;
1324 kx = newKx;
1325 ky = newKy;
1326 kz = newKz;
1327 continue;
1328 }
1329
1330 // Get the collision type and parameters.
1331 medium->GetElectronCollision(newEnergy, cstype, level, energy, newKx,
1332 newKy, newKz, nion, ndxc, band);
1333
1334 // If activated, histogram the distance with respect to the
1335 // last collision.
1336 if (m_hasDistanceHistogram && m_histDistance &&
1337 !m_distanceHistogramType.empty()) {
1338 const int nDistanceHistogramTypes = m_distanceHistogramType.size();
1339 for (int iType = nDistanceHistogramTypes; iType--;) {
1340 if (m_distanceHistogramType[iType] != cstype) continue;
1341 if (m_debug) {
1342 std::cout << m_className << "::TransportElectron:\n";
1343 std::cout << " Collision type: " << cstype << "\n";
1344 std::cout << " Fill distance histogram.\n";
1345 getchar();
1346 }
1347 switch (m_distanceOption) {
1348 case 'x':
1349 m_histDistance->Fill(m_stack[iE].xLast - x);
1350 break;
1351 case 'y':
1352 m_histDistance->Fill(m_stack[iE].yLast - y);
1353 break;
1354 case 'z':
1355 m_histDistance->Fill(m_stack[iE].zLast - z);
1356 break;
1357 case 'r':
1358 const double r2 = pow(m_stack[iE].xLast - x, 2) +
1359 pow(m_stack[iE].yLast - y, 2) +
1360 pow(m_stack[iE].zLast - z, 2);
1361 m_histDistance->Fill(sqrt(r2));
1362 break;
1363 }
1364 m_stack[iE].xLast = x;
1365 m_stack[iE].yLast = y;
1366 m_stack[iE].zLast = z;
1367 break;
1368 }
1369 }
1370
1371 switch (cstype) {
1372 // Elastic collision
1373 case ElectronCollisionTypeElastic:
1374 break;
1375 // Ionising collision
1376 case ElectronCollisionTypeIonisation:
1377 if (m_usePlotting && m_plotIonisations) {
1378 m_viewer->AddIonisationMarker(x, y, z);
1379 }
1380 if (m_hasUserHandleIonisation) {
1381 m_userHandleIonisation(x, y, z, t, cstype, level, medium);
1382 }
1383 for (int j = nion; j--;) {
1384 int itype;
1385 double esec;
1386 medium->GetIonisationProduct(j, itype, esec);
1387 if (itype == IonProdTypeElectron) {
1388 esec = std::max(esec, Small);
1389 if (m_hasSecondaryHistogram) m_histSecondary->Fill(esec);
1390 // Add the secondary electron to the stack.
1391 newElectron = m_stack[iE];
1392 newElectron.hole = false;
1393 newElectron.x0 = x;
1394 newElectron.x = x;
1395 newElectron.y0 = y;
1396 newElectron.y = y;
1397 newElectron.z0 = z;
1398 newElectron.z = z;
1399 newElectron.t0 = t;
1400 newElectron.t = t;
1401 newElectron.energy = esec;
1402 newElectron.e0 = newElectron.energy;
1403 if (m_useBandStructure) {
1404 newElectron.band = -1;
1405 medium->GetElectronMomentum(esec, newElectron.kx,
1406 newElectron.ky, newElectron.kz,
1407 newElectron.band);
1408 } else {
1409 // Randomise the secondary electron direction.
1410 const double phi = TwoPi * RndmUniform();
1411 const double ctheta = 2 * RndmUniform() - 1.;
1412 const double stheta = sqrt(1. - ctheta * ctheta);
1413 newElectron.kx = cos(phi) * stheta;
1414 newElectron.ky = sin(phi) * stheta;
1415 newElectron.kz = ctheta;
1416 }
1417 newElectron.status = 0;
1418 newElectron.driftLine.clear();
1419 if (aval && (m_sizeCut <= 0 || (int)m_stack.size() < m_sizeCut)) {
1420 m_stack.push_back(newElectron);
1421 }
1422 // Increment the electron counter.
1423 ++m_nElectrons;
1424 } else if (itype == IonProdTypeHole) {
1425 esec = std::max(esec, Small);
1426 // Add the secondary hole to the stack.
1427 newElectron = m_stack[iE];
1428 newElectron.hole = true;
1429 newElectron.x0 = x;
1430 newElectron.x = x;
1431 newElectron.y0 = y;
1432 newElectron.y = y;
1433 newElectron.z0 = z;
1434 newElectron.z = z;
1435 newElectron.t0 = t;
1436 newElectron.t = t;
1437 newElectron.energy = esec;
1438 newElectron.e0 = newElectron.energy;
1439 if (m_useBandStructure) {
1440 newElectron.band = -1;
1441 medium->GetElectronMomentum(esec, newElectron.kx,
1442 newElectron.ky, newElectron.kz,
1443 newElectron.band);
1444 } else {
1445 // Randomise the secondary hole direction.
1446 const double phi = TwoPi * RndmUniform();
1447 const double ctheta = 2 * RndmUniform() - 1.;
1448 const double stheta = sqrt(1. - ctheta * ctheta);
1449 newElectron.kx = cos(phi) * stheta;
1450 newElectron.ky = sin(phi) * stheta;
1451 newElectron.kz = ctheta;
1452 }
1453 newElectron.status = 0;
1454 newElectron.driftLine.clear();
1455 if (aval && (m_sizeCut <= 0 || (int)m_stack.size() < m_sizeCut)) {
1456 m_stack.push_back(newElectron);
1457 }
1458 // Increment the hole counter.
1459 ++m_nHoles;
1460 } else if (itype == IonProdTypeIon) {
1461 ++m_nIons;
1462 }
1463 }
1464 if (m_debug) {
1465 std::cout << m_className << "::TransportElectron:\n";
1466 std::cout << " Ionisation.\n";
1467 std::cout << " At " << x << "," << y << "," << z << "\n";
1468 }
1469 break;
1470 // Attachment
1471 case ElectronCollisionTypeAttachment:
1472 if (m_usePlotting && m_plotAttachments) {
1473 m_viewer->AddAttachmentMarker(x, y, z);
1474 }
1475 if (m_hasUserHandleAttachment) {
1476 m_userHandleAttachment(x, y, z, t, cstype, level, medium);
1477 }
1478 m_stack[iE].x = x;
1479 m_stack[iE].y = y;
1480 m_stack[iE].z = z;
1481 m_stack[iE].t = t;
1482 m_stack[iE].energy = energy;
1483 m_stack[iE].status = StatusAttached;
1484 if (hole) {
1485 m_endpointsHoles.push_back(m_stack[iE]);
1486 --m_nHoles;
1487 } else {
1488 m_endpointsElectrons.push_back(m_stack[iE]);
1489 --m_nElectrons;
1490 }
1491 m_stack.erase(m_stack.begin() + iE);
1492 ok = false;
1493 break;
1494 // Inelastic collision
1495 case ElectronCollisionTypeInelastic:
1496 if (m_hasUserHandleInelastic) {
1497 m_userHandleInelastic(x, y, z, t, cstype, level, medium);
1498 }
1499 break;
1500 // Excitation
1501 case ElectronCollisionTypeExcitation:
1502 if (m_usePlotting && m_plotExcitations) {
1503 m_viewer->AddExcitationMarker(x, y, z);
1504 }
1505 if (m_hasUserHandleInelastic) {
1506 m_userHandleInelastic(x, y, z, t, cstype, level, medium);
1507 }
1508 if (ndxc > 0) {
1509 // Get the electrons and photons produced in the
1510 // deexcitation cascade.
1511 double tDxc = 0., sDxc = 0., eDxc = 0.;
1512 int typeDxc = 0;
1513 stackPhotonsTime.clear();
1514 stackPhotonsEnergy.clear();
1515 for (int j = ndxc; j--;) {
1516 if (!medium->GetDeexcitationProduct(j, tDxc, sDxc, typeDxc,
1517 eDxc)) {
1518 std::cerr << m_className << "::TransportElectron:\n";
1519 std::cerr << " Cannot retrieve deexcitation product " << j
1520 << "/" << ndxc << ".\n";
1521 break;
1522 }
1523
1524 if (typeDxc == DxcProdTypeElectron) {
1525 if (!aval || (m_sizeCut > 0 && (int)m_stack.size() >= m_sizeCut))
1526 continue;
1527 // Penning ionisation
1528 newElectron = m_stack[iE];
1529 double xDxc = x, yDxc = y, zDxc = z;
1530 if (sDxc > Small) {
1531 // Randomise the point of creation
1532 double phiDxc = TwoPi * RndmUniform();
1533 double cthetaDxc = 1. - 2 * RndmUniform();
1534 double sthetaDxc = sqrt(1. - cthetaDxc * cthetaDxc);
1535 xDxc += sDxc * cos(phiDxc) * sthetaDxc;
1536 yDxc += sDxc * sin(phiDxc) * sthetaDxc;
1537 zDxc += sDxc * cthetaDxc;
1538 }
1539 // Get the electric field and medium at this location.
1540 Medium* dxcMedium = 0;
1541 double fx = 0., fy = 0., fz = 0.;
1542 m_sensor->ElectricField(xDxc, yDxc, zDxc, fx, fy, fz, dxcMedium,
1543 status);
1544 // Check if this location is inside a drift medium.
1545 if (status != 0) continue;
1546 // Check if this location is inside the drift area.
1547 if (!m_sensor->IsInArea(xDxc, yDxc, zDxc)) continue;
1548 // Make sure we haven't jumped across a wire.
1549 double xCross, yCross, zCross;
1550 if (m_sensor->IsWireCrossed(x, y, z, xDxc, yDxc, zDxc, xCross,
1551 yCross, zCross)) {
1552 continue;
1553 }
1554 newElectron.x0 = xDxc;
1555 newElectron.x = xDxc;
1556 newElectron.y0 = yDxc;
1557 newElectron.y = yDxc;
1558 newElectron.z0 = zDxc;
1559 newElectron.z = zDxc;
1560 newElectron.t0 = t + tDxc;
1561 newElectron.t = t + tDxc;
1562 newElectron.energy = std::max(eDxc, Small);
1563 newElectron.e0 = newElectron.energy;
1564 // Randomise the initial direction.
1565 const double phi = TwoPi * RndmUniform();
1566 const double ctheta = 2 * RndmUniform() - 1.;
1567 const double stheta = sqrt(1. - ctheta * ctheta);
1568 newElectron.kx = cos(phi) * stheta;
1569 newElectron.ky = sin(phi) * stheta;
1570 newElectron.kz = ctheta;
1571 newElectron.status = 0;
1572 newElectron.driftLine.clear();
1573 // Add the electron to the list.
1574 m_stack.push_back(newElectron);
1575 // Increment the electron and ion counters.
1576 ++m_nElectrons;
1577 ++m_nIons;
1578 } else if (typeDxc == DxcProdTypePhoton && m_usePhotons &&
1579 eDxc > m_gammaCut) {
1580 // Radiative de-excitation
1581 stackPhotonsTime.push_back(t + tDxc);
1582 stackPhotonsEnergy.push_back(eDxc);
1583 }
1584 }
1585
1586 // Transport the photons (if any)
1587 const int nSizePhotons = stackPhotonsTime.size();
1588 for (int j = nSizePhotons; j--;) {
1589 if (aval) {
1590 TransportPhoton(x, y, z, stackPhotonsTime[j],
1591 stackPhotonsEnergy[j]);
1592 }
1593 }
1594 }
1595 break;
1596 // Super-elastic collision
1597 case ElectronCollisionTypeSuperelastic:
1598 break;
1599 // Acoustic phonon scattering (intravalley)
1600 case ElectronCollisionTypeAcousticPhonon:
1601 break;
1602 // Optical phonon scattering (intravalley)
1603 case ElectronCollisionTypeOpticalPhonon:
1604 break;
1605 // Intervalley scattering (phonon assisted)
1606 case ElectronCollisionTypeIntervalleyG:
1607 case ElectronCollisionTypeIntervalleyF:
1608 case ElectronCollisionTypeInterbandXL:
1609 case ElectronCollisionTypeInterbandXG:
1610 case ElectronCollisionTypeInterbandLG:
1611 break;
1612 // Coulomb scattering
1613 case ElectronCollisionTypeImpurity:
1614 break;
1615 default:
1616 std::cerr << m_className << "::TransportElectron:\n";
1617 std::cerr << " Unknown collision type.\n";
1618 ok = false;
1619 break;
1620 }
1621
1622 // Continue with the next electron/hole?
1623 if (!ok || nCollTemp > m_nCollSkip ||
1624 cstype == ElectronCollisionTypeIonisation ||
1625 (m_plotExcitations && cstype == ElectronCollisionTypeExcitation) ||
1626 (m_plotAttachments && cstype == ElectronCollisionTypeAttachment)) {
1627 break;
1628 }
1629 kx = newKx;
1630 ky = newKy;
1631 kz = newKz;
1632 }
1633
1634 if (!ok) continue;
1635
1636 if (!m_useBandStructure) {
1637 // Normalise the direction vector.
1638 const double k = sqrt(kx * kx + ky * ky + kz * kz);
1639 kx /= k;
1640 ky /= k;
1641 kz /= k;
1642 }
1643 // Update the stack.
1644 m_stack[iE].energy = energy;
1645 m_stack[iE].t = t;
1646 m_stack[iE].x = x;
1647 m_stack[iE].y = y;
1648 m_stack[iE].z = z;
1649 m_stack[iE].kx = kx;
1650 m_stack[iE].ky = ky;
1651 m_stack[iE].kz = kz;
1652 // Add a new point to the drift line (if enabled).
1653 if (m_useDriftLines) {
1654 point newPoint;
1655 newPoint.x = x;
1656 newPoint.y = y;
1657 newPoint.z = z;
1658 newPoint.t = t;
1659 m_stack[iE].driftLine.push_back(newPoint);
1660 }
1661 }
1662 }
1663 m_nElectronEndpoints = m_endpointsElectrons.size();
1664 m_nHoleEndpoints = m_endpointsHoles.size();
1665
1666 // Calculate the induced charge.
1667 if (m_useInducedCharge) {
1668 for (int i = m_nElectronEndpoints; i--;) {
1669 m_sensor->AddInducedCharge(
1670 -1, m_endpointsElectrons[i].x0, m_endpointsElectrons[i].y0,
1671 m_endpointsElectrons[i].z0, m_endpointsElectrons[i].x,
1672 m_endpointsElectrons[i].y, m_endpointsElectrons[i].z);
1673 }
1674 for (int i = m_nHoleEndpoints; i--;) {
1675 m_sensor->AddInducedCharge(+1, m_endpointsHoles[i].x0, m_endpointsHoles[i].y0,
1676 m_endpointsHoles[i].z0, m_endpointsHoles[i].x,
1677 m_endpointsHoles[i].y, m_endpointsHoles[i].z);
1678 }
1679 }
1680
1681 // Plot the drift paths and photon tracks.
1682 if (m_usePlotting) {
1683 // Electrons
1684 for (int i = m_nElectronEndpoints; i--;) {
1685 const int np = GetNumberOfElectronDriftLinePoints(i);
1686 int jL;
1687 if (np <= 0) continue;
1688 m_viewer->NewElectronDriftLine(np, jL, m_endpointsElectrons[i].x0,
1689 m_endpointsElectrons[i].y0,
1690 m_endpointsElectrons[i].z0);
1691 for (int jP = np; jP--;) {
1692 GetElectronDriftLinePoint(x, y, z, t, jP, i);
1693 m_viewer->SetDriftLinePoint(jL, jP, x, y, z);
1694 }
1695 }
1696 // Holes
1697 for (int i = m_nHoleEndpoints; i--;) {
1698 const int np = GetNumberOfHoleDriftLinePoints(i);
1699 int jL;
1700 if (np <= 0) continue;
1701 m_viewer->NewHoleDriftLine(np, jL, m_endpointsHoles[i].x0,
1702 m_endpointsHoles[i].y0, m_endpointsHoles[i].z0);
1703 for (int jP = np; jP--;) {
1704 GetHoleDriftLinePoint(x, y, z, t, jP, i);
1705 m_viewer->SetDriftLinePoint(jL, jP, x, y, z);
1706 }
1707 }
1708 // Photons
1709 for (int i = m_nPhotons; i--;) {
1710 m_viewer->NewPhotonTrack(m_photons[i].x0, m_photons[i].y0, m_photons[i].z0,
1711 m_photons[i].x1, m_photons[i].y1, m_photons[i].z1);
1712 }
1713 }
1714 return true;
1715}
1716
1717void AvalancheMicroscopic::TransportPhoton(const double x0, const double y0,
1718 const double z0, const double t0,
1719 const double e0) {
1720
1721 // Make sure that the sensor is defined.
1722 if (!m_sensor) {
1723 std::cerr << m_className << "::TransportPhoton:\n";
1724 std::cerr << " Sensor is not defined.\n";
1725 return;
1726 }
1727
1728 // Make sure that the starting point is inside a medium.
1729 Medium* medium;
1730 if (!m_sensor->GetMedium(x0, y0, z0, medium)) {
1731 std::cerr << m_className << "::TransportPhoton:\n";
1732 std::cerr << " No medium at initial position.\n";
1733 return;
1734 }
1735
1736 // Make sure that the medium is "driftable" and microscopic.
1737 if (!medium->IsDriftable() || !medium->IsMicroscopic()) {
1738 std::cerr << m_className << "::TransportPhoton:\n";
1739 std::cerr << " Medium at initial position does not provide "
1740 << " microscopic tracking data.\n";
1741 return;
1742 }
1743
1744 if (m_debug) {
1745 std::cout << m_className << "::TransportPhoton:\n";
1746 std::cout << " Starting photon transport in medium " << medium->GetName()
1747 << ".\n";
1748 }
1749
1750 // Get the id number of the drift medium.
1751 int id = medium->GetId();
1752
1753 // Position
1754 double x = x0, y = y0, z = z0;
1755 double t = t0;
1756 // Initial direction (randomised)
1757 double ctheta = 2 * RndmUniform() - 1.;
1758 double stheta = sqrt(1. - ctheta * ctheta);
1759 double phi = TwoPi * RndmUniform();
1760 double dx = cos(phi) * stheta;
1761 double dy = sin(phi) * stheta;
1762 double dz = ctheta;
1763 // Energy
1764 double e = e0;
1765 // Photon collision rate
1766 double f = 0.;
1767 // Timestep
1768 double dt = 0.;
1769
1770 int type, level;
1771 double e1;
1772 int nsec = 0;
1773 double esec = 0.;
1774
1776 if (f <= 0.) return;
1777
1778 dt = -log(RndmUniformPos()) / f;
1779 t += dt;
1780 dt *= SpeedOfLight;
1781 x += dt * dx;
1782 y += dt * dy;
1783 z += dt * dz;
1784
1785 // Check if the photon is still inside a medium.
1786 if (!m_sensor->GetMedium(x, y, z, medium) || medium->GetId() != id) {
1787 // Try to terminate the photon track close to the boundary
1788 // by means of iterative bisection.
1789 dx *= dt;
1790 dy *= dt;
1791 dz *= dt;
1792 x -= dx;
1793 y -= dy;
1794 z -= dz;
1795 double delta = sqrt(dx * dx + dy * dy + dz * dz);
1796 if (delta > 0) {
1797 dx /= delta;
1798 dy /= delta;
1799 dz /= delta;
1800 }
1801 // Mid-point
1802 double xM = x, yM = y, zM = z;
1803 while (delta > BoundaryDistance) {
1804 delta *= 0.5;
1805 dt *= 0.5;
1806 xM = x + delta * dx;
1807 yM = y + delta * dy;
1808 zM = z + delta * dz;
1809 // Check if the mid-point is inside the drift medium.
1810 if (m_sensor->GetMedium(xM, yM, zM, medium) && medium->GetId() == id) {
1811 x = xM;
1812 y = yM;
1813 z = zM;
1814 t += dt;
1815 }
1816 }
1817 photon newPhoton;
1818 newPhoton.x0 = x0;
1819 newPhoton.y0 = y0;
1820 newPhoton.z0 = z0;
1821 newPhoton.x1 = x;
1822 newPhoton.y1 = y;
1823 newPhoton.z1 = z;
1824 newPhoton.energy = e0;
1825 newPhoton.status = StatusLeftDriftMedium;
1826 m_photons.push_back(newPhoton);
1827 ++m_nPhotons;
1828 return;
1829 }
1830
1831 if (!medium->GetPhotonCollision(e, type, level, e1, ctheta, nsec, esec))
1832 return;
1833
1834 if (type == PhotonCollisionTypeIonisation) {
1835 // Randomise secondary electron direction.
1836 phi = TwoPi * RndmUniform();
1837 ctheta = 2 * RndmUniform() - 1.;
1838 stheta = sqrt(1. - ctheta * ctheta);
1839 // Add the secondary electron to the stack.
1840 electron newElectron;
1841 newElectron.hole = false;
1842 newElectron.x0 = x;
1843 newElectron.x = x;
1844 newElectron.y0 = y;
1845 newElectron.y = y;
1846 newElectron.z0 = z;
1847 newElectron.z = z;
1848 newElectron.t0 = t;
1849 newElectron.t = t;
1850 newElectron.energy = std::max(esec, Small);
1851 newElectron.e0 = newElectron.energy;
1852 newElectron.kx = cos(phi) * stheta;
1853 newElectron.ky = sin(phi) * stheta;
1854 newElectron.kz = ctheta;
1855 newElectron.status = 0;
1856 newElectron.driftLine.clear();
1857 if (m_sizeCut <= 0 || (int)m_stack.size() < m_sizeCut)
1858 m_stack.push_back(newElectron);
1859 // Increment the electron and ion counters.
1860 ++m_nElectrons;
1861 ++m_nIons;
1862 } else if (type == PhotonCollisionTypeExcitation) {
1863 double tDxc = 0.;
1864 double sDxc = 0.;
1865 int typeDxc = 0;
1866 std::vector<double> stackPhotonsTime;
1867 stackPhotonsTime.clear();
1868 std::vector<double> stackPhotonsEnergy;
1869 stackPhotonsEnergy.clear();
1870 for (int j = nsec; j--;) {
1871 if (!medium->GetDeexcitationProduct(j, tDxc, sDxc, typeDxc, esec))
1872 continue;
1873 if (typeDxc == DxcProdTypeElectron) {
1874 // Ionisation
1875 phi = TwoPi * RndmUniform();
1876 ctheta = 2 * RndmUniform() - 1.;
1877 stheta = sqrt(1. - ctheta * ctheta);
1878 // Add the electron to the stack.
1879 electron newElectron;
1880 newElectron.hole = false;
1881 newElectron.x0 = x;
1882 newElectron.x = x;
1883 newElectron.y0 = y;
1884 newElectron.y = y;
1885 newElectron.z0 = z;
1886 newElectron.z = z;
1887 newElectron.t0 = t + tDxc;
1888 newElectron.t = t + tDxc;
1889 newElectron.energy = std::max(esec, Small);
1890 newElectron.e0 = newElectron.energy;
1891 newElectron.kx = cos(phi) * stheta;
1892 newElectron.ky = sin(phi) * stheta;
1893 newElectron.kz = ctheta;
1894 newElectron.status = 0;
1895 newElectron.driftLine.clear();
1896 m_stack.push_back(newElectron);
1897 // Increment the electron and ion counters.
1898 ++m_nElectrons;
1899 ++m_nIons;
1900 } else if (typeDxc == DxcProdTypePhoton && m_usePhotons &&
1901 esec > m_gammaCut) {
1902 // Radiative de-excitation
1903 stackPhotonsTime.push_back(t + tDxc);
1904 stackPhotonsEnergy.push_back(esec);
1905 }
1906 }
1907 // Transport the photons (if any).
1908 const int nSizePhotons = stackPhotonsTime.size();
1909 for (int k = nSizePhotons; k--;) {
1910 TransportPhoton(x, y, z, stackPhotonsTime[k], stackPhotonsEnergy[k]);
1911 }
1912 }
1913
1914 photon newPhoton;
1915 newPhoton.x0 = x0;
1916 newPhoton.y0 = y0;
1917 newPhoton.z0 = z0;
1918 newPhoton.x1 = x;
1919 newPhoton.y1 = y;
1920 newPhoton.z1 = z;
1921 newPhoton.energy = e0;
1922 newPhoton.status = -2;
1923 m_photons.push_back(newPhoton);
1924 ++m_nPhotons;
1925}
1926
1927void AvalancheMicroscopic::ComputeRotationMatrix(
1928 const double bx, const double by, const double bz, const double bmag,
1929 const double ex, const double ey, const double ez) {
1930
1931 // Adopting the Magboltz convention, the stepping is performed
1932 // in a coordinate system with the B field along the x axis
1933 // and the electric field at an angle btheta in the x-z plane.
1934
1935 // Calculate the first rotation matrix (to align B with x axis).
1936 const double bt = by * by + bz * bz;
1937 if (bt < Small) {
1938 // B field is already along axis.
1939 m_rb11 = m_rb22 = m_rb33 = 1.;
1940 m_rb12 = m_rb13 = m_rb21 = m_rb23 = m_rb31 = m_rb32 = 0.;
1941 } else {
1942 m_rb11 = bx / bmag;
1943 m_rb12 = by / bmag;
1944 m_rb21 = -m_rb12;
1945 m_rb13 = bz / bmag;
1946 m_rb31 = -m_rb13;
1947 m_rb22 = (m_rb11 * by * by + bz * bz) / bt;
1948 m_rb33 = (m_rb11 * bz * bz + by * by) / bt;
1949 m_rb23 = m_rb32 = (m_rb11 - 1.) * by * bz / bt;
1950 }
1951 // Calculate the second rotation matrix (rotation around x axis).
1952 const double fy = m_rb21 * ex + m_rb22 * ey + m_rb23 * ez;
1953 const double fz = m_rb31 * ex + m_rb32 * ey + m_rb33 * ez;
1954 const double ft = sqrt(fy * fy + fz * fz);
1955 if (ft < Small) {
1956 // E and B field are parallel.
1957 m_rx22 = m_rx33 = 1.;
1958 m_rx23 = m_rx32 = 0.;
1959 } else {
1960 m_rx22 = m_rx33 = fz / ft;
1961 m_rx23 = -fy / ft;
1962 m_rx32 = -m_rx23;
1963 }
1964}
1965
1966void AvalancheMicroscopic::RotateGlobal2Local(double& dx, double& dy,
1967 double& dz) {
1968
1969 const double dx1 = m_rb11 * dx + m_rb12 * dy + m_rb13 * dz;
1970 const double dy1 = m_rb21 * dx + m_rb22 * dy + m_rb23 * dz;
1971 const double dz1 = m_rb31 * dx + m_rb32 * dy + m_rb33 * dz;
1972
1973 dx = dx1;
1974 dy = m_rx22 * dy1 + m_rx23 * dz1;
1975 dz = m_rx32 * dy1 + m_rx33 * dz1;
1976}
1977
1978void AvalancheMicroscopic::RotateLocal2Global(double& dx, double& dy,
1979 double& dz) {
1980
1981 const double dx1 = dx;
1982 const double dy1 = m_rx22 * dy + m_rx32 * dz;
1983 const double dz1 = m_rx23 * dy + m_rx33 * dz;
1984
1985 dx = m_rb11 * dx1 + m_rb21 * dy1 + m_rb31 * dz1;
1986 dy = m_rb12 * dx1 + m_rb22 * dy1 + m_rb32 * dz1;
1987 dz = m_rb13 * dx1 + m_rb23 * dy1 + m_rb33 * dz1;
1988}
1989}
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
DoubleAc fabs(const DoubleAc &f)
Definition: DoubleAc.h:616
void EnableDistanceHistogramming(const int type)
void SetDistanceHistogram(TH1 *histo, const char opt='r')
void SetUserHandleStep(void(*f)(double x, double y, double z, double t, double e, double dx, double dy, double dz, bool hole))
void EnablePlotting(ViewDrift *view)
void SetUserHandleIonisation(void(*f)(double x, double y, double z, double t, int type, int level, Medium *m))
bool DriftElectron(const double x0, const double y0, const double z0, const double t0, const double e0, const double dx0=0., const double dy0=0., const double dz0=0.)
unsigned int GetNumberOfElectronDriftLinePoints(const unsigned int i=0) const
void SetUserHandleAttachment(void(*f)(double x, double y, double z, double t, int type, int level, Medium *m))
void GetHoleDriftLinePoint(double &x, double &y, double &z, double &t, const int ip, const unsigned int iel=0) const
void GetElectronEndpoint(const unsigned int i, double &x0, double &y0, double &z0, double &t0, double &e0, double &x1, double &y1, double &z1, double &t1, double &e1, int &status) const
void GetHoleEndpoint(const unsigned int i, double &x0, double &y0, double &z0, double &t0, double &e0, double &x1, double &y1, double &z1, double &t1, double &e1, int &status) const
void GetElectronDriftLinePoint(double &x, double &y, double &z, double &t, const int ip, const unsigned int iel=0) const
void EnableElectronEnergyHistogramming(TH1 *histo)
void GetPhoton(const unsigned int i, double &e, double &x0, double &y0, double &z0, double &t0, double &x1, double &y1, double &z1, double &t1, int &status) const
void EnableSecondaryEnergyHistogramming(TH1 *histo)
bool AvalancheElectron(const double x0, const double y0, const double z0, const double t0, const double e0, const double dx0=0., const double dy0=0., const double dz0=0.)
unsigned int GetNumberOfHoleDriftLinePoints(const unsigned int i=0) const
void SetUserHandleInelastic(void(*f)(double x, double y, double z, double t, int type, int level, Medium *m))
void SetTimeWindow(const double t0, const double t1)
virtual bool GetPhotonCollision(const double e, int &type, int &level, double &e1, double &ctheta, int &nsec, double &esec)
Definition: Medium.cc:1474
virtual void GetElectronMomentum(const double e, double &px, double &py, double &pz, int &band)
Definition: Medium.cc:693
bool IsMicroscopic() const
Definition: Medium.hh:58
virtual bool GetIonisationProduct(const int i, int &type, double &energy)
Definition: Medium.cc:748
virtual bool GetElectronCollision(const double e, int &type, int &level, double &e1, double &dx, double &dy, double &dz, int &nion, int &ndxc, int &band)
Definition: Medium.cc:727
int GetId() const
Definition: Medium.hh:20
virtual double GetElectronNullCollisionRate(const int band=0)
Definition: Medium.cc:708
virtual double GetElectronCollisionRate(const double e, const int band=0)
Definition: Medium.cc:717
virtual bool IsSemiconductor() const
Definition: Medium.hh:24
virtual bool GetDeexcitationProduct(const int i, double &t, double &s, int &type, double &energy)
Definition: Medium.cc:760
virtual double GetElectronEnergy(const double px, const double py, const double pz, double &vx, double &vy, double &vz, const int band=0)
Definition: Medium.cc:677
virtual double GetPhotonCollisionRate(const double &e)
Definition: Medium.cc:1466
std::string GetName() const
Definition: Medium.hh:22
bool IsDriftable() const
Definition: Medium.hh:57
void MagneticField(const double x, const double y, const double z, double &bx, double &by, double &bz, int &status)
Definition: Sensor.cc:95
void AddSignal(const double &q, const double &t, const double &dt, const double &x, const double &y, const double &z, const double &vx, const double &vy, const double &vz)
Definition: Sensor.cc:409
void ElectricField(const double x, const double y, const double z, double &ex, double &ey, double &ez, double &v, Medium *&medium, int &status)
Definition: Sensor.cc:44
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 IsWireCrossed(const double x0, const double y0, const double z0, const double x1, const double y1, const double z1, double &xc, double &yc, double &zc)
Definition: Sensor.cc:278
void AddInducedCharge(const double q, const double x0, const double y0, const double z0, const double x1, const double y1, const double z1)
Definition: Sensor.cc:493
void NewElectronDriftLine(const unsigned int np, int &id, const double x0, const double y0, const double z0)
Definition: ViewDrift.cc:140
void AddExcitationMarker(const double x, const double y, const double z)
Definition: ViewDrift.cc:312
void AddIonisationMarker(const double x, const double y, const double z)
Definition: ViewDrift.cc:323
void SetDriftLinePoint(const unsigned int iL, const unsigned int iP, const double x, const double y, const double z)
Definition: ViewDrift.cc:255
void AddAttachmentMarker(const double x, const double y, const double z)
Definition: ViewDrift.cc:334
void NewPhotonTrack(const double x0, const double y0, const double z0, const double x1, const double y1, const double z1)
Definition: ViewDrift.cc:219
void NewHoleDriftLine(const unsigned int np, int &id, const double x0, const double y0, const double z0)
Definition: ViewDrift.cc:170
Definition: vec.h:477
double RndmUniform()
Definition: Random.hh:16
double RndmUniformPos()
Definition: Random.hh:19