Garfield++ 5.0
A toolkit for the detailed simulation of particle detectors based on ionisation measurement in gases and semiconductors
Loading...
Searching...
No Matches
TrackHeed.cc
Go to the documentation of this file.
1#include <iostream>
2
5
16
17#include "HeedChamber.hh"
18#include "HeedFieldMap.h"
19
22#include "Garfield/Random.hh"
23#include "Garfield/Sensor.hh"
24#include "Garfield/ViewDrift.hh"
25
26#include "Garfield/TrackHeed.hh"
27
28namespace {
29
30void ClearBank(std::vector<Heed::gparticle*>& bank) {
31 for (auto particle : bank)
32 if (particle) delete particle;
33 bank.clear();
34}
35
36Heed::vec NormaliseDirection(const double dx0, const double dy0,
37 const double dz0) {
38 double dx = dx0, dy = dy0, dz = dz0;
39 const double d = sqrt(dx * dx + dy * dy + dz * dz);
40 if (d < Garfield::Small) {
41 // Null vector. Sample the direction isotropically.
42 Garfield::RndmDirection(dx, dy, dz);
43 } else {
44 // Normalise the direction vector.
45 const double scale = 1. / d;
46 dx *= scale;
47 dy *= scale;
48 dz *= scale;
49 }
50 return Heed::vec(dx, dy, dz);
51}
52
53Heed::MolecPhotoAbsCS makeMPACS(const std::string& atom, const int n,
54 const double w = 0.) {
56}
57
58Heed::MolecPhotoAbsCS makeMPACS(const std::string& atom1, const int n1,
59 const std::string& atom2, const int n2,
60 const double w = 0.) {
62 Heed::PhotoAbsCSLib::getAPACS(atom2), n2, w);
63}
64
65Heed::MolecPhotoAbsCS makeMPACS(const std::string& atom1, const int n1,
66 const std::string& atom2, const int n2,
67 const std::string& atom3, const int n3,
68 const double w = 0.) {
71 Heed::PhotoAbsCSLib::getAPACS(atom3), n3, w);
72}
73
74}
75
76// Actual class implementation
77
78namespace Garfield {
79
80TrackHeed::TrackHeed(Sensor* sensor) : Track("Heed") {
81 m_sensor = sensor;
82 m_fieldMap.reset(new Heed::HeedFieldMap());
83}
84
86
87bool TrackHeed::NewTrack(const double x0, const double y0, const double z0,
88 const double t0, const double dx0, const double dy0,
89 const double dz0) {
90 m_hasActiveTrack = false;
91
92 // Make sure the sensor has been set.
93 if (!m_sensor) {
94 std::cerr << m_className << "::NewTrack: Sensor is not defined.\n";
95 return false;
96 }
97
98 bool update = false;
99 if (!UpdateBoundingBox(update)) return false;
100
101 // Make sure the initial position is inside an ionisable medium.
102 Medium* medium = m_sensor->GetMedium(x0, y0, z0);
103 if (!medium || !medium->IsIonisable()) {
104 std::cerr << m_className << "::NewTrack:\n"
105 << " No ionisable medium at initial position.\n";
106 return false;
107 }
108
109 // Check if the medium has changed since the last call.
110 if (medium->GetName() != m_mediumName ||
111 fabs(medium->GetMassDensity() - m_mediumDensity) > 1.e-9) {
112 m_isChanged = true;
113 }
114
115 // If medium, particle or bounding box have changed,
116 // update the cross-sections.
117 if (m_isChanged) {
118 if (!Initialise(medium)) return false;
119 m_isChanged = false;
120 m_mediumName = medium->GetName();
121 m_mediumDensity = medium->GetMassDensity();
122 }
123
124 // Reset the list of clusters.
125 m_clusters.clear();
126 m_cluster = 0;
127
128 // Set the velocity vector.
129 Heed::vec velocity = NormaliseDirection(dx0, dy0, dz0);
130 velocity = velocity * Heed::CLHEP::c_light * GetBeta();
131
132 if (m_debug) {
133 std::cout << m_className << "::NewTrack:\n Track starts at (" << x0
134 << ", " << y0 << ", " << z0 << ") at time " << t0 << "\n";
135 }
136
137 // Initial position (shift with respect to bounding box center and
138 // convert from cm to mm).
139 Heed::point p0((x0 - m_cX) * 10., (y0 - m_cY) * 10., (z0 - m_cZ) * 10.);
140
141 // Setup the particle.
143 if (m_particleName == "e-") {
144 particleType = &Heed::electron_def;
145 } else if (m_particleName == "e+") {
146 particleType = &Heed::positron_def;
147 } else if (m_particleName == "mu-") {
148 particleType = &Heed::muon_minus_def;
149 } else if (m_particleName == "mu+") {
150 particleType = &Heed::muon_plus_def;
151 } else if (m_particleName == "pi-") {
152 particleType = &Heed::pi_minus_meson_def;
153 } else if (m_particleName == "pi+") {
154 particleType = &Heed::pi_plus_meson_def;
155 } else if (m_particleName == "K-") {
156 particleType = &Heed::K_minus_meson_def;
157 } else if (m_particleName == "K+") {
158 particleType = &Heed::K_plus_meson_def;
159 } else if (m_particleName == "p") {
160 particleType = &Heed::proton_def;
161 } else if (m_particleName == "pbar") {
162 particleType = &Heed::anti_proton_def;
163 } else if (m_particleName == "d") {
164 particleType = &Heed::deuteron_def;
165 } else if (m_particleName == "alpha") {
166 particleType = &Heed::alpha_particle_def;
167 } else if (m_particleName == "exotic") {
168 // User defined particle
169 m_particle_def.reset(new Heed::particle_def(Heed::pi_plus_meson_def));
170 m_particle_def->set_mass(m_mass * 1.e-6);
171 m_particle_def->set_charge(m_q);
172 particleType = m_particle_def.get();
173 } else {
174 // Not a predefined particle, use muon definition.
175 if (m_q > 0.) {
176 particleType = &Heed::muon_minus_def;
177 } else {
178 particleType = &Heed::muon_plus_def;
179 }
180 }
181
182 Heed::HeedParticle particle(m_chamber.get(), p0, velocity, t0, particleType,
183 m_fieldMap.get(), m_coulombScattering);
184 if (m_useBfieldAuto) {
185 m_fieldMap->UseBfield(m_sensor->HasMagneticField());
186 }
187 // Set the step limits.
188 particle.set_step_limits(m_maxStep * Heed::CLHEP::cm,
189 m_radStraight * Heed::CLHEP::cm,
190 m_stepAngleStraight * Heed::CLHEP::rad,
191 m_stepAngleCurved * Heed::CLHEP::rad);
192 // Transport the particle.
193 std::vector<Heed::gparticle*> particleBank;
194 if (m_oneStepFly) {
195 particle.fly(particleBank, true);
196 } else {
197 particle.fly(particleBank);
198 }
199
200 // Sort the clusters by time.
201 std::sort(particleBank.begin(), particleBank.end(),
202 [](Heed::gparticle* p1, Heed::gparticle* p2) {
203 return p1->time() < p2->time(); });
204 // Loop over the clusters (virtual photons) created by the particle.
205 for (auto gp : particleBank) {
206 // Convert the particle to a (virtual) photon.
207 Heed::HeedPhoton* virtualPhoton = dynamic_cast<Heed::HeedPhoton*>(gp);
208 if (!virtualPhoton) {
209 std::cerr << m_className << "::NewTrack:\n"
210 << " Particle is not a virtual photon. Program bug!\n";
211 // Skip this one.
212 continue;
213 }
214 if (!AddCluster(virtualPhoton, m_clusters)) break;
215 }
216 ClearBank(particleBank);
218 m_cluster = m_clusters.size() + 2;
219
220 m_hasActiveTrack = true;
221
222 // Plot the track, if requested.
223 if (m_viewer) {
224 PlotNewTrack(x0, y0, z0);
225 for (const auto& cluster : m_clusters) {
226 PlotCluster(cluster.x, cluster.y, cluster.z);
227 }
228 }
229 return true;
230}
231
233
234 if (!m_transferCs) {
235 std::cerr << m_className << "::GetClusterDensity:\n"
236 << " Ionisation cross-section is not available.\n";
237 return 0.;
238 }
239 return m_transferCs->quanC;
240}
241
243
244 if (!m_transferCs) {
245 std::cerr << m_className << "::GetStoppingPower:\n"
246 << " Ionisation cross-section is not available.\n";
247 return 0.;
248 }
249 return m_transferCs->meanC1 * 1.e6;
250}
251
252bool TrackHeed::AddCluster(Heed::HeedPhoton* virtualPhoton,
253 std::vector<Cluster>& clusters) {
254
255 // Get the location of the interaction (convert from mm to cm
256 // and shift with respect to bounding box center).
257 const double xc = virtualPhoton->position().x * 0.1 + m_cX;
258 const double yc = virtualPhoton->position().y * 0.1 + m_cY;
259 const double zc = virtualPhoton->position().z * 0.1 + m_cZ;
260 const double tc = virtualPhoton->time();
261
262 // Make sure the clusters is inside the drift area and active medium.
263 if (!IsInside(xc, yc, zc)) return false;
264
265 Cluster cluster;
266 cluster.x = xc;
267 cluster.y = yc;
268 cluster.z = zc;
269 cluster.t = tc;
270 // Get the transferred energy (convert from MeV to eV).
271 cluster.energy = virtualPhoton->m_energy * 1.e6;
272 cluster.extra = 0.;
273 // Add the first ion (at the position of the cluster).
274 Electron ion;
275 ion.x = xc;
276 ion.y = yc;
277 ion.z = zc;
278 ion.t = tc;
279 cluster.ions.push_back(std::move(ion));
280
281 // Transport the virtual photon.
282 std::vector<Heed::gparticle*> secondaries;
283 virtualPhoton->fly(secondaries);
284 while (!secondaries.empty()) {
285 std::vector<Heed::gparticle*> newSecondaries;
286 // Loop over the secondaries.
287 for (auto secondary : secondaries) {
288 // Is the secondary a delta electron?
289 auto delta = dynamic_cast<Heed::HeedDeltaElectron*>(secondary);
290 if (delta) {
291 cluster.extra += delta->kinetic_energy() * 1.e6;
292 const double x = delta->position().x * 0.1 + m_cX;
293 const double y = delta->position().y * 0.1 + m_cY;
294 const double z = delta->position().z * 0.1 + m_cZ;
295 if (!IsInside(x, y, z)) continue;
296 if (m_doDeltaTransport) {
297 // Transport the delta electron.
298 delta->fly(newSecondaries);
299 // Add the conduction electrons and ions to the list.
300 AddElectrons(delta->conduction_electrons, cluster.electrons);
301 AddElectrons(delta->conduction_ions, cluster.ions);
302 } else {
303 // Add the delta electron to the list, for later use.
304 Electron deltaElectron;
305 deltaElectron.x = delta->position().x * 0.1 + m_cX;
306 deltaElectron.y = delta->position().y * 0.1 + m_cY;
307 deltaElectron.z = delta->position().z * 0.1 + m_cZ;
308 deltaElectron.t = delta->time();
309 deltaElectron.e = delta->kinetic_energy() * 1.e6;
310 deltaElectron.dx = delta->direction().x;
311 deltaElectron.dy = delta->direction().y;
312 deltaElectron.dz = delta->direction().z;
313 cluster.electrons.push_back(std::move(deltaElectron));
314 }
315 continue;
316 }
317 // Is the secondary a real photon?
318 auto photon = dynamic_cast<Heed::HeedPhoton*>(secondary);
319 if (!photon) {
320 std::cerr << m_className << "::AddCluster:\n"
321 << " Particle is neither an electron nor a photon.\n";
322 continue;
323 }
324 cluster.extra += photon->m_energy * 1.e6;
325 const double x = photon->position().x * 0.1 + m_cX;
326 const double y = photon->position().y * 0.1 + m_cY;
327 const double z = photon->position().z * 0.1 + m_cZ;
328 if (!IsInside(x, y, z)) continue;
329 // Transport the photon.
330 if (m_doPhotonReabsorption) {
331 photon->fly(newSecondaries);
332 } else {
333 Photon unabsorbedPhoton;
334 unabsorbedPhoton.x = photon->position().x * 0.1 + m_cX;
335 unabsorbedPhoton.y = photon->position().y * 0.1 + m_cY;
336 unabsorbedPhoton.z = photon->position().z * 0.1 + m_cZ;
337 unabsorbedPhoton.t = photon->time();
338 unabsorbedPhoton.e = photon->m_energy * 1.e6;
339 unabsorbedPhoton.dx = photon->direction().x;
340 unabsorbedPhoton.dy = photon->direction().y;
341 unabsorbedPhoton.dz = photon->direction().z;
342 cluster.photons.push_back(std::move(unabsorbedPhoton));
343 }
344 }
345 for (auto secondary : secondaries)
346 if (secondary) delete secondary;
347 secondaries.clear();
348 secondaries.swap(newSecondaries);
349 }
350 clusters.push_back(std::move(cluster));
351 return true;
352}
353
354void TrackHeed::AddElectrons(
355 const std::vector<Heed::HeedCondElectron>& conductionElectrons,
356 std::vector<Electron>& electrons) {
357
358 for (const auto& conductionElectron : conductionElectrons) {
359 Electron electron;
360 electron.x = conductionElectron.x * 0.1 + m_cX;
361 electron.y = conductionElectron.y * 0.1 + m_cY;
362 electron.z = conductionElectron.z * 0.1 + m_cZ;
363 electron.t = conductionElectron.time;
364 electrons.push_back(std::move(electron));
365 }
366}
367
368bool TrackHeed::GetCluster(double& xc, double& yc, double& zc,
369 double& tc, int& ne,
370 double& ec, double& extra) {
371 int ni = 0, np = 0;
372 return GetCluster(xc, yc, zc, tc, ne, ni, np, ec, extra);
373}
374
375bool TrackHeed::GetCluster(double& xc, double& yc, double& zc,
376 double& tc, int& ne, int& ni,
377 double& ec, double& extra) {
378 int np = 0;
379 return GetCluster(xc, yc, zc, tc, ne, ni, np, ec, extra);
380}
381
382bool TrackHeed::GetCluster(double& xc, double& yc, double& zc,
383 double& tc, int& ne, int& ni, int& np,
384 double& ec, double& extra) {
385 // Initialise.
386 xc = yc = zc = tc = ec = extra = 0.;
387 ne = ni = np = 0;
388
389 if (m_clusters.empty()) return false;
390 // Increment the cluster index.
391 if (m_cluster < m_clusters.size()) {
392 ++m_cluster;
393 } else if (m_cluster > m_clusters.size()) {
394 m_cluster = 0;
395 }
396 if (m_cluster >= m_clusters.size()) return false;
397
398 ne = m_clusters[m_cluster].electrons.size();
399 ni = m_clusters[m_cluster].ions.size();
400 np = m_clusters[m_cluster].photons.size();
401 xc = m_clusters[m_cluster].x;
402 yc = m_clusters[m_cluster].y;
403 zc = m_clusters[m_cluster].z;
404 tc = m_clusters[m_cluster].t;
405 ec = m_clusters[m_cluster].energy;
406 extra = m_clusters[m_cluster].extra;
407 return true;
408}
409
410bool TrackHeed::GetElectron(const unsigned int i, double& x, double& y,
411 double& z, double& t, double& e, double& dx,
412 double& dy, double& dz) {
413
414 if (m_clusters.empty() || m_cluster >= m_clusters.size()) return false;
415 // Make sure an electron with this number exists.
416 if (i >= m_clusters[m_cluster].electrons.size()) {
417 std::cerr << m_className << "::GetElectron: Index out of range.\n";
418 return false;
419 }
420 const auto& electron = m_clusters[m_cluster].electrons[i];
421 x = electron.x;
422 y = electron.y;
423 z = electron.z;
424 t = electron.t;
425 e = electron.e;
426 dx = electron.dx;
427 dy = electron.dy;
428 dz = electron.dz;
429 return true;
430}
431
432bool TrackHeed::GetIon(const unsigned int i, double& x, double& y, double& z,
433 double& t) const {
434 if (m_clusters.empty() || m_cluster >= m_clusters.size()) return false;
435 // Make sure an ion with this index exists.
436 if (i >= m_clusters[m_cluster].ions.size()) {
437 std::cerr << m_className << "::GetIon: Index out of range.\n";
438 return false;
439 }
440 const auto& ion = m_clusters[m_cluster].ions[i];
441 x = ion.x;
442 y = ion.y;
443 z = ion.z;
444 t = ion.t;
445 return true;
446}
447
448bool TrackHeed::GetPhoton(const unsigned int i, double& x, double& y,
449 double& z, double& t, double& e, double& dx,
450 double& dy, double& dz) const {
451 if (m_clusters.empty() || m_cluster >= m_clusters.size()) return false;
452 // Make sure a photon with this index exists.
453 if (i >= m_clusters[m_cluster].photons.size()) {
454 std::cerr << m_className << "::GetPhoton: Index out of range.\n";
455 return false;
456 }
457 const auto& photon = m_clusters[m_cluster].photons[i];
458 x = photon.x;
459 y = photon.y;
460 z = photon.z;
461 t = photon.t;
462 e = photon.e;
463 dx = photon.dx;
464 dy = photon.dy;
465 dz = photon.dz;
466 return true;
467}
468
469void TrackHeed::TransportDeltaElectron(const double x0, const double y0,
470 const double z0, const double t0,
471 const double e0, const double dx0,
472 const double dy0, const double dz0,
473 int& ne) {
474 int ni = 0;
475 return TransportDeltaElectron(x0, y0, z0, t0, e0, dx0, dy0, dz0, ne, ni);
476}
477
478void TrackHeed::TransportDeltaElectron(const double x0, const double y0,
479 const double z0, const double t0,
480 const double e0, const double dx0,
481 const double dy0, const double dz0,
482 int& ne, int& ni) {
483 ne = ni = 0;
484 // Check if delta electron transport was disabled.
485 if (!m_doDeltaTransport) {
486 std::cerr << m_className << "::TransportDeltaElectron:\n"
487 << " Delta electron transport has been switched off.\n";
488 return;
489 }
490
491 // Make sure the sensor has been set.
492 if (!m_sensor) {
493 std::cerr << m_className << "::TransportDeltaElectron:\n"
494 << " Sensor is not defined.\n";
495 return;
496 }
497
498 bool update = false;
499 if (!UpdateBoundingBox(update)) return;
500
501 // Make sure the initial position is inside an ionisable medium.
502 Medium* medium = m_sensor->GetMedium(x0, y0, z0);
503 if (!medium || !medium->IsIonisable()) {
504 std::cerr << m_className << "::TransportDeltaElectron:\n"
505 << " No ionisable medium at initial position.\n";
506 return;
507 }
508
509 // Check if the medium has changed since the last call.
510 if (medium->GetName() != m_mediumName ||
511 fabs(medium->GetMassDensity() - m_mediumDensity) > 1.e-9) {
512 m_isChanged = true;
513 update = true;
514 m_hasActiveTrack = false;
515 }
516
517 // If medium or bounding box have changed, update the "chamber".
518 if (update) {
519 if (!Initialise(medium)) return;
520 m_mediumName = medium->GetName();
521 m_mediumDensity = medium->GetMassDensity();
522 }
523 m_clusters.clear();
524 m_cluster = 0;
525
526 // Initial position (shift with respect to bounding box center and
527 // convert from cm to mm).
528 Heed::point p0((x0 - m_cX) * 10., (y0 - m_cY) * 10., (z0 - m_cZ) * 10.);
529
530 Cluster cluster;
531 // Make sure the kinetic energy is positive.
532 if (e0 <= 0.) {
533 // Just create a conduction electron on the spot.
534 Electron electron;
535 electron.x = x0;
536 electron.y = y0;
537 electron.z = z0;
538 electron.t = t0;
539 cluster.electrons.push_back(std::move(electron));
540 m_clusters.push_back(std::move(cluster));
541 ne = 1;
542 return;
543 }
544
545 // Calculate the speed for the given kinetic energy.
546 const double gamma = 1. + e0 / ElectronMass;
547 const double beta = sqrt(1. - 1. / (gamma * gamma));
548 const double speed = Heed::CLHEP::c_light * beta;
549 // Set the velocity vector.
550 Heed::vec velocity = NormaliseDirection(dx0, dy0, dz0) * speed;
551
552 // Transport the electron.
553 std::vector<Heed::gparticle*> secondaries;
554 Heed::HeedDeltaElectron delta(m_chamber.get(), p0, velocity, t0, 0,
555 m_fieldMap.get());
556 delta.fly(secondaries);
557 ClearBank(secondaries);
558
559 AddElectrons(delta.conduction_electrons, cluster.electrons);
560 AddElectrons(delta.conduction_ions, cluster.ions);
561 ne = cluster.electrons.size();
562 ni = cluster.ions.size();
563 m_clusters.push_back(std::move(cluster));
564}
565
566void TrackHeed::TransportPhoton(const double x0, const double y0,
567 const double z0, const double t0,
568 const double e0, const double dx0,
569 const double dy0, const double dz0, int& ne) {
570 int ni = 0, np = 0;
571 TransportPhoton(x0, y0, z0, t0, e0, dx0, dy0, dz0, ne, ni, np);
572}
573
574void TrackHeed::TransportPhoton(const double x0, const double y0,
575 const double z0, const double t0,
576 const double e0, const double dx0,
577 const double dy0, const double dz0, int& ne,
578 int& ni) {
579 int np = 0;
580 TransportPhoton(x0, y0, z0, t0, e0, dx0, dy0, dz0, ne, ni, np);
581}
582
583void TrackHeed::TransportPhoton(const double x0, const double y0,
584 const double z0, const double t0,
585 const double e0, const double dx0,
586 const double dy0, const double dz0, int& ne,
587 int& ni, int& np) {
588 ne = ni = np = 0;
589 // Make sure the energy is positive.
590 if (e0 <= 0.) {
591 std::cerr << m_className << "::TransportPhoton:\n"
592 << " Photon energy must be positive.\n";
593 return;
594 }
595
596 // Make sure the sensor has been set.
597 if (!m_sensor) {
598 std::cerr << m_className << "::TransportPhoton: Sensor is not defined.\n";
599 return;
600 }
601
602 bool update = false;
603 if (!UpdateBoundingBox(update)) return;
604
605 // Make sure the initial position is inside an ionisable medium.
606 Medium* medium = m_sensor->GetMedium(x0, y0, z0);
607 if (!medium || !medium->IsIonisable()) {
608 std::cerr << m_className << "::TransportPhoton:\n"
609 << " No ionisable medium at initial position.\n";
610 return;
611 }
612
613 // Check if the medium has changed since the last call.
614 if (medium->GetName() != m_mediumName ||
615 fabs(medium->GetMassDensity() - m_mediumDensity) > 1.e-9) {
616 m_isChanged = true;
617 update = true;
618 }
619
620 // If medium or bounding box have changed, update the "chamber".
621 if (update) {
622 if (!Initialise(medium)) return;
623 m_mediumName = medium->GetName();
624 m_mediumDensity = medium->GetMassDensity();
625 }
626
627 // Clusters from the current track will be lost.
628 m_hasActiveTrack = false;
629 m_clusters.clear();
630 m_cluster = 0;
631 Cluster cluster;
632
633 // Set the direction vector.
634 Heed::vec velocity = NormaliseDirection(dx0, dy0, dz0);
635 velocity = velocity * Heed::CLHEP::c_light;
636
637 // Initial position (shift with respect to bounding box center and
638 // convert from cm to mm).
639 Heed::point p0((x0 - m_cX) * 10., (y0 - m_cY) * 10., (z0 - m_cZ) * 10.);
640
641 // Create and transport the photon.
642 Heed::HeedPhoton photon(m_chamber.get(), p0, velocity, t0, 0, e0 * 1.e-6,
643 m_fieldMap.get());
644 std::vector<Heed::gparticle*> secondaries;
645 photon.fly(secondaries);
646 if (secondaries.empty()) {
647 Photon unabsorbedPhoton;
648 unabsorbedPhoton.x = photon.position().x * 0.1 + m_cX;
649 unabsorbedPhoton.y = photon.position().y * 0.1 + m_cY;
650 unabsorbedPhoton.z = photon.position().z * 0.1 + m_cZ;
651 unabsorbedPhoton.t = photon.time();
652 unabsorbedPhoton.e = photon.m_energy * 1.e6;
653 unabsorbedPhoton.dx = photon.direction().x;
654 unabsorbedPhoton.dy = photon.direction().y;
655 unabsorbedPhoton.dz = photon.direction().z;
656 cluster.photons.push_back(std::move(unabsorbedPhoton));
657 }
658
659 while (!secondaries.empty()) {
660 std::vector<Heed::gparticle*> newSecondaries;
661 // Loop over the particle bank and look for daughter particles.
662 for (auto gp : secondaries) {
663 // Is it a delta electron?
664 auto delta = dynamic_cast<Heed::HeedDeltaElectron*>(gp);
665 if (delta) {
666 if (m_doDeltaTransport) {
667 // Transport the delta electron.
668 delta->fly(newSecondaries);
669 // Add the conduction electrons and ions to the list.
670 AddElectrons(delta->conduction_electrons, cluster.electrons);
671 AddElectrons(delta->conduction_ions, cluster.ions);
672 } else {
673 // Add the delta electron to the list, for later use.
674 Electron deltaElectron;
675 deltaElectron.x = delta->position().x * 0.1 + m_cX;
676 deltaElectron.y = delta->position().y * 0.1 + m_cY;
677 deltaElectron.z = delta->position().z * 0.1 + m_cZ;
678 deltaElectron.t = delta->time();
679 deltaElectron.e = delta->kinetic_energy() * 1.e6;
680 deltaElectron.dx = delta->direction().x;
681 deltaElectron.dy = delta->direction().y;
682 deltaElectron.dz = delta->direction().z;
683 cluster.electrons.push_back(std::move(deltaElectron));
684 }
685 continue;
686 }
687 // Check if it is a fluorescence photon.
688 auto fluorescencePhoton = dynamic_cast<Heed::HeedPhoton*>(gp);
689 if (!fluorescencePhoton) {
690 std::cerr << m_className << "::TransportPhoton:\n"
691 << " Unknown secondary particle.\n";
692 ClearBank(secondaries);
693 ClearBank(newSecondaries);
694 return;
695 }
696 if (m_doPhotonReabsorption) {
697 fluorescencePhoton->fly(newSecondaries);
698 } else {
699 Photon unabsorbedPhoton;
700 unabsorbedPhoton.x = fluorescencePhoton->position().x * 0.1 + m_cX;
701 unabsorbedPhoton.y = fluorescencePhoton->position().y * 0.1 + m_cY;
702 unabsorbedPhoton.z = fluorescencePhoton->position().z * 0.1 + m_cZ;
703 unabsorbedPhoton.t = fluorescencePhoton->time();
704 unabsorbedPhoton.e = fluorescencePhoton->m_energy * 1.e6;
705 unabsorbedPhoton.dx = fluorescencePhoton->direction().x;
706 unabsorbedPhoton.dy = fluorescencePhoton->direction().y;
707 unabsorbedPhoton.dz = fluorescencePhoton->direction().z;
708 cluster.photons.push_back(std::move(unabsorbedPhoton));
709 }
710 }
711 secondaries.swap(newSecondaries);
712 ClearBank(newSecondaries);
713 }
714 ClearBank(secondaries);
715 ne = cluster.electrons.size();
716 ni = cluster.ions.size();
717 np = cluster.photons.size();
718 m_clusters.push_back(std::move(cluster));
719}
720
721void TrackHeed::EnableElectricField() { m_fieldMap->UseEfield(true); }
722void TrackHeed::DisableElectricField() { m_fieldMap->UseEfield(false); }
723
725 m_fieldMap->UseBfield(true);
726 m_useBfieldAuto = false;
727}
728
730 m_fieldMap->UseBfield(false);
731 m_useBfieldAuto = false;
732}
733
734void TrackHeed::SetEnergyMesh(const double e0, const double e1,
735 const int nsteps) {
736 if (fabs(e1 - e0) < Small) {
737 std::cerr << m_className << "::SetEnergyMesh:\n"
738 << " Invalid energy range:\n"
739 << " " << e0 << " < E [eV] < " << e1 << "\n";
740 return;
741 }
742
743 if (nsteps <= 0) {
744 std::cerr << m_className << "::SetEnergyMesh:\n"
745 << " Number of intervals must be > 0.\n";
746 return;
747 }
748
749 m_emin = 1.e-6 * std::min(e0, e1);
750 m_emax = 1.e-6 * std::max(e0, e1);
751 m_nEnergyIntervals = nsteps;
752}
753
754void TrackHeed::SetParticleUser(const double m, const double z) {
755 if (fabs(z) < Small) {
756 std::cerr << m_className << "::SetParticleUser:\n"
757 << " Particle cannot have zero charge.\n";
758 return;
759 }
760 if (m < Small) {
761 std::cerr << m_className << "::SetParticleUser:\n"
762 << " Particle mass must be greater than zero.\n";
763 }
764 m_q = z;
765 m_mass = m;
766 m_isElectron = false;
767 m_spin = 0;
768 m_particleName = "exotic";
769}
770
771bool TrackHeed::Initialise(Medium* medium, const bool verbose) {
772 // Make sure the path to the Heed database is known.
773 std::string databasePath;
774 char* dbPath = std::getenv("HEED_DATABASE");
775 if (dbPath) {
776 databasePath = dbPath;
777 } else {
778 // Try GARFIELD_INSTALL.
779 dbPath = std::getenv("GARFIELD_INSTALL");
780 if (dbPath) {
781 databasePath = std::string(dbPath) + "/share/Heed/database";
782 } else {
783 // Try GARFIELD_HOME.
784 dbPath = std::getenv("GARFIELD_HOME");
785 if (dbPath) {
786 databasePath = std::string(dbPath) + "/Heed/heed++/database";
787 } else {
788 }
789 }
790 }
791 if (databasePath.empty()) {
792 std::cerr << m_className << "::Initialise:\n"
793 << " Cannot retrieve database path (none of the"
794 << " environment variables HEED_DATABASE, GARFIELD_INSTALL,"
795 << " GARFIELD_HOME is defined).\n"
796 << " Cannot proceed.\n";
797 return false;
798 }
799 if (databasePath.back() != '/') databasePath.append("/");
800
801 if (m_debug || verbose) {
802 std::cout << m_className << "::Initialise:\n"
803 << " Database path: " << databasePath << "\n";
804 }
805
806 // Make sure the medium exists.
807 if (!medium) {
808 std::cerr << m_className << "::Initialise: Null pointer.\n";
809 return false;
810 }
811
812 // Setup the energy mesh.
813 m_energyMesh.reset(new Heed::EnergyMesh(m_emin, m_emax, m_nEnergyIntervals));
814
815 if (medium->IsGas()) {
816 if (!SetupGas(medium)) return false;
817 } else {
818 if (!SetupMaterial(medium)) return false;
819 }
820
821 // Energy transfer cross-section
822 // Set a flag indicating whether the primary particle is an electron.
823 m_transferCs.reset(new Heed::EnTransfCS(1.e-6 * m_mass, GetGamma() - 1.,
824 m_isElectron, m_matter.get(),
825 m_q));
826 if (!m_transferCs->m_ok) {
827 std::cerr << m_className << "::Initialise:\n"
828 << " Problems occured when calculating the differential"
829 << " cross-section table.\n"
830 << " Results will be inaccurate.\n";
831 }
832 if (!SetupDelta(databasePath)) return false;
833
834 if (m_debug || verbose) {
835 const double nc = m_transferCs->quanC;
836 const double dedx = m_transferCs->meanC * 1.e3;
837 const double dedx1 = m_transferCs->meanC1 * 1.e3;
838 const double w = m_matter->W * 1.e6;
839 const double f = m_matter->F;
840 const double minI = m_matter->min_ioniz_pot * 1.e6;
841 std::cout << m_className << "::Initialise:\n";
842 std::cout << " Cluster density: " << nc << " cm-1\n";
843 std::cout << " Stopping power (restricted): " << dedx << " keV/cm\n";
844 std::cout << " Stopping power (incl. tail): " << dedx1 << " keV/cm\n";
845 std::cout << " W value: " << w << " eV\n";
846 std::cout << " Fano factor: " << f << "\n";
847 std::cout << " Min. ionization potential: " << minI << " eV\n";
848 }
849
850 Heed::fixsyscoor primSys(Heed::point(0., 0., 0.), Heed::basis("primary"),
851 "primary");
852 m_chamber.reset(new HeedChamber(primSys, m_lX, m_lY, m_lZ,
853 *m_transferCs.get(), *m_deltaCs.get()));
854 m_fieldMap->SetSensor(m_sensor);
855 return true;
856}
857
858bool TrackHeed::SetupGas(Medium* medium) {
859 // Get temperature and pressure.
860 double pressure = medium->GetPressure();
861 pressure = (pressure / AtmosphericPressure) * Heed::CLHEP::atmosphere;
862 double temperature = medium->GetTemperature();
863
864 const unsigned int nComponents = medium->GetNumberOfComponents();
865 if (nComponents < 1) {
866 std::cerr << m_className << "::SetupGas:\n"
867 << " Gas " << medium->GetName() << " has zero constituents.\n";
868 return false;
869 }
870
871 std::vector<Heed::MolecPhotoAbsCS> mpacs;
872 std::vector<std::string> notations;
873 std::vector<double> fractions;
874 for (unsigned int i = 0; i < nComponents; ++i) {
875 std::string gasname;
876 double frac;
877 medium->GetComponent(i, gasname, frac);
878 if (gasname == "paraH2" || gasname == "orthoD2" ||
879 gasname == "D2") {
880 gasname = "H2";
881 } else if (gasname == "He-3") {
882 gasname = "He";
883 } else if (gasname == "CD4") {
884 gasname = "CH4";
885 } else if (gasname == "iC4H10" || gasname == "nC4H10") {
886 gasname = "C4H10";
887 } else if (gasname == "neoC5H12" || gasname == "nC5H12") {
888 gasname = "C5H12";
889 } else if (gasname == "cC3H6") {
890 gasname = "C3H6";
891 } else if (gasname == "nC3H7OH") {
892 gasname = "C3H7OH";
893 }
894 // Assemble the molecular photoabsorption cross-section.
895 if (gasname == "CF4") {
896 mpacs.emplace_back(makeMPACS("C for CF4", 1, "F", 4));
897 } else if (gasname == "Ar") {
898 mpacs.emplace_back(makeMPACS("Ar", 1, 26.4e-6));
899 } else if (gasname == "He" || gasname == "He-3") {
900 mpacs.emplace_back(makeMPACS("He", 1, 41.3e-6));
901 } else if (gasname == "Ne") {
902 mpacs.emplace_back(makeMPACS("Ne", 1, 35.4e-6));
903 } else if (gasname == "Kr") {
904 mpacs.emplace_back(makeMPACS("Kr", 1, 24.4e-6));
905 } else if (gasname == "Xe") {
906 mpacs.emplace_back(makeMPACS("Xe", 1, 22.1e-6));
907 } else if (gasname == "CH4" || gasname == "CD4") {
908 mpacs.emplace_back(makeMPACS("C for CH4", 1, "H for CH4", 4, 27.3e-6));
909 } else if (gasname == "C2H6") {
910 mpacs.emplace_back(makeMPACS("C for C2H6", 2, "H for H2", 6, 25.0e-6));
911 } else if (gasname == "C3H8") {
912 mpacs.emplace_back(makeMPACS("C for CH4", 3, "H for H2", 8, 24.0e-6));
913 } else if (gasname == "C4H10") {
914 mpacs.emplace_back(makeMPACS("C for C4H10", 4, "H for H2", 10, 23.4e-6));
915 } else if (gasname == "CO2") {
916 mpacs.emplace_back(makeMPACS("C for CO2", 1, "O for CO2", 2, 33.0e-6));
917 } else if (gasname == "C5H12") {
918 mpacs.emplace_back(makeMPACS("C for C4H10", 5, "H for H2", 12, 23.2e-6));
919 } else if (gasname == "Water" || gasname == "H2O") {
920 mpacs.emplace_back(makeMPACS("H for H2", 2, "O", 1, 29.6e-6));
921 } else if (gasname == "O2") {
922 mpacs.emplace_back(makeMPACS("O", 2, 30.8e-6));
923 } else if (gasname == "N2" || gasname == "N2 (Phelps)") {
924 mpacs.emplace_back(makeMPACS("N", 2, 34.8e-6));
925 } else if (gasname == "NO") {
926 mpacs.emplace_back(makeMPACS("N", 1, "O", 1));
927 } else if (gasname == "N2O") {
928 mpacs.emplace_back(makeMPACS("N", 2, "O", 1, 34.8e-6));
929 } else if (gasname == "C2H4") {
930 mpacs.emplace_back(makeMPACS("C for C2H4", 2, "H for H2", 4, 25.8e-6));
931 } else if (gasname == "C2H2") {
932 mpacs.emplace_back(makeMPACS("C for CH4", 2, "H for H2", 2, 25.8e-6));
933 } else if (gasname == "H2" || gasname == "D2") {
934 mpacs.emplace_back(makeMPACS("H for H2", 2));
935 } else if (gasname == "CO") {
936 mpacs.emplace_back(makeMPACS("C for CO2", 1, "O", 1));
937 } else if (gasname == "Methylal") {
938 // W similar to C4H10
939 mpacs.emplace_back(makeMPACS("O", 2, "C for Methylal", 3,
940 "H for H2", 8, 10.0e-6 * 23.4 / 10.55));
941 } else if (gasname == "DME") {
942 mpacs.emplace_back(makeMPACS("C for Methylal", 2, "H for H2", 6, "O", 1));
943 } else if (gasname == "C2F6") {
944 mpacs.emplace_back(makeMPACS("C for C2H6", 2, "F", 6));
945 } else if (gasname == "SF6") {
946 mpacs.emplace_back(makeMPACS("S", 1, "F", 6));
947 } else if (gasname == "NH3") {
948 mpacs.emplace_back(makeMPACS("N", 1, "H for NH4", 3, 26.6e-6));
949 } else if (gasname == "C3H6" || gasname == "cC3H6") {
950 mpacs.emplace_back(makeMPACS("C for C2H6", 3, "H for H2", 6));
951 } else if (gasname == "CH3OH") {
952 mpacs.emplace_back(makeMPACS("C for C2H6", 1, "H for H2", 4,
953 "O", 1, 24.7e-6));
954 } else if (gasname == "C2H5OH") {
955 mpacs.emplace_back(makeMPACS("C for C2H6", 2, "H for H2", 6,
956 "O", 1, 24.8e-6));
957 } else if (gasname == "C3H7OH") {
958 mpacs.emplace_back(makeMPACS("C for C2H6", 3, "H for H2", 8, "O", 1));
959 } else if (gasname == "Cs") {
960 mpacs.emplace_back(makeMPACS("Cs", 1));
961 } else if (gasname == "F2") {
962 mpacs.emplace_back(makeMPACS("F", 2));
963 } else if (gasname == "CS2") {
964 mpacs.emplace_back(makeMPACS("C for CO2", 1, "S", 2));
965 } else if (gasname == "COS") {
966 mpacs.emplace_back(makeMPACS("C for CO2", 1, "O", 1, "S", 1));
967 } else if (gasname == "BF3") {
968 mpacs.emplace_back(makeMPACS("B", 1, "F", 3));
969 } else if (gasname == "C2HF5") {
970 mpacs.emplace_back(makeMPACS("C for C2H6", 2, "H for H2", 1, "F", 5));
971 } else if (gasname == "C2H2F4") {
972 mpacs.emplace_back(makeMPACS("C for C2H6", 2, "F", 4, "H for H2", 2));
973 } else if (gasname == "CHF3") {
974 mpacs.emplace_back(makeMPACS("C for CF4", 1, "H for H2", 1, "F", 3));
975 } else if (gasname == "CF3Br") {
976 mpacs.emplace_back(makeMPACS("C for CF4", 1, "F", 3, "Br", 1));
977 } else if (gasname == "C3F8") {
978 mpacs.emplace_back(makeMPACS("C for CF4", 3, "F", 8));
979 } else if (gasname == "O3") {
980 mpacs.emplace_back(makeMPACS("O", 3));
981 } else if (gasname == "Hg") {
982 mpacs.emplace_back(makeMPACS("Hg", 1));
983 } else if (gasname == "H2S") {
984 mpacs.emplace_back(makeMPACS("H for H2", 2, "S", 1));
985 } else if (gasname == "GeH4") {
986 mpacs.emplace_back(makeMPACS("Ge", 1, "H for H2", 4));
987 } else if (gasname == "SiH4") {
988 mpacs.emplace_back(makeMPACS("Si", 1, "H for H2", 4));
989 } else {
990 std::cerr << m_className << "::SetupGas:\n"
991 << " Photoabsorption cross-section for "
992 << gasname << " is not implemented.\n";
993 return false;
994 }
995 notations.push_back(gasname);
996 fractions.push_back(frac);
997 }
998 if (m_usePacsOutput) {
999 std::ofstream pacsfile;
1000 pacsfile.open("heed_pacs.txt", std::ios::out);
1001 const int nValues = m_energyMesh->get_q();
1002 if (nValues > 0) {
1003 for (int i = 0; i < nValues; ++i) {
1004 double e = m_energyMesh->get_e(i);
1005 pacsfile << 1.e6 * e << " ";
1006 for (unsigned int j = 0; j < nComponents; ++j) {
1007 pacsfile << mpacs[j].get_ACS(e) << " " << mpacs[j].get_ICS(e) << " ";
1008 }
1009 pacsfile << "\n";
1010 }
1011 }
1012 pacsfile.close();
1013 }
1014
1015 const std::string gasname = medium->GetName();
1016 m_gas.reset(new Heed::GasDef(gasname, gasname, nComponents, notations,
1017 fractions, pressure, temperature, -1.));
1018
1019 const double w = std::max(medium->GetW() * 1.e-6, 0.);
1020 double f = medium->GetFanoFactor();
1021 if (f <= 0.) f = Heed::standard_factor_Fano;
1022
1023 m_matter.reset(
1024 new Heed::HeedMatterDef(m_energyMesh.get(), m_gas.get(), mpacs, w, f));
1025 return true;
1026}
1027
1028bool TrackHeed::SetupMaterial(Medium* medium) {
1029 // Get temperature and density.
1030 double temperature = medium->GetTemperature();
1031 const double density =
1032 medium->GetMassDensity() * Heed::CLHEP::gram / Heed::CLHEP::cm3;
1033
1034 const unsigned int nComponents = medium->GetNumberOfComponents();
1035 std::vector<Heed::AtomPhotoAbsCS*> atPacs(nComponents, nullptr);
1036 std::vector<std::string> notations;
1037 std::vector<double> fractions;
1038 for (unsigned int i = 0; i < nComponents; ++i) {
1039 std::string materialName;
1040 double frac;
1041 medium->GetComponent(i, materialName, frac);
1042 if (materialName == "C") {
1043 if (medium->GetName() == "Diamond") {
1044 atPacs[i] = Heed::PhotoAbsCSLib::getAPACS("Diamond");
1045 } else {
1046 atPacs[i] = Heed::PhotoAbsCSLib::getAPACS("C");
1047 }
1048 } else if (materialName == "Si") {
1049 atPacs[i] = Heed::PhotoAbsCSLib::getAPACS("Si crystal");
1050 // atPacs[i] = Heed::PhotoAbsCSLib::getAPACS("Si G4");
1051 } else if (materialName == "Ga") {
1052 atPacs[i] = Heed::PhotoAbsCSLib::getAPACS("Ga for GaAs");
1053 } else if (materialName == "Ge") {
1054 atPacs[i] = Heed::PhotoAbsCSLib::getAPACS("Ge crystal");
1055 } else if (materialName == "As") {
1056 atPacs[i] = Heed::PhotoAbsCSLib::getAPACS("As for GaAs");
1057 } else if (materialName == "Cd") {
1058 atPacs[i] = Heed::PhotoAbsCSLib::getAPACS("Cd for CdTe");
1059 } else if (materialName == "Te") {
1060 atPacs[i] = Heed::PhotoAbsCSLib::getAPACS("Te for CdTe");
1061 } else {
1062 std::cerr << m_className << "::SetupMaterial:\n"
1063 << " Photoabsorption cross-section for " << materialName
1064 << " is not implemented.\n";
1065 return false;
1066 }
1067 notations.push_back(materialName);
1068 fractions.push_back(frac);
1069 }
1070 if (m_usePacsOutput) {
1071 std::ofstream pacsfile;
1072 pacsfile.open("heed_pacs.txt", std::ios::out);
1073 const int nValues = m_energyMesh->get_q();
1074 if (nValues > 0) {
1075 for (int i = 0; i < nValues; ++i) {
1076 double e = m_energyMesh->get_e(i);
1077 pacsfile << 1.e6 * e << " ";
1078 for (unsigned int j = 0; j < nComponents; ++j) {
1079 pacsfile << atPacs[j]->get_ACS(e) << " " << atPacs[j]->get_ICS(e)
1080 << " ";
1081 }
1082 pacsfile << "\n";
1083 }
1084 }
1085 pacsfile.close();
1086 }
1087 const std::string materialName = medium->GetName();
1088 m_material.reset(new Heed::MatterDef(materialName, materialName, nComponents,
1089 notations, fractions, density,
1090 temperature));
1091
1092 double w = medium->GetW() * 1.e-6;
1093 if (w < 0.) w = 0.;
1094 double f = medium->GetFanoFactor();
1095 if (f <= 0.) f = Heed::standard_factor_Fano;
1096
1097 m_matter.reset(new Heed::HeedMatterDef(m_energyMesh.get(), m_material.get(),
1098 atPacs, w, f));
1099
1100 return true;
1101}
1102
1103bool TrackHeed::SetupDelta(const std::string& databasePath) {
1104 // Load elastic scattering data.
1105 std::string filename = databasePath + "cbdel.dat";
1106 m_elScat.reset(new Heed::ElElasticScat(filename));
1107
1108 filename = databasePath + "elastic_disp.dat";
1109 m_lowSigma.reset(new Heed::ElElasticScatLowSigma(m_elScat.get(), filename));
1110
1111 // Load data for calculation of ionization.
1112 // Get W value and Fano factor.
1113 const double w = m_matter->W * 1.e6;
1114 const double f = m_matter->F;
1115 filename = databasePath + "delta_path.dat";
1116 m_pairProd.reset(new Heed::PairProd(filename, w, f));
1117
1118 m_deltaCs.reset(new Heed::HeedDeltaElectronCS(
1119 m_matter.get(), m_elScat.get(), m_lowSigma.get(), m_pairProd.get()));
1120 return true;
1121}
1122
1123double TrackHeed::GetW() const { return m_matter->W * 1.e6; }
1124double TrackHeed::GetFanoFactor() const { return m_matter->F; }
1125
1126double TrackHeed::GetPhotoAbsorptionCrossSection(const double en) const {
1127
1128 if (!m_matter) return 0.;
1129 // Convert eV to MeV.
1130 const double e = 1.e-6 * en;
1131 double cs = 0.;
1132 const auto n = m_matter->apacs.size();
1133 for (size_t i = 0; i < n; ++i) {
1134 const double w = m_matter->matter->weight_quan(i);
1135 cs += m_matter->apacs[i]->get_ACS(e) * w;
1136 }
1137 // Convert Mbarn to cm-2.
1138 return cs * 1.e-18;
1139}
1140
1141bool TrackHeed::IsInside(const double x, const double y, const double z) {
1142 // Check if the point is inside the drift area.
1143 if (!m_sensor->IsInArea(x, y, z)) return false;
1144 // Check if the point is inside a medium.
1145 Medium* medium = m_sensor->GetMedium(x, y, z);
1146 if (!medium) return false;
1147 // Make sure the medium has not changed.
1148 if (medium->GetName() != m_mediumName ||
1149 fabs(medium->GetMassDensity() - m_mediumDensity) > 1.e-9 ||
1150 !medium->IsIonisable()) {
1151 return false;
1152 }
1153 return true;
1154}
1155
1156bool TrackHeed::UpdateBoundingBox(bool& update) {
1157 // Get the bounding box.
1158 double xmin = 0., ymin = 0., zmin = 0.;
1159 double xmax = 0., ymax = 0., zmax = 0.;
1160 if (!m_sensor->GetArea(xmin, ymin, zmin, xmax, ymax, zmax)) {
1161 std::cerr << m_className << "::UpdateBoundingBox: Drift area is not set.\n";
1162 return false;
1163 }
1164 // Check if the bounding box has changed.
1165 const double lx = fabs(xmax - xmin);
1166 const double ly = fabs(ymax - ymin);
1167 const double lz = fabs(zmax - zmin);
1168 if (m_debug) {
1169 std::cout << m_className << "::UpdateBoundingBox:\n"
1170 << " Bounding box dimensions:\n"
1171 << " x: " << lx << " cm\n"
1172 << " y: " << ly << " cm\n"
1173 << " z: " << lz << " cm\n";
1174 }
1175 if (fabs(lx - m_lX) > Small || fabs(ly - m_lY) > Small ||
1176 fabs(lz - m_lZ) > Small) {
1177 m_lX = lx;
1178 m_lY = ly;
1179 m_lZ = lz;
1180 m_isChanged = true;
1181 update = true;
1182 m_hasActiveTrack = false;
1183 }
1184 // Update the center of the bounding box.
1185 m_cX = (std::isinf(xmin) || std::isinf(xmax)) ? 0. : 0.5 * (xmin + xmax);
1186 m_cY = (std::isinf(ymin) || std::isinf(ymax)) ? 0. : 0.5 * (ymin + ymax);
1187 m_cZ = (std::isinf(zmin) || std::isinf(zmax)) ? 0. : 0.5 * (zmin + zmax);
1188 if (m_debug) {
1189 std::cout << m_className << "::UpdateBoundingBox:\n"
1190 << " Center of bounding box:\n"
1191 << " x: " << m_cX << " cm\n"
1192 << " y: " << m_cY << " cm\n"
1193 << " z: " << m_cZ << " cm\n";
1194 }
1195
1196 m_fieldMap->SetSensor(m_sensor);
1197 m_fieldMap->SetCentre(m_cX, m_cY, m_cZ);
1198
1199 return true;
1200}
1201}
Abstract base class for media.
Definition Medium.hh:16
virtual void GetComponent(const unsigned int i, std::string &label, double &f)
Get the name and fraction of a given component.
Definition Medium.cc:106
double GetW() const
Get the W value.
Definition Medium.hh:86
virtual double GetMassDensity() const
Get the mass density [g/cm3].
Definition Medium.cc:102
double GetPressure() const
Definition Medium.hh:41
unsigned int GetNumberOfComponents() const
Get number of components of the medium.
Definition Medium.hh:48
const std::string & GetName() const
Get the medium name/identifier.
Definition Medium.hh:26
virtual bool IsGas() const
Is this medium a gas?
Definition Medium.hh:28
bool IsIonisable() const
Is charge deposition by charged particles/photon enabled in this medium?
Definition Medium.hh:81
double GetTemperature() const
Get the temperature [K].
Definition Medium.hh:37
double GetFanoFactor() const
Get the Fano factor.
Definition Medium.hh:90
Medium * GetMedium(const double x, const double y, const double z)
Get the medium at (x, y, z).
Definition Sensor.cc:178
bool IsInArea(const double x, const double y, const double z)
Check if a point is inside the user area.
Definition Sensor.cc:260
void EnableMagneticField()
Take the magnetic field into account in the stepping algorithm.
Definition TrackHeed.cc:724
void SetEnergyMesh(const double e0, const double e1, const int nsteps)
Definition TrackHeed.cc:734
void EnableElectricField()
Take the electric field into account in the stepping algorithm.
Definition TrackHeed.cc:721
void DisableMagneticField()
Do not take the magnetic field into account in the stepping algorithm.
Definition TrackHeed.cc:729
double GetPhotoAbsorptionCrossSection(const double e) const
Return the photoabsorption cross-section at a given energy.
void TransportDeltaElectron(const double x0, const double y0, const double z0, const double t0, const double e0, const double dx0, const double dy0, const double dz0, int &ne, int &ni)
Definition TrackHeed.cc:478
double GetFanoFactor() const
Return the Fano factor of the medium (of the last simulated track).
bool GetPhoton(const unsigned int i, double &x, double &y, double &z, double &t, double &e, double &dx, double &dy, double &dz) const
Definition TrackHeed.cc:448
bool NewTrack(const double x0, const double y0, const double z0, const double t0, const double dx0, const double dy0, const double dz0) override
Definition TrackHeed.cc:87
bool Initialise(Medium *medium, const bool verbose=false)
Compute the differential cross-section for a given medium.
Definition TrackHeed.cc:771
bool GetElectron(const unsigned int i, double &x, double &y, double &z, double &t, double &e, double &dx, double &dy, double &dz)
Definition TrackHeed.cc:410
bool GetIon(const unsigned int i, double &x, double &y, double &z, double &t) const
Definition TrackHeed.cc:432
virtual ~TrackHeed()
Destructor.
Definition TrackHeed.cc:85
TrackHeed()
Default constructor.
Definition TrackHeed.hh:69
void TransportPhoton(const double x0, const double y0, const double z0, const double t0, const double e0, const double dx0, const double dy0, const double dz0, int &ne, int &ni, int &np)
Definition TrackHeed.cc:583
double GetW() const
Return the W value of the medium (of the last simulated track).
void SetParticleUser(const double m, const double z)
Definition TrackHeed.cc:754
bool GetCluster(double &xc, double &yc, double &zc, double &tc, int &nc, double &ec, double &extra) override
Definition TrackHeed.cc:368
double GetClusterDensity() override
Definition TrackHeed.cc:232
void DisableElectricField()
Do not take the electric field into account in the stepping algorithm.
Definition TrackHeed.cc:722
double GetStoppingPower() override
Get the stopping power (mean energy loss [eV] per cm).
Definition TrackHeed.cc:242
Sensor * m_sensor
Definition Track.hh:114
double GetBeta() const
Return the speed ( ) of the projectile.
Definition Track.hh:56
double GetGamma() const
Return the Lorentz factor of the projectile.
Definition Track.hh:58
bool m_isElectron
Definition Track.hh:111
bool m_isChanged
Definition Track.hh:116
ViewDrift * m_viewer
Definition Track.hh:118
std::string m_particleName
Definition Track.hh:112
void PlotCluster(const double x0, const double y0, const double z0)
Definition Track.cc:195
std::string m_className
Definition Track.hh:104
double m_mass
Definition Track.hh:108
Track()=delete
Default constructor.
void PlotNewTrack(const double x0, const double y0, const double z0)
Definition Track.cc:190
std::vector< HeedCondElectron > conduction_electrons
std::vector< HeedCondElectron > conduction_ions
Retrieve electric and magnetic field from Sensor.
double m_energy
Photon energy [MeV].
Definition HeedPhoton.h:35
static AtomPhotoAbsCS * getAPACS(const std::string &name)
Basis.
Definition vec.h:313
static void reset_counter()
Reset the counter.
Definition gparticle.h:198
vfloat time() const
Get the current time of the particle.
Definition gparticle.h:191
const vec & position() const
Get the current position of the particle.
Definition gparticle.h:189
const vec & direction() const
Get the current direction of the particle.
Definition gparticle.h:193
virtual void fly(std::vector< gparticle * > &secondaries)
Transport the particle.
Definition gparticle.h:158
void set_step_limits(const vfloat fmax_range, const vfloat frad_for_straight, const vfloat fmax_straight_arange, const vfloat fmax_circ_arange)
Set limits/parameters for trajectory steps.
Definition gparticle.h:178
Point.
Definition vec.h:368
vfloat x
Definition vec.h:192
vfloat z
Definition vec.h:194
vfloat y
Definition vec.h:193
void RndmDirection(double &dx, double &dy, double &dz, const double length=1.)
Draw a random (isotropic) direction vector.
Definition Random.hh:128
particle_def K_plus_meson_def("K_plus", "K+", 493.677 *MeV/c_squared, 1, 0.0)
particle_def alpha_particle_def("alpha_particle", "alpha", 3727.417 *MeV/c_squared, 2 *eplus, 0.)
particle_def anti_proton_def("", "p-", proton_def)
particle_def deuteron_def("deuteron", "d", 1875.613 *MeV/c_squared, eplus, 0.0)
particle_def electron_def("electron", "e-", electron_mass_c2/c_squared, electron_charge, 0.5)
particle_def muon_minus_def("muon_minus", "mu-", 105.658367 *MeV/c_squared, electron_charge, 0.5)
DoubleAc fabs(const DoubleAc &f)
Definition DoubleAc.h:615
particle_def pi_plus_meson_def("pi_plus", "pi+", 139.56755 *MeV/c_squared, eplus, 0.0)
particle_def muon_plus_def("muon_plus", "mu+", muon_minus_def)
particle_def positron_def("positron", "e+", electron_def)
particle_def K_minus_meson_def("K_minus", "K-", K_plus_meson_def)
particle_def pi_minus_meson_def("pi_minus", "pi-", 139.56755 *MeV/c_squared, -eplus, 0.0)
constexpr double standard_factor_Fano
Definition PhotoAbsCS.h:575
DoubleAc sqrt(const DoubleAc &f)
Definition DoubleAc.cpp:314
particle_def proton_def("proton", "p+", proton_mass_c2/c_squared, eplus, 0.5)
std::vector< Electron > ions
Definition TrackHeed.hh:65
std::vector< Photon > photons
Definition TrackHeed.hh:63
std::vector< Electron > electrons
Definition TrackHeed.hh:64