Garfield++ v2r0
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#include <sstream>
3
7
18
19#include "HeedChamber.hh"
20#include "HeedFieldMap.h"
21
22#include "Sensor.hh"
23#include "ViewDrift.hh"
25#include "GarfieldConstants.hh"
26#include "Random.hh"
27
28#include "TrackHeed.hh"
29
30namespace {
31
32void ClearBank(std::vector<Heed::gparticle*>& bank) {
33
34 std::vector<Heed::gparticle*>::iterator it;
35 std::vector<Heed::gparticle*>::const_iterator end = bank.end();
36 for (it = bank.begin(); it != end; ++it) if (*it) delete *it;
37 bank.clear();
38}
39
40}
41
42// Global functions and variables required by Heed
43namespace Heed {
44
45// Particle id number for book-keeping
47
48}
49
51Heed::trajestep_limit Heed::gtrajlim( 100. * Heed::CLHEP::cm,
52 1000. * Heed::CLHEP::cm,
53 0.1 * Heed::CLHEP::rad,
54 0.2 * Heed::CLHEP::rad);
55
56// Actual class implementation
57
58namespace Garfield {
59
61 : m_ready(false),
62 m_hasActiveTrack(false),
63 m_mediumDensity(-1.),
64 m_mediumName(""),
65 m_usePhotonReabsorption(true),
66 m_usePacsOutput(false),
67 m_doDeltaTransport(true),
68 m_matter(NULL),
69 m_gas(NULL),
70 m_material(NULL),
71 m_atPacs(NULL),
72 m_molPacs(NULL),
73 m_emin(2.e-6),
74 m_emax(2.e-1),
75 m_nEnergyIntervals(200),
76 m_energyMesh(NULL),
77 m_transferCs(NULL),
78 m_elScat(NULL),
79 m_lowSigma(NULL),
80 m_pairProd(NULL),
81 m_deltaCs(NULL),
82 m_chamber(NULL),
83 m_lX(0.),
84 m_lY(0.),
85 m_lZ(0.),
86 m_cX(0.),
87 m_cY(0.),
88 m_cZ(0.) {
89
90 m_className = "TrackHeed";
91 m_conductionElectrons.reserve(1000);
92 m_conductionIons.reserve(1000);
93}
94
96
97 if (m_matter) delete m_matter;
98 if (m_gas) delete m_gas;
99 if (m_material) delete m_material;
100 if (m_atPacs) delete m_atPacs;
101 if (m_molPacs) delete m_molPacs;
102 if (m_energyMesh) delete m_energyMesh;
103 if (m_transferCs) delete m_transferCs;
104 if (m_elScat) delete m_elScat;
105 if (m_lowSigma) delete m_lowSigma;
106 if (m_pairProd) delete m_pairProd;
107 if (m_deltaCs) delete m_deltaCs;
108 if (m_chamber) delete m_chamber;
109}
110
111bool TrackHeed::NewTrack(const double x0, const double y0, const double z0,
112 const double t0, const double dx0, const double dy0,
113 const double dz0) {
114
115 m_hasActiveTrack = false;
116 m_ready = false;
117
118 // Make sure the sensor has been set.
119 if (!m_sensor) {
120 std::cerr << m_className << "::NewTrack:\n"
121 << " Sensor is not defined.\n";
122 return false;
123 }
124
125 // Get the bounding box.
126 double xmin = 0., ymin = 0., zmin = 0.;
127 double xmax = 0., ymax = 0., zmax = 0.;
128 if (!m_sensor->GetArea(xmin, ymin, zmin, xmax, ymax, zmax)) {
129 std::cerr << m_className << "::NewTrack:\n"
130 << " Drift area is not set.\n";
131 return false;
132 }
133 // Check if the bounding box has changed.
134 const double lx = fabs(xmax - xmin);
135 const double ly = fabs(ymax - ymin);
136 const double lz = fabs(zmax - zmin);
137 if (m_debug) {
138 std::cout << m_className << "::NewTrack:\n"
139 << " Bounding box dimensions:\n"
140 << " x: " << lx << " cm\n"
141 << " y: " << ly << " cm\n"
142 << " z: " << lz << " cm\n";
143 }
144 if (fabs(lx - m_lX) > Small || fabs(ly - m_lY) > Small || fabs(lz - m_lZ) > Small) {
145 m_lX = lx;
146 m_lY = ly;
147 m_lZ = lz;
148 m_isChanged = true;
149 }
150 // Update the center of the bounding box.
151 if (std::isinf(xmin) || std::isinf(xmax)) {
152 m_cX = 0.;
153 } else {
154 m_cX = 0.5 * (xmin + xmax);
155 }
156 if (std::isinf(ymin) || std::isinf(ymax)) {
157 m_cY = 0.;
158 } else {
159 m_cY = 0.5 * (ymin + ymax);
160 }
161 if (std::isinf(zmin) || std::isinf(zmax)) {
162 m_cZ = 0.;
163 } else {
164 m_cZ = 0.5 * (zmin + zmax);
165 }
166 if (m_debug) {
167 std::cout << m_className << "::NewTrack:\n"
168 << " Center of bounding box:\n"
169 << " x: " << m_cX << " cm\n"
170 << " y: " << m_cY << " cm\n"
171 << " z: " << m_cZ << " cm\n";
172 }
173
174 m_fieldMap.SetSensor(m_sensor);
175 m_fieldMap.SetCentre(m_cX, m_cY, m_cZ);
176
177 // Make sure the initial position is inside an ionisable medium.
178 Medium* medium = NULL;
179 if (!m_sensor->GetMedium(x0, y0, z0, medium)) {
180 std::cerr << m_className << "::NewTrack:\n"
181 << " No medium at initial position.\n";
182 return false;
183 } else if (!medium->IsIonisable()) {
184 std::cerr << m_className << "::NewTrack:\n"
185 << " Medium at initial position is not ionisable.\n";
186 return false;
187 }
188
189 // Check if the medium has changed since the last call.
190 if (medium->GetName() != m_mediumName ||
191 fabs(medium->GetMassDensity() - m_mediumDensity) > 1.e-9) {
192 m_isChanged = true;
193 }
194
195 // If medium, particle or bounding box have changed,
196 // update the cross-sections.
197 if (m_isChanged) {
198 if (!Setup(medium)) return false;
199 m_isChanged = false;
200 m_mediumName = medium->GetName();
201 m_mediumDensity = medium->GetMassDensity();
202 }
203
204 ClearParticleBank();
205
206 m_deltaElectrons.clear();
207 m_conductionElectrons.clear();
208 m_conductionIons.clear();
209
210 // Check the direction vector.
211 double dx = dx0, dy = dy0, dz = dz0;
212 const double d = sqrt(dx * dx + dy * dy + dz * dz);
213 if (d < Small) {
214 if (m_debug) {
215 std::cout << m_className << "::NewTrack:\n"
216 << " Direction vector has zero norm.\n"
217 << " Initial direction is randomized.\n";
218 }
219 // Null vector. Sample the direction isotropically.
220 RndmDirection(dx, dy, dz);
221 } else {
222 // Normalise the direction vector.
223 dx /= d;
224 dy /= d;
225 dz /= d;
226 }
227 Heed::vec velocity(dx, dy, dz);
228 velocity = velocity * Heed::CLHEP::c_light * GetBeta();
229
230 if (m_debug) {
231 std::cout << m_className << "::NewTrack:\n Track starts at ("
232 << x0 << ", " << y0 << ", " << z0 << ") at time " << t0 << "\n"
233 << " Direction: (" << dx << ", " << dy << ", " << dz << ")\n";
234 }
235
236 // Initial position (shift with respect to bounding box center and
237 // convert from cm to mm).
238 Heed::point p0((x0 - m_cX) * 10., (y0 - m_cY) * 10., (z0 - m_cZ) * 10.);
239
240 // Setup the particle.
242 if (m_particleName == "e-") {
243 particleType = &Heed::electron_def;
244 } else if (m_particleName == "e+") {
245 particleType = &Heed::positron_def;
246 } else if (m_particleName == "mu-") {
247 particleType = &Heed::muon_minus_def;
248 } else if (m_particleName == "mu+") {
249 particleType = &Heed::muon_plus_def;
250 } else if (m_particleName == "pi-") {
251 particleType = &Heed::pi_minus_meson_def;
252 } else if (m_particleName == "pi+") {
253 particleType = &Heed::pi_plus_meson_def;
254 } else if (m_particleName == "K-") {
255 particleType = &Heed::K_minus_meson_def;
256 } else if (m_particleName == "K+") {
257 particleType = &Heed::K_plus_meson_def;
258 } else if (m_particleName == "p") {
259 particleType = &Heed::proton_def;
260 } else if (m_particleName == "pbar") {
261 particleType = &Heed::anti_proton_def;
262 } else if (m_particleName == "d") {
263 particleType = &Heed::deuteron_def;
264 } else if (m_particleName == "alpha") {
265 particleType = &Heed::alpha_particle_def;
266 } else if (m_particleName == "exotic") {
267 // User defined particle
270 particleType = &Heed::user_particle_def;
271 } else {
272 // Not a predefined particle, use muon definition.
273 if (m_q > 0.) {
274 particleType = &Heed::muon_minus_def;
275 } else {
276 particleType = &Heed::muon_plus_def;
277 }
278 }
279
280 Heed::HeedParticle particle(m_chamber, p0, velocity, t0,
281 particleType, &m_fieldMap);
282 // Transport the particle.
283 particle.fly(m_particleBank);
284 m_bankIterator = m_particleBank.begin();
285 m_hasActiveTrack = true;
286 m_ready = true;
287
288 // Plot the new track.
289 if (m_usePlotting) PlotNewTrack(x0, y0, z0);
290 return true;
291}
292
294
295 if (!m_ready) {
296 std::cerr << m_className << "::GetClusterDensity:\n"
297 << " Track has not been initialized.\n";
298 return 0.;
299 }
300
301 if (!m_transferCs) {
302 std::cerr << m_className << "::GetClusterDensity:\n"
303 << " Ionisation cross-section is not available.\n";
304 return 0.;
305 }
306
307 return m_transferCs->quanC;
308}
309
311
312 if (!m_ready) {
313 std::cerr << m_className << "::GetStoppingPower:\n"
314 << " Track has not been initialized.\n";
315 return 0.;
316 }
317
318 if (!m_transferCs) {
319 std::cerr << m_className << "::GetStoppingPower:\n"
320 << " Ionisation cross-section is not available.\n";
321 return 0.;
322 }
323
324 return m_transferCs->meanC1 * 1.e6;
325}
326
327bool TrackHeed::GetCluster(double& xcls, double& ycls, double& zcls,
328 double& tcls, int& n, double& e, double& extra) {
329
330 int ni = 0;
331 return GetCluster(xcls, ycls, zcls, tcls, n, ni, e, extra);
332}
333
334bool TrackHeed::GetCluster(double& xcls, double& ycls, double& zcls,
335 double& tcls, int& ne, int& ni, double& e,
336 double& extra) {
337
338 // Initialise and reset.
339 xcls = ycls = zcls = tcls = 0.;
340 extra = 0.;
341 ne = ni = 0;
342 e = 0.;
343
344 m_deltaElectrons.clear();
345 m_conductionElectrons.clear();
346 m_conductionIons.clear();
347
348 // Make sure NewTrack has been called successfully.
349 if (!m_ready) {
350 std::cerr << m_className << "::GetCluster:\n"
351 << " Track has not been initialized. Call NewTrack first.\n";
352 return false;
353 }
354
355 std::vector<Heed::gparticle*>::const_iterator end = m_particleBank.end();
356 if (m_bankIterator == end) {
357 std::cerr << m_className << "::GetCluster:\n"
358 << " There are no more clusters on this track.\n";
359 return false;
360 }
361
362 // Look for the next cluster (i. e. virtual photon) in the list.
363 Heed::HeedPhoton* virtualPhoton = NULL;
364 for (; m_bankIterator != end; ++m_bankIterator) {
365 // Convert the particle to a (virtual) photon.
366 virtualPhoton = dynamic_cast<Heed::HeedPhoton*>(*m_bankIterator);
367 if (!virtualPhoton) {
368 std::cerr << m_className << "::GetCluster:\n"
369 << " Particle is not a virtual photon. Program bug!\n";
370 // Try the next element.
371 continue;
372 }
373
374 // Get the location of the interaction (convert from mm to cm
375 // and shift with respect to bounding box center).
376 xcls = virtualPhoton->currpos.pt.v.x * 0.1 + m_cX;
377 ycls = virtualPhoton->currpos.pt.v.y * 0.1 + m_cY;
378 zcls = virtualPhoton->currpos.pt.v.z * 0.1 + m_cZ;
379 tcls = virtualPhoton->currpos.time;
380 // Skip clusters outside the drift area or outside the active medium.
381 if (!IsInside(xcls, ycls, zcls)) continue;
382 // Add the first ion (at the position of the cluster).
383 m_conductionIons.push_back(
384 Heed::HeedCondElectron(virtualPhoton->currpos.pt, tcls));
385 ++m_bankIterator;
386 break;
387 }
388
389 // Stop if we did not find a virtual photon.
390 if (m_bankIterator == end || !virtualPhoton) return false;
391 // Plot the cluster, if requested.
392 if (m_usePlotting) PlotCluster(xcls, ycls, zcls);
393
394 std::vector<Heed::gparticle*> secondaries;
395 // Transport the virtual photon.
396 virtualPhoton->fly(secondaries);
397 // Get the transferred energy (convert from MeV to eV).
398 e = virtualPhoton->energy * 1.e6;
399
400 while (!secondaries.empty()) {
401 std::vector<Heed::gparticle*> newSecondaries;
402 // Loop over the secondaries.
403 std::vector<Heed::gparticle*>::iterator it;
404 std::vector<Heed::gparticle*>::const_iterator send = secondaries.end();
405 for (it = secondaries.begin(); it != send; ++it) {
406 // Check if it is a delta electron.
407 Heed::HeedDeltaElectron* delta = dynamic_cast<Heed::HeedDeltaElectron*>(*it);
408 if (delta) {
409 extra += delta->curr_kin_energy * 1.e6;
410 const double x = delta->currpos.pt.v.x * 0.1 + m_cX;
411 const double y = delta->currpos.pt.v.y * 0.1 + m_cY;
412 const double z = delta->currpos.pt.v.z * 0.1 + m_cZ;
413 if (!IsInside(x, y, z)) continue;
414 if (m_doDeltaTransport) {
415 // Transport the delta electron.
416 delta->fly(newSecondaries);
417 // Add the conduction electrons and ions to the list.
418 m_conductionElectrons.insert(m_conductionElectrons.end(),
419 delta->conduction_electrons.begin(),
420 delta->conduction_electrons.end());
421 m_conductionIons.insert(m_conductionIons.end(),
422 delta->conduction_ions.begin(),
423 delta->conduction_ions.end());
424 } else {
425 // Add the delta electron to the list, for later use.
426 deltaElectron newDeltaElectron;
427 newDeltaElectron.x = delta->currpos.pt.v.x * 0.1 + m_cX;
428 newDeltaElectron.y = delta->currpos.pt.v.y * 0.1 + m_cY;
429 newDeltaElectron.z = delta->currpos.pt.v.z * 0.1 + m_cZ;
430 newDeltaElectron.t = delta->currpos.time;
431 newDeltaElectron.e = delta->curr_kin_energy * 1.e6;
432 newDeltaElectron.dx = delta->currpos.dir.x;
433 newDeltaElectron.dy = delta->currpos.dir.y;
434 newDeltaElectron.dz = delta->currpos.dir.z;
435 m_deltaElectrons.push_back(newDeltaElectron);
436 }
437 continue;
438 }
439 // Check if it is a real photon.
440 Heed::HeedPhoton* photon = dynamic_cast<Heed::HeedPhoton*>(*it);
441 if (!photon) {
442 std::cerr << m_className << "::GetCluster:\n"
443 << " Particle is neither an electron nor a photon.\n";
444 }
445 extra += photon->energy * 1.e6;
446 const double x = photon->currpos.pt.v.x * 0.1 + m_cX;
447 const double y = photon->currpos.pt.v.y * 0.1 + m_cY;
448 const double z = photon->currpos.pt.v.z * 0.1 + m_cZ;
449 if (!IsInside(x, y, z)) continue;
450 // Transport the photon.
451 if (m_usePhotonReabsorption) photon->fly(newSecondaries);
452 }
453 for (it = secondaries.begin(); it != secondaries.end(); ++it) {
454 if (*it) delete *it;
455 }
456 secondaries.clear();
457 secondaries.swap(newSecondaries);
458 }
459 // Get the total number of electrons produced in this step.
460 ne = m_doDeltaTransport ? m_conductionElectrons.size() : m_deltaElectrons.size();
461 ni = m_conductionIons.size();
462 return true;
463}
464
465bool TrackHeed::GetElectron(const unsigned int i,
466 double& x, double& y, double& z, double& t,
467 double& e, double& dx, double& dy, double& dz) {
468
469 // Make sure NewTrack has successfully been called.
470 if (!m_ready) {
471 std::cerr << m_className << "::GetElectron:\n"
472 << " Track has not been initialized. Call NewTrack first.\n";
473 return false;
474 }
475
476 if (m_doDeltaTransport) {
477 // Make sure an electron with this number exists.
478 if (i >= m_conductionElectrons.size()) {
479 std::cerr << m_className << "::GetElectron: Index out of range.\n";
480 return false;
481 }
482
483 x = m_conductionElectrons[i].ptloc.v.x * 0.1 + m_cX;
484 y = m_conductionElectrons[i].ptloc.v.y * 0.1 + m_cY;
485 z = m_conductionElectrons[i].ptloc.v.z * 0.1 + m_cZ;
486 t = m_conductionElectrons[i].time;
487 e = 0.;
488 dx = dy = dz = 0.;
489
490 } else {
491 // Make sure a delta electron with this number exists.
492 if (i >= m_deltaElectrons.size()) {
493 std::cerr << m_className << "::GetElectron:\n"
494 << " Delta electron number out of range.\n";
495 return false;
496 }
497
498 x = m_deltaElectrons[i].x;
499 y = m_deltaElectrons[i].y;
500 z = m_deltaElectrons[i].z;
501 t = m_deltaElectrons[i].t;
502 e = m_deltaElectrons[i].e;
503 dx = m_deltaElectrons[i].dx;
504 dy = m_deltaElectrons[i].dy;
505 dz = m_deltaElectrons[i].dz;
506 }
507 return true;
508}
509
510bool TrackHeed::GetIon(const unsigned int i,
511 double& x, double& y, double& z, double& t) const {
512
513 // Make sure a "conduction" ion with this number exists.
514 if (i >= m_conductionIons.size()) {
515 std::cerr << m_className << "::GetIon: Index out of range.\n";
516 return false;
517 }
518
519 x = m_conductionIons[i].ptloc.v.x * 0.1 + m_cX;
520 y = m_conductionIons[i].ptloc.v.y * 0.1 + m_cY;
521 z = m_conductionIons[i].ptloc.v.z * 0.1 + m_cZ;
522 t = m_conductionIons[i].time;
523 return true;
524}
525
526void TrackHeed::TransportDeltaElectron(const double x0, const double y0,
527 const double z0, const double t0,
528 const double e0, const double dx0,
529 const double dy0, const double dz0,
530 int& nel) {
531 int ni = 0;
532 return TransportDeltaElectron(x0, y0, z0, t0, e0, dx0, dy0, dz0, nel, ni);
533}
534
535void TrackHeed::TransportDeltaElectron(const double x0, const double y0,
536 const double z0, const double t0,
537 const double e0, const double dx0,
538 const double dy0, const double dz0,
539 int& nel, int& ni) {
540
541 nel = 0;
542 ni = 0;
543
544 // Check if delta electron transport was disabled.
545 if (!m_doDeltaTransport) {
546 std::cerr << m_className << "::TransportDeltaElectron:\n"
547 << " Delta electron transport has been switched off.\n";
548 return;
549 }
550
551
552 // Make sure the sensor has been set.
553 if (!m_sensor) {
554 std::cerr << m_className << "::TransportDeltaElectron:\n"
555 << " Sensor is not defined.\n";
556 m_ready = false;
557 return;
558 }
559
560 // Get the bounding box.
561 double xmin, ymin, zmin;
562 double xmax, ymax, zmax;
563 if (!m_sensor->GetArea(xmin, ymin, zmin, xmax, ymax, zmax)) {
564 std::cerr << m_className << "::TransportDeltaElectron:\n"
565 << " Drift area is not set.\n";
566 m_ready = false;
567 return;
568 }
569 // Check if the bounding box has changed.
570 bool update = false;
571 const double lx = fabs(xmax - xmin);
572 const double ly = fabs(ymax - ymin);
573 const double lz = fabs(zmax - zmin);
574 if (fabs(lx - m_lX) > Small || fabs(ly - m_lY) > Small || fabs(lz - m_lZ) > Small) {
575 m_lX = lx;
576 m_lY = ly;
577 m_lZ = lz;
578 m_isChanged = true;
579 update = true;
580 m_hasActiveTrack = false;
581 }
582 // Update the center of the bounding box.
583 m_cX = 0.5 * (xmin + xmax);
584 m_cY = 0.5 * (ymin + ymax);
585 m_cZ = 0.5 * (zmin + zmax);
586
587 m_fieldMap.SetSensor(m_sensor);
588 m_fieldMap.SetCentre(m_cX, m_cY, m_cZ);
589
590 // Make sure the initial position is inside an ionisable medium.
591 Medium* medium = NULL;
592 if (!m_sensor->GetMedium(x0, y0, z0, medium)) {
593 std::cerr << m_className << "::TransportDeltaElectron:\n"
594 << " No medium at initial position.\n";
595 return;
596 } else if (!medium->IsIonisable()) {
597 std::cerr << "TrackHeed:TransportDeltaElectron:\n"
598 << " Medium at initial position is not ionisable.\n";
599 m_ready = false;
600 return;
601 }
602
603 // Check if the medium has changed since the last call.
604 if (medium->GetName() != m_mediumName ||
605 fabs(medium->GetMassDensity() - m_mediumDensity) > 1.e-9) {
606 m_isChanged = true;
607 update = true;
608 m_ready = false;
609 m_hasActiveTrack = false;
610 }
611
612 // If medium or bounding box have changed, update the "chamber".
613 if (update) {
614 if (!Setup(medium)) return;
615 m_ready = true;
616 m_mediumName = medium->GetName();
617 m_mediumDensity = medium->GetMassDensity();
618 }
619
620 m_deltaElectrons.clear();
621 m_conductionElectrons.clear();
622 m_conductionIons.clear();
623
624 // Initial position (shift with respect to bounding box center and
625 // convert from cm to mm).
626 Heed::point p0((x0 - m_cX) * 10., (y0 - m_cY) * 10., (z0 - m_cZ) * 10.);
627
628 // Make sure the kinetic energy is positive.
629 if (e0 <= 0.) {
630 // Just create a conduction electron on the spot.
631 m_conductionElectrons.push_back(Heed::HeedCondElectron(p0, t0));
632 nel = 1;
633 return;
634 }
635
636 // Check the direction vector.
637 double dx = dx0, dy = dy0, dz = dz0;
638 const double d = sqrt(dx * dx + dy * dy + dz * dz);
639 if (d <= 0.) {
640 // Null vector. Sample the direction isotropically.
641 RndmDirection(dx, dy, dz);
642 } else {
643 // Normalise the direction vector.
644 dx /= d;
645 dy /= d;
646 dz /= d;
647 }
648 Heed::vec velocity(dx, dy, dz);
649
650 // Calculate the speed for the given kinetic energy.
651 const double gamma = 1. + e0 / ElectronMass;
652 const double beta = sqrt(1. - 1. / (gamma * gamma));
653 double speed = Heed::CLHEP::c_light * beta;
654 velocity = velocity * speed;
655
656 // Transport the electron.
657 std::vector<Heed::gparticle*> secondaries;
658 Heed::HeedDeltaElectron delta(m_chamber, p0, velocity, t0, 0, &m_fieldMap);
659 delta.fly(secondaries);
660 ClearBank(secondaries);
661
662 m_conductionElectrons.swap(delta.conduction_electrons);
663 m_conductionIons.swap(delta.conduction_ions);
664 nel = m_conductionElectrons.size();
665 ni = m_conductionIons.size();
666}
667
668void TrackHeed::TransportPhoton(const double x0, const double y0,
669 const double z0, const double t0,
670 const double e0, const double dx0,
671 const double dy0, const double dz0, int& nel) {
672 int ni = 0;
673 TransportPhoton(x0, y0, z0, t0, e0, dx0, dy0, dz0, nel, ni);
674}
675
676void TrackHeed::TransportPhoton(const double x0, const double y0,
677 const double z0, const double t0,
678 const double e0, const double dx0,
679 const double dy0, const double dz0,
680 int& nel, int& ni) {
681
682 nel = 0;
683 ni = 0;
684
685 // Make sure the energy is positive.
686 if (e0 <= 0.) {
687 std::cerr << m_className << "::TransportPhoton:\n"
688 << " Photon energy must be positive.\n";
689 return;
690 }
691
692 // Make sure the sensor has been set.
693 if (!m_sensor) {
694 std::cerr << m_className << "::TransportPhoton:\n"
695 << " Sensor is not defined.\n";
696 m_ready = false;
697 return;
698 }
699
700 // Get the bounding box.
701 double xmin, ymin, zmin;
702 double xmax, ymax, zmax;
703 if (!m_sensor->GetArea(xmin, ymin, zmin, xmax, ymax, zmax)) {
704 std::cerr << m_className << "::TransportPhoton:\n"
705 << " Drift area is not set.\n";
706 m_ready = false;
707 return;
708 }
709 // Check if the bounding box has changed.
710 bool update = false;
711 const double lx = fabs(xmax - xmin);
712 const double ly = fabs(ymax - ymin);
713 const double lz = fabs(zmax - zmin);
714 if (fabs(lx - m_lX) > Small || fabs(ly - m_lY) > Small || fabs(lz - m_lZ) > Small) {
715 m_lX = lx;
716 m_lY = ly;
717 m_lZ = lz;
718 m_isChanged = true;
719 update = true;
720 m_hasActiveTrack = false;
721 }
722 // Update the center of the bounding box.
723 m_cX = 0.5 * (xmin + xmax);
724 m_cY = 0.5 * (ymin + ymax);
725 m_cZ = 0.5 * (zmin + zmax);
726
727 m_fieldMap.SetSensor(m_sensor);
728 m_fieldMap.SetCentre(m_cX, m_cY, m_cZ);
729
730 // Make sure the initial position is inside an ionisable medium.
731 Medium* medium = NULL;
732 if (!m_sensor->GetMedium(x0, y0, z0, medium)) {
733 std::cerr << m_className << "::TransportPhoton:\n"
734 << " No medium at initial position.\n";
735 return;
736 } else if (!medium->IsIonisable()) {
737 std::cerr << "TrackHeed:TransportPhoton:\n"
738 << " Medium at initial position is not ionisable.\n";
739 m_ready = false;
740 return;
741 }
742
743 // Check if the medium has changed since the last call.
744 if (medium->GetName() != m_mediumName ||
745 fabs(medium->GetMassDensity() - m_mediumDensity) > 1.e-9) {
746 m_isChanged = true;
747 update = true;
748 m_ready = false;
749 }
750
751 // If medium or bounding box have changed, update the "chamber".
752 if (update) {
753 if (!Setup(medium)) return;
754 m_ready = true;
755 m_mediumName = medium->GetName();
756 m_mediumDensity = medium->GetMassDensity();
757 }
758
759 // Delete the particle bank.
760 // Clusters from the current track will be lost.
761 m_hasActiveTrack = false;
762 ClearParticleBank();
763 m_deltaElectrons.clear();
764 m_conductionElectrons.clear();
765 m_conductionIons.clear();
766
767 // Check the direction vector.
768 double dx = dx0, dy = dy0, dz = dz0;
769 const double d = sqrt(dx * dx + dy * dy + dz * dz);
770 if (d <= 0.) {
771 // Null vector. Sample the direction isotropically.
772 RndmDirection(dx, dy, dz);
773 } else {
774 // Normalise the direction vector.
775 dx /= d;
776 dy /= d;
777 dz /= d;
778 }
779 Heed::vec velocity(dx, dy, dz);
780 velocity = velocity * Heed::CLHEP::c_light;
781
782 // Initial position (shift with respect to bounding box center and
783 // convert from cm to mm).
784 Heed::point p0((x0 - m_cX) * 10., (y0 - m_cY) * 10., (z0 - m_cZ) * 10.);
785
786 // Create and transport the photon.
787 Heed::HeedPhoton photon(m_chamber, p0, velocity, t0, 0, e0 * 1.e-6,
788 &m_fieldMap);
789 ClearParticleBank();
790 std::vector<Heed::gparticle*> secondaries;
791 photon.fly(secondaries);
792
793 while (!secondaries.empty()) {
794 std::vector<Heed::gparticle*> newSecondaries;
795 // Loop over the particle bank and look for daughter particles.
796 std::vector<Heed::gparticle*>::iterator it;
797 for (it = secondaries.begin(); it != secondaries.end(); ++it) {
798 // Check if it is a delta electron.
799 Heed::HeedDeltaElectron* delta = dynamic_cast<Heed::HeedDeltaElectron*>(*it);
800 if (delta) {
801 if (m_doDeltaTransport) {
802 // Transport the delta electron.
803 delta->fly(newSecondaries);
804 // Add the conduction electrons to the list.
805 m_conductionElectrons.insert(m_conductionElectrons.end(),
806 delta->conduction_electrons.begin(),
807 delta->conduction_electrons.end());
808 m_conductionIons.insert(m_conductionIons.end(),
809 delta->conduction_ions.begin(),
810 delta->conduction_ions.end());
811 } else {
812 // Add the delta electron to the list, for later use.
813 deltaElectron newDeltaElectron;
814 newDeltaElectron.x = delta->currpos.pt.v.x * 0.1 + m_cX;
815 newDeltaElectron.y = delta->currpos.pt.v.y * 0.1 + m_cY;
816 newDeltaElectron.z = delta->currpos.pt.v.z * 0.1 + m_cZ;
817 newDeltaElectron.t = delta->currpos.time;
818 newDeltaElectron.e = delta->curr_kin_energy * 1.e6;
819 newDeltaElectron.dx = delta->currpos.dir.x;
820 newDeltaElectron.dy = delta->currpos.dir.y;
821 newDeltaElectron.dz = delta->currpos.dir.z;
822 m_deltaElectrons.push_back(newDeltaElectron);
823 }
824 continue;
825 }
826 // Check if it is a fluorescence photon.
827 Heed::HeedPhoton* fluorescencePhoton = dynamic_cast<Heed::HeedPhoton*>(*it);
828 if (!fluorescencePhoton) {
829 std::cerr << m_className << "::TransportPhoton:\n"
830 << " Unknown secondary particle.\n";
831 ClearBank(secondaries);
832 ClearBank(newSecondaries);
833 return;
834 }
835 fluorescencePhoton->fly(newSecondaries);
836 }
837 secondaries.swap(newSecondaries);
838 ClearBank(newSecondaries);
839 }
840
841 // Get the total number of electrons produced in this step.
842 nel = m_doDeltaTransport ? m_conductionElectrons.size() :
843 m_deltaElectrons.size();
844 ni = m_conductionIons.size();
845}
846
847void TrackHeed::EnableElectricField() { m_fieldMap.UseEfield(true); }
848void TrackHeed::DisableElectricField() { m_fieldMap.UseEfield(false); }
849void TrackHeed::EnableMagneticField() { m_fieldMap.UseBfield(true); }
850void TrackHeed::DisableMagneticField() { m_fieldMap.UseBfield(false); }
851
852void TrackHeed::SetEnergyMesh(const double e0, const double e1,
853 const int nsteps) {
854
855 if (fabs(e1 - e0) < Small) {
856 std::cerr << m_className << "::SetEnergyMesh:\n";
857 std::cerr << " Invalid energy range:\n";
858 std::cerr << " " << e0 << " < E [eV] < " << e1 << "\n";
859 return;
860 }
861
862 if (nsteps <= 0) {
863 std::cerr << m_className << "::SetEnergyMesh:\n";
864 std::cerr << " Number of intervals must be > 0.\n";
865 return;
866 }
867
868 m_emin = std::min(e0, e1);
869 m_emax = std::max(e0, e1);
870 m_emin *= 1.e-6;
871 m_emax *= 1.e-6;
872 m_nEnergyIntervals = nsteps;
873}
874
875void TrackHeed::SetParticleUser(const double m, const double z) {
876
877 if (fabs(z) < Small) {
878 std::cerr << m_className << "::SetParticleUser:\n"
879 << " Particle cannot have zero charge.\n";
880 return;
881 }
882 if (m < Small) {
883 std::cerr << m_className << "::SetParticleUser:\n"
884 << " Particle mass must be greater than zero.\n";
885 }
886 m_q = z;
887 m_mass = m;
888 m_isElectron = false;
889 m_spin = 0;
890 m_particleName = "exotic";
891}
892
893bool TrackHeed::Setup(Medium* medium) {
894
895 // Make sure the path to the Heed database is known.
896 std::string databasePath;
897 char* dbPath = std::getenv("HEED_DATABASE");
898 if (dbPath == NULL) {
899 // Try GARFIELD_HOME.
900 dbPath = std::getenv("GARFIELD_HOME");
901 if (dbPath == NULL) {
902 std::cerr << m_className << "::Setup:\n Cannot retrieve database path "
903 << "(environment variables HEED_DATABASE and GARFIELD_HOME "
904 << "are not defined).\n Cannot proceed.\n";
905 return false;
906 }
907 databasePath = std::string(dbPath) + "/Heed/heed++/database";
908 } else {
909 databasePath = dbPath;
910 }
911 if (databasePath[databasePath.size() - 1] != '/') {
912 databasePath.append("/");
913 }
914
915 // Check once more that the medium exists.
916 if (!medium) {
917 std::cerr << m_className << "::Setup: Null pointer.\n";
918 return false;
919 }
920
921 // Setup the energy mesh.
922 if (m_energyMesh) {
923 delete m_energyMesh;
924 m_energyMesh = NULL;
925 }
926 m_energyMesh = new Heed::EnergyMesh(m_emin, m_emax, m_nEnergyIntervals);
927
928 if (medium->IsGas()) {
929 if (!SetupGas(medium)) return false;
930 } else {
931 if (!SetupMaterial(medium)) return false;
932 }
933
934 // Energy transfer cross-section
935 // Set a flag indicating whether the primary particle is an electron.
936 const int sel = m_isElectron ? 1 : 0;
937 const double gamma = GetGamma();
938
939 if (m_transferCs) {
940 delete m_transferCs;
941 m_transferCs = NULL;
942 }
943 m_transferCs =
944 new Heed::EnTransfCS(1.e-6 * m_mass, gamma - 1, sel, m_matter, long(m_q));
945
946 if (!SetupDelta(databasePath)) return false;
947
948 if (m_debug) {
949 const double nc = m_transferCs->quanC;
950 const double dedx = m_transferCs->meanC * 1.e3;
951 const double dedx1 = m_transferCs->meanC1 * 1.e3;
952 const double w = m_matter->W * 1.e6;
953 const double f = m_matter->F;
954 const double minI = m_matter->min_ioniz_pot * 1.e6;
955 std::cout << m_className << "::Setup:\n";
956 std::cout << " Cluster density: " << nc << " cm-1\n";
957 std::cout << " Stopping power (restricted): " << dedx << " keV/cm\n";
958 std::cout << " Stopping power (incl. tail): " << dedx1 << " keV/cm\n";
959 std::cout << " W value: " << w << " eV\n";
960 std::cout << " Fano factor: " << f << "\n";
961 std::cout << " Min. ionization potential: " << minI << " eV\n";
962 }
963
964 Heed::fixsyscoor primSys(Heed::point(0., 0., 0.),
965 Heed::basis("primary"), "primary");
966 if (m_chamber) {
967 delete m_chamber;
968 m_chamber = NULL;
969 }
970 m_chamber = new HeedChamber(primSys, m_lX, m_lY, m_lZ,
971 *m_transferCs, *m_deltaCs);
972 m_fieldMap.SetSensor(m_sensor);
973 return true;
974}
975
976bool TrackHeed::SetupGas(Medium* medium) {
977
978 // Get temperature and pressure.
979 double pressure = medium->GetPressure();
980 pressure = (pressure / AtmosphericPressure) * Heed::CLHEP::atmosphere;
981 double temperature = medium->GetTemperature();
982
983 const int nComponents = medium->GetNumberOfComponents();
984 if (nComponents < 1) {
985 std::cerr << m_className << "::SetupGas:\n";
986 std::cerr << " Gas " << medium->GetName() << " has zero constituents.\n";
987 return false;
988 }
989
990 if (m_molPacs) {
991 delete m_molPacs;
992 m_molPacs = NULL;
993 }
994 m_molPacs = new Heed::MolecPhotoAbsCS* [nComponents];
995 std::vector<std::string> notations;
996 std::vector<double> fractions;
997
998 for (int i = 0; i < nComponents; ++i) {
999 std::string gasname;
1000 double frac;
1001 medium->GetComponent(i, gasname, frac);
1002 // If necessary, change the Magboltz name to the Heed internal name.
1003 if (gasname == "He-3") gasname = "He";
1004 if (gasname == "CD4") gasname = "CH4";
1005 if (gasname == "iC4H10" || gasname == "nC4H10") gasname = "C4H10";
1006 if (gasname == "neoC5H12" || gasname == "nC5H12") gasname = "C5H12";
1007 if (gasname == "H2O") gasname = "Water";
1008 if (gasname == "D2") gasname = "H2";
1009 if (gasname == "cC3H6") gasname = "C3H6";
1010 // Find the corresponding photoabsorption cross-section.
1011 if (gasname == "CF4")
1012 m_molPacs[i] = &Heed::CF4_MPACS;
1013 else if (gasname == "Ar")
1014 m_molPacs[i] = &Heed::Ar_MPACS;
1015 else if (gasname == "He")
1016 m_molPacs[i] = &Heed::He_MPACS;
1017 else if (gasname == "Ne")
1018 m_molPacs[i] = &Heed::Ne_MPACS;
1019 else if (gasname == "Kr")
1020 m_molPacs[i] = &Heed::Kr_MPACS;
1021 else if (gasname == "Xe")
1022 m_molPacs[i] = &Heed::Xe_MPACS;
1023 else if (gasname == "CH4")
1024 m_molPacs[i] = &Heed::CH4_MPACS;
1025 else if (gasname == "C2H6")
1026 m_molPacs[i] = &Heed::C2H6_MPACS;
1027 else if (gasname == "C3H8")
1028 m_molPacs[i] = &Heed::C3H8_MPACS;
1029 else if (gasname == "C4H10")
1030 m_molPacs[i] = &Heed::C4H10_MPACS;
1031 else if (gasname == "CO2")
1032 m_molPacs[i] = &Heed::CO2_MPACS;
1033 else if (gasname == "C5H12")
1034 m_molPacs[i] = &Heed::C5H12_MPACS;
1035 else if (gasname == "Water")
1036 m_molPacs[i] = &Heed::H2O_MPACS;
1037 else if (gasname == "O2")
1038 m_molPacs[i] = &Heed::O2_MPACS;
1039 else if (gasname == "N2" || gasname == "N2 (Phelps)")
1040 m_molPacs[i] = &Heed::N2_MPACS;
1041 else if (gasname == "NO")
1042 m_molPacs[i] = &Heed::NO_MPACS;
1043 else if (gasname == "N2O")
1044 m_molPacs[i] = &Heed::N2O_MPACS;
1045 else if (gasname == "C2H4")
1046 m_molPacs[i] = &Heed::C2H4_MPACS;
1047 else if (gasname == "C2H2")
1048 m_molPacs[i] = &Heed::C2H2_MPACS;
1049 else if (gasname == "H2")
1050 m_molPacs[i] = &Heed::H2_MPACS;
1051 else if (gasname == "CO")
1052 m_molPacs[i] = &Heed::CO_MPACS;
1053 else if (gasname == "Methylal")
1054 m_molPacs[i] = &Heed::Methylal_MPACS;
1055 else if (gasname == "DME")
1056 m_molPacs[i] = &Heed::DME_MPACS;
1057 else if (gasname == "C2F6")
1058 m_molPacs[i] = &Heed::C2F6_MPACS;
1059 else if (gasname == "SF6")
1060 m_molPacs[i] = &Heed::SF6_MPACS;
1061 else if (gasname == "NH3")
1062 m_molPacs[i] = &Heed::NH3_MPACS;
1063 else if (gasname == "C3H6")
1064 m_molPacs[i] = &Heed::C3H6_MPACS;
1065 else if (gasname == "CH3OH")
1066 m_molPacs[i] = &Heed::CH3OH_MPACS;
1067 else if (gasname == "C2H5OH")
1068 m_molPacs[i] = &Heed::C2H5OH_MPACS;
1069 else if (gasname == "C3H7OH")
1070 m_molPacs[i] = &Heed::C3H7OH_MPACS;
1071 else if (gasname == "Cs")
1072 m_molPacs[i] = &Heed::Cs_MPACS;
1073 else if (gasname == "F2")
1074 m_molPacs[i] = &Heed::F2_MPACS;
1075 else if (gasname == "CS2")
1076 m_molPacs[i] = &Heed::CS2_MPACS;
1077 else if (gasname == "COS")
1078 m_molPacs[i] = &Heed::COS_MPACS;
1079 else if (gasname == "CD4")
1080 m_molPacs[i] = &Heed::CH4_MPACS;
1081 else if (gasname == "BF3")
1082 m_molPacs[i] = &Heed::BF3_MPACS;
1083 else if (gasname == "C2HF5")
1084 m_molPacs[i] = &Heed::C2HF5_MPACS;
1085 else if (gasname == "C2H2F4")
1086 m_molPacs[i] = &Heed::C2H2F4_MPACS;
1087 else if (gasname == "CHF3")
1088 m_molPacs[i] = &Heed::CHF3_MPACS;
1089 else if (gasname == "CF3Br")
1090 m_molPacs[i] = &Heed::CF3Br_MPACS;
1091 else if (gasname == "C3F8")
1092 m_molPacs[i] = &Heed::C3F8_MPACS;
1093 else if (gasname == "O3")
1094 m_molPacs[i] = &Heed::O3_MPACS;
1095 else if (gasname == "Hg")
1096 m_molPacs[i] = &Heed::Hg_MPACS;
1097 else if (gasname == "H2S")
1098 m_molPacs[i] = &Heed::H2S_MPACS;
1099 else if (gasname == "GeH4")
1100 m_molPacs[i] = &Heed::GeH4_MPACS;
1101 else if (gasname == "SiH4")
1102 m_molPacs[i] = &Heed::SiH4_MPACS;
1103 else {
1104 std::cerr << m_className << "::SetupGas:\n Photoabsorption "
1105 << "cross-section for " << gasname << " not available.\n";
1106 return false;
1107 }
1108 notations.push_back(gasname);
1109 fractions.push_back(frac);
1110 }
1111 if (m_usePacsOutput) {
1112 std::ofstream pacsfile;
1113 pacsfile.open("heed_pacs.txt", std::ios::out);
1114 const int nValues = m_energyMesh->get_q();
1115 if (nValues > 0) {
1116 for (int i = 0; i < nValues; ++i) {
1117 double e = m_energyMesh->get_e(i);
1118 pacsfile << 1.e6 * e << " ";
1119 for (int j = 0; j < nComponents; ++j) {
1120 pacsfile << m_molPacs[j]->get_ACS(e) << " "
1121 << m_molPacs[j]->get_ICS(e) << " ";
1122 }
1123 pacsfile << "\n";
1124 }
1125 }
1126 pacsfile.close();
1127 }
1128
1129 const std::string gasname = FindUnusedMaterialName(medium->GetName());
1130 if (m_gas) {
1131 delete m_gas;
1132 m_gas = NULL;
1133 }
1134
1135 m_gas = new Heed::GasDef(gasname, gasname, nComponents, notations, fractions,
1136 pressure, temperature, -1.);
1137
1138 const double w = std::max(medium->GetW() * 1.e-6, 0.);
1139 double f = medium->GetFanoFactor();
1140 if (f <= 0.) f = Heed::standard_factor_Fano;
1141
1142 if (m_matter) {
1143 delete m_matter;
1144 m_matter = NULL;
1145 }
1146 m_matter = new Heed::HeedMatterDef(m_energyMesh, m_gas, m_molPacs, w, f);
1147
1148 return true;
1149}
1150
1151bool TrackHeed::SetupMaterial(Medium* medium) {
1152
1153 // Get temperature and density.
1154 double temperature = medium->GetTemperature();
1155 const double density = medium->GetMassDensity() * Heed::CLHEP::gram /
1156 Heed::CLHEP::cm3;
1157
1158 const int nComponents = medium->GetNumberOfComponents();
1159 if (m_atPacs) {
1160 delete m_atPacs;
1161 m_atPacs = NULL;
1162 }
1163 m_atPacs = new Heed::AtomPhotoAbsCS* [nComponents];
1164
1165 std::vector<std::string> notations;
1166 std::vector<double> fractions;
1167 for (int i = 0; i < nComponents; ++i) {
1168 std::string materialName;
1169 double frac;
1170 medium->GetComponent(i, materialName, frac);
1171 if (materialName == "C")
1172 m_atPacs[i] = &Heed::Carbon_PACS;
1173 else if (materialName == "Si")
1174 m_atPacs[i] = &Heed::Silicon_crystal_PACS;
1175 // else if (materialName == "Si") m_atPacs[i] = &Heed::Silicon_G4_PACS;
1176 else if (materialName == "Ga")
1177 m_atPacs[i] = &Heed::Gallium_PACS;
1178 else if (materialName == "Ge")
1179 m_atPacs[i] = &Heed::Germanium_PACS;
1180 else if (materialName == "As")
1181 m_atPacs[i] = &Heed::Arsenic_PACS;
1182 else if (materialName == "Cd")
1183 m_atPacs[i] = &Heed::Cadmium_PACS;
1184 else if (materialName == "Te")
1185 m_atPacs[i] = &Heed::Tellurium_PACS;
1186 else {
1187 std::cerr << m_className << "::SetupMaterial:\n";
1188 std::cerr << " Photoabsorption cross-section data for " << materialName
1189 << " are not implemented.\n";
1190 return false;
1191 }
1192 notations.push_back(materialName);
1193 fractions.push_back(frac);
1194 }
1195 if (m_usePacsOutput) {
1196 std::ofstream pacsfile;
1197 pacsfile.open("heed_pacs.txt", std::ios::out);
1198 const int nValues = m_energyMesh->get_q();
1199 if (nValues > 0) {
1200 for (int i = 0; i < nValues; ++i) {
1201 double e = m_energyMesh->get_e(i);
1202 pacsfile << 1.e6 * e << " ";
1203 for (int j = 0; j < nComponents; ++j) {
1204 pacsfile << m_atPacs[j]->get_ACS(e) << " " << m_atPacs[j]->get_ICS(e)
1205 << " ";
1206 }
1207 pacsfile << "\n";
1208 }
1209 }
1210 pacsfile.close();
1211 }
1212 if (m_material) {
1213 delete m_material;
1214 m_material = NULL;
1215 }
1216 const std::string materialName = FindUnusedMaterialName(medium->GetName());
1217 m_material = new Heed::MatterDef(materialName, materialName, nComponents,
1218 notations, fractions, density, temperature);
1219
1220 double w = medium->GetW() * 1.e-6;
1221 if (w < 0.) w = 0.;
1222 double f = medium->GetFanoFactor();
1223 if (f <= 0.) f = Heed::standard_factor_Fano;
1224
1225 if (m_matter) {
1226 delete m_matter;
1227 m_matter = NULL;
1228 }
1229 m_matter = new Heed::HeedMatterDef(m_energyMesh, m_material, m_atPacs, w, f);
1230
1231 return true;
1232}
1233
1234bool TrackHeed::SetupDelta(const std::string& databasePath) {
1235
1236 // Load elastic scattering data.
1237 std::string filename = databasePath + "cbdel.dat";
1238 if (m_elScat) {
1239 delete m_elScat;
1240 m_elScat = NULL;
1241 }
1242 m_elScat = new Heed::ElElasticScat(filename);
1243
1244 filename = databasePath + "elastic_disp.dat";
1245 if (m_lowSigma) {
1246 delete m_lowSigma;
1247 m_lowSigma = NULL;
1248 }
1249 m_lowSigma = new Heed::ElElasticScatLowSigma(m_elScat, filename);
1250
1251 // Load data for calculation of ionization.
1252 // Get W value and Fano factor.
1253 const double w = m_matter->W * 1.e6;
1254 const double f = m_matter->F;
1255 filename = databasePath + "delta_path.dat";
1256 if (m_pairProd) {
1257 delete m_pairProd;
1258 m_pairProd = NULL;
1259 }
1260 m_pairProd = new Heed::PairProd(filename, w, f);
1261
1262 if (m_deltaCs) {
1263 delete m_deltaCs;
1264 m_deltaCs = NULL;
1265 }
1266 m_deltaCs = new Heed::HeedDeltaElectronCS(m_matter, m_elScat, m_lowSigma, m_pairProd);
1267 return true;
1268}
1269
1270double TrackHeed::GetW() const { return m_matter->W * 1.e6; }
1271double TrackHeed::GetFanoFactor() const { return m_matter->F; }
1272
1273std::string TrackHeed::FindUnusedMaterialName(const std::string& namein) {
1274
1275 std::string nameout = namein;
1276 unsigned int counter = 0;
1277 while (Heed::MatterDef::get_MatterDef(nameout)) {
1278 std::stringstream ss;
1279 ss << namein << "_" << counter;
1280 nameout = ss.str();
1281 ++counter;
1282 }
1283 return nameout;
1284
1285}
1286
1287void TrackHeed::ClearParticleBank() {
1288
1290 ClearBank(m_particleBank);
1291 m_bankIterator = m_particleBank.end();
1292}
1293
1294bool TrackHeed::IsInside(const double x, const double y, const double z) {
1295
1296 // Check if the point is inside the drift area.
1297 if (!m_sensor->IsInArea(x, y, z)) return false;
1298 // Check if the point is inside a medium.
1299 Medium* medium = NULL;
1300 if (!m_sensor->GetMedium(x, y, z, medium)) return false;
1301 // Make sure the medium has not changed.
1302 if (medium->GetName() != m_mediumName ||
1303 fabs(medium->GetMassDensity() - m_mediumDensity) > 1.e-9 ||
1304 !medium->IsIonisable()) {
1305 return false;
1306 }
1307 return true;
1308}
1309
1310}
Abstract base class for media.
Definition: Medium.hh:11
virtual double GetMassDensity() const
Definition: Medium.cc:137
const std::string & GetName() const
Definition: Medium.hh:22
virtual bool IsGas() const
Definition: Medium.hh:23
bool IsIonisable() const
Definition: Medium.hh:59
bool IsInArea(const double x, const double y, const double z)
Check if a point is inside the user area.
Definition: Sensor.cc:264
bool GetMedium(const double x, const double y, const double z, Medium *&medium)
Get the medium at (x, y, z).
Definition: Sensor.cc:150
bool GetArea(double &xmin, double &ymin, double &zmin, double &xmax, double &ymax, double &zmax)
Return the current user area.
Definition: Sensor.cc:237
void EnableMagneticField()
Definition: TrackHeed.cc:849
void SetEnergyMesh(const double e0, const double e1, const int nsteps)
Definition: TrackHeed.cc:852
virtual bool GetCluster(double &xcls, double &ycls, double &zcls, double &tcls, int &n, double &e, double &extra)
Definition: TrackHeed.cc:327
void EnableElectricField()
Definition: TrackHeed.cc:847
void DisableMagneticField()
Definition: TrackHeed.cc:850
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 &nel, int &ni)
Definition: TrackHeed.cc:676
double GetFanoFactor() const
Definition: TrackHeed.cc:1271
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 &nel, int &ni)
Definition: TrackHeed.cc:535
virtual double GetClusterDensity()
Definition: TrackHeed.cc:293
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:465
bool GetIon(const unsigned int i, double &x, double &y, double &z, double &t) const
Definition: TrackHeed.cc:510
virtual ~TrackHeed()
Destructor.
Definition: TrackHeed.cc:95
TrackHeed()
Constructor.
Definition: TrackHeed.cc:60
virtual bool NewTrack(const double x0, const double y0, const double z0, const double t0, const double dx0, const double dy0, const double dz0)
Definition: TrackHeed.cc:111
double GetW() const
Definition: TrackHeed.cc:1270
void SetParticleUser(const double m, const double z)
Definition: TrackHeed.cc:875
void DisableElectricField()
Definition: TrackHeed.cc:848
virtual double GetStoppingPower()
Get the stopping power (mean energy loss [eV] per cm).
Definition: TrackHeed.cc:310
Sensor * m_sensor
Definition: Track.hh:90
bool m_debug
Definition: Track.hh:97
double GetBeta() const
Definition: Track.hh:40
double GetGamma() const
Definition: Track.hh:41
bool m_isElectron
Definition: Track.hh:87
bool m_isChanged
Definition: Track.hh:92
double m_q
Definition: Track.hh:82
std::string m_particleName
Definition: Track.hh:88
void PlotCluster(const double x0, const double y0, const double z0)
Definition: Track.cc:221
std::string m_className
Definition: Track.hh:80
bool m_usePlotting
Definition: Track.hh:94
double m_mass
Definition: Track.hh:84
void PlotNewTrack(const double x0, const double y0, const double z0)
Definition: Track.cc:214
Atomic photoabsorption cross-section abstract base class.
Definition: PhotoAbsCS.h:285
virtual double get_ACS(double energy) const =0
virtual double get_ICS(double energy) const =0
double quanC
Integrated (ionization) cross-section.
Definition: EnTransfCS.h:70
long get_q() const
Return number of bins.
Definition: EnergyMesh.h:45
double get_e(long n) const
Return left side of a given bin.
Definition: EnergyMesh.h:51
std::vector< HeedCondElectron > conduction_electrons
std::vector< HeedCondElectron > conduction_ions
void SetSensor(Garfield::Sensor *sensor)
Definition: HeedFieldMap.h:19
void SetCentre(const double x, const double y, const double z)
Definition: HeedFieldMap.h:20
void UseEfield(const bool flag)
Definition: HeedFieldMap.h:25
void UseBfield(const bool flag)
Definition: HeedFieldMap.h:26
double W
Mean work per pair production, MeV.
Definition: HeedMatterDef.h:38
double F
Fano factor.
Definition: HeedMatterDef.h:39
double energy
Photon energy [MeV].
Definition: HeedPhoton.h:37
static MatterDef * get_MatterDef(const std::string &fnotation)
Definition: MatterDef.cpp:135
virtual double get_ACS(double energy) const
Photo-absorption cross-section [Mbarn] at a given energy [MeV].
virtual double get_ICS(double energy) const
Photo-ionization cross-section [Mbarn] at a given energy [MeV].
Basis.
Definition: vec.h:319
stvpoint currpos
Definition: gparticle.h:177
virtual void fly(std::vector< gparticle * > &secondaries)
Transport the particle.
Definition: gparticle.h:225
double curr_kin_energy
Definition: mparticle.h:32
void set_mass(const double m)
void set_charge(const double z)
Point.
Definition: vec.h:374
vec v
Definition: vec.h:376
vfloat time
Definition: gparticle.h:47
vec dir
Unit vector, in the first system in the tree.
Definition: gparticle.h:25
point pt
Coordinates in the first system in the tree.
Definition: gparticle.h:23
Definition: vec.h:186
vfloat x
Definition: vec.h:203
vfloat z
Definition: vec.h:203
vfloat y
Definition: vec.h:203
void RndmDirection(double &dx, double &dy, double &dz, const double length=1.)
Draw a random (isotropic) direction vector.
Definition: Random.hh:106
Definition: BGMesh.cpp:5
MolecPhotoAbsCS Cs_MPACS(Caesium_PACS, 1)
particle_def pi_minus_meson_def("pi_minus_meson", "pi-", 139.56755 *MeV/c_squared, -eplus, 0, 0, 0.0, spin_def(1.0, -1.0))
Definition: particle_def.h:125
particle_def pi_plus_meson_def("pi_plus_meson", "pi+", 139.56755 *MeV/c_squared, eplus, 0, 0, 0.0, spin_def(1.0, 1.0))
Definition: particle_def.h:123
ExAtomPhotoAbsCS Tellurium_PACS(49, shelllist_dir_name+"shelllist.dat", pacs_table_dir_name+"Te.dat")
Definition: PhotoAbsCSLib.h:58
MolecPhotoAbsCS CF3Br_MPACS(Carbon_for_CF4_PACS, 1, Fluorine_PACS, 3, Bromine_PACS, 1)
MolecPhotoAbsCS N2_MPACS(Nitrogen_PACS, 2, 34.8e-6)
Definition: PhotoAbsCSLib.h:66
ExAtomPhotoAbsCS Silicon_crystal_PACS(14, shelllist_dir_name+"shelllist_solid.dat", pacs_table_dir_name+"Si.dat", "Si_crystal")
Definition: PhotoAbsCSLib.h:45
particle_def muon_minus_def("muon_minus", "mu-", 105.658367 *MeV/c_squared, electron_charge, 1, 0, 0.5, spin_def(0.0, 0.0))
Definition: particle_def.h:112
MolecPhotoAbsCS Ne_MPACS(Neon_PACS, 1, 35.4e-6)
Definition: PhotoAbsCSLib.h:68
MolecPhotoAbsCS C5H12_MPACS(Carbon_for_C4H10_PACS, 5, Hydrogen_for_H2_PACS, 12, 23.2e-6)
Definition: PhotoAbsCSLib.h:96
MolecPhotoAbsCS CO_MPACS(Carbon_for_CO2_PACS, 1, Oxygen_PACS, 1)
Definition: PhotoAbsCSLib.h:99
long last_particle_number
Definition: HeedParticle.h:9
particle_def alpha_particle_def("alpha_particle", "alpha", 3727.417 *MeV/c_squared, 2 *eplus, 0, 4, 0.0, spin_def(0.0, 0.0))
Definition: particle_def.h:131
particle_def anti_proton_def("", "p-", proton_def)
Definition: particle_def.h:115
MolecPhotoAbsCS Hg_MPACS(Mercury_PACS, 1)
MolecPhotoAbsCS SiH4_MPACS(Silicon_PACS, 1, Hydrogen_for_H2_PACS, 4)
MolecPhotoAbsCS CS2_MPACS(Carbon_for_CO2_PACS, 1, Sulfur_PACS, 2)
MolecPhotoAbsCS COS_MPACS(Carbon_for_CO2_PACS, 1, Oxygen_PACS, 1, Sulfur_PACS, 1)
particle_def deuteron_def("deuteron", "dtr", 1875.613 *MeV/c_squared, eplus, 0, 2, 0.0, spin_def(0.0, 0.0))
Definition: particle_def.h:130
MolecPhotoAbsCS DME_MPACS(Carbon_for_Methylal_PACS, 2, Hydrogen_for_H2_PACS, 6, Oxygen_PACS, 1)
MolecPhotoAbsCS Xe_MPACS(Xenon_PACS, 1, 22.1e-6)
Definition: PhotoAbsCSLib.h:71
particle_def proton_def("proton", "p+", proton_mass_c2/c_squared, eplus, 0, 1, 0.5, spin_def(0.5, 0.5))
Definition: particle_def.h:114
MolecPhotoAbsCS H2S_MPACS(Hydrogen_for_H2_PACS, 2, Sulfur_PACS, 1)
ExAtomPhotoAbsCS Cadmium_PACS(48, shelllist_dir_name+"shelllist.dat", pacs_table_dir_name+"Cd.dat")
Definition: PhotoAbsCSLib.h:57
MolecPhotoAbsCS F2_MPACS(Fluorine_PACS, 2)
MolecPhotoAbsCS C3H8_MPACS(Carbon_for_CH4_PACS, 3, Hydrogen_for_H2_PACS, 8, 24.0e-6)
Definition: PhotoAbsCSLib.h:86
ExAtomPhotoAbsCS Arsenic_PACS(33, shelllist_dir_name+"shelllist.dat", pacs_table_dir_name+"As.dat")
Definition: PhotoAbsCSLib.h:54
particle_def K_minus_meson_def("K_minus_meson_def", "K-", K_plus_meson_def)
Definition: particle_def.h:128
const double standard_factor_Fano
Definition: PhotoAbsCS.h:574
MolecPhotoAbsCS C2H5OH_MPACS(Carbon_for_C2H6_PACS, 2, Hydrogen_for_H2_PACS, 6, Oxygen_PACS, 1, 24.8e-6)
MolecPhotoAbsCS O2_MPACS(Oxygen_PACS, 2, 30.8e-6)
Definition: PhotoAbsCSLib.h:67
MolecPhotoAbsCS C2H2_MPACS(Carbon_for_CH4_PACS, 2, Hydrogen_for_H2_PACS, 2, 25.8e-6)
Definition: PhotoAbsCSLib.h:83
MolecPhotoAbsCS C2H4_MPACS(Carbon_for_C2H4_PACS, 2, Hydrogen_for_H2_PACS, 4, 25.8e-6)
Definition: PhotoAbsCSLib.h:84
particle_def K_plus_meson_def("K_plus_meson_def", "K+", 493.677 *MeV/c_squared, 1, 0, 0, 0.0, spin_def(0.5, -0.5))
Definition: particle_def.h:127
MolecPhotoAbsCS Ar_MPACS(Argon_PACS, 1, 26.4e-6)
Definition: PhotoAbsCSLib.h:69
MolecPhotoAbsCS CHF3_MPACS(Carbon_for_CF4_PACS, 1, Hydrogen_for_H2_PACS, 1, Fluorine_PACS, 3)
MolecPhotoAbsCS NH3_MPACS(Nitrogen_PACS, 1, Hydrogen_for_NH4_PACS, 3, 26.6e-6)
Definition: PhotoAbsCSLib.h:72
MolecPhotoAbsCS CH3OH_MPACS(Carbon_for_C2H6_PACS, 1, Hydrogen_for_H2_PACS, 4, Oxygen_PACS, 1, 24.7e-6)
MolecPhotoAbsCS C2HF5_MPACS(Carbon_for_C2H6_PACS, 2, Hydrogen_for_H2_PACS, 1, Fluorine_PACS, 5)
MolecPhotoAbsCS C4H10_MPACS(Carbon_for_C4H10_PACS, 4, Hydrogen_for_H2_PACS, 10, 23.4e-6)
Definition: PhotoAbsCSLib.h:87
MolecPhotoAbsCS CH4_MPACS(Carbon_for_CH4_PACS, 1, Hydrogen_for_CH4_PACS, 4, 27.3e-6)
Definition: PhotoAbsCSLib.h:75
MolecPhotoAbsCS SF6_MPACS(Sulfur_PACS, 1, Fluorine_PACS, 6)
Definition: PhotoAbsCSLib.h:81
particle_def user_particle_def("user_particle", "X", 139.56755 *MeV/c_squared, eplus, 0, 0, 0.0, spin_def(0.0, 0.0))
Definition: particle_def.h:134
MolecPhotoAbsCS C2H2F4_MPACS(Carbon_for_C2H6_PACS, 2, Fluorine_PACS, 4, Hydrogen_for_H2_PACS, 2)
MolecPhotoAbsCS BF3_MPACS(Boron_PACS, 1, Fluorine_PACS, 3)
ExAtomPhotoAbsCS Germanium_PACS(32, shelllist_dir_name+"shelllist.dat", pacs_table_dir_name+"Ge.dat")
Definition: PhotoAbsCSLib.h:52
MolecPhotoAbsCS Kr_MPACS(Krypton_PACS, 1, 24.4e-6)
Definition: PhotoAbsCSLib.h:70
DoubleAc fabs(const DoubleAc &f)
Definition: DoubleAc.h:615
MolecPhotoAbsCS C2F6_MPACS(Carbon_for_C2H6_PACS, 2, Fluorine_PACS, 6)
MolecPhotoAbsCS C3H7OH_MPACS(Carbon_for_C2H6_PACS, 3, Hydrogen_for_H2_PACS, 8, Oxygen_PACS, 1)
MolecPhotoAbsCS CO2_MPACS(Carbon_for_CO2_PACS, 1, Oxygen_for_CO2_PACS, 2, 33.0e-6)
Definition: PhotoAbsCSLib.h:74
MolecPhotoAbsCS C2H6_MPACS(Carbon_for_C2H6_PACS, 2, Hydrogen_for_H2_PACS, 6, 25.0e-6)
Definition: PhotoAbsCSLib.h:85
MolecPhotoAbsCS He_MPACS(Helium_PACS, 1, 41.3e-6)
Definition: PhotoAbsCSLib.h:65
particle_def muon_plus_def("muon_plus", "mu+", muon_minus_def)
Definition: particle_def.h:113
particle_def positron_def("positron", "e+", electron_def)
Definition: particle_def.h:111
particle_def electron_def("electron", "e-", electron_mass_c2/c_squared, electron_charge, 1, 0, 0.5, spin_def(0.0, 0.0))
Definition: particle_def.h:110
MolecPhotoAbsCS H2_MPACS(Hydrogen_for_H2_PACS, 2)
Definition: PhotoAbsCSLib.h:64
ExAtomPhotoAbsCS Gallium_PACS(31, shelllist_dir_name+"shelllist.dat", pacs_table_dir_name+"Ga.dat")
Definition: PhotoAbsCSLib.h:51
ExAtomPhotoAbsCS Carbon_PACS(6, shelllist_dir_name+"shelllist.dat", pacs_table_dir_name+"C.dat")
Definition: PhotoAbsCSLib.h:28
MolecPhotoAbsCS NO_MPACS(Nitrogen_PACS, 1, Oxygen_PACS, 1)
Definition: PhotoAbsCSLib.h:98
MolecPhotoAbsCS O3_MPACS(Oxygen_PACS, 3)
MolecPhotoAbsCS GeH4_MPACS(Germanium_PACS, 1, Hydrogen_for_H2_PACS, 4)
MolecPhotoAbsCS C3F8_MPACS(Carbon_for_CF4_PACS, 3, Fluorine_PACS, 8)
MolecPhotoAbsCS Methylal_MPACS(Oxygen_PACS, 2, Carbon_for_Methylal_PACS, 3, Hydrogen_for_H2_PACS, 8, 10.0e-6 *23.4/10.55)
Definition: PhotoAbsCSLib.h:92
MolecPhotoAbsCS H2O_MPACS(Hydrogen_for_H2_PACS, 2, Oxygen_PACS, 1, 29.6e-6)
Definition: PhotoAbsCSLib.h:97
MolecPhotoAbsCS CF4_MPACS(Carbon_for_CF4_PACS, 1, Fluorine_PACS, 4)
Definition: PhotoAbsCSLib.h:76
MolecPhotoAbsCS N2O_MPACS(Nitrogen_PACS, 2, Oxygen_PACS, 1, 34.8e-6)
Definition: PhotoAbsCSLib.h:73
MolecPhotoAbsCS C3H6_MPACS(Carbon_for_C2H6_PACS, 3, Hydrogen_for_H2_PACS, 6)
trajestep_limit gtrajlim
Definition: TrackHeed.cc:50