30void ClearBank(std::vector<Heed::gparticle*>& bank) {
31 for (
auto particle : bank)
32 if (particle)
delete particle;
36Heed::vec NormaliseDirection(
const double dx0,
const double dy0,
38 double dx = dx0, dy = dy0, dz = dz0;
39 const double d =
sqrt(dx * dx + dy * dy + dz * dz);
40 if (d < Garfield::Small) {
45 const double scale = 1. / d;
54 const double w = 0.) {
59 const std::string& atom2,
const int n2,
60 const double w = 0.) {
66 const std::string& atom2,
const int n2,
67 const std::string& atom3,
const int n3,
68 const double w = 0.) {
88 const double t0,
const double dx0,
const double dy0,
90 m_hasActiveTrack =
false;
94 std::cerr <<
m_className <<
"::NewTrack: Sensor is not defined.\n";
99 if (!UpdateBoundingBox(update))
return false;
105 <<
" No ionisable medium at initial position.\n";
110 if (medium->
GetName() != m_mediumName ||
120 m_mediumName = medium->
GetName();
129 Heed::vec velocity = NormaliseDirection(dx0, dy0, dz0);
130 velocity = velocity * Heed::CLHEP::c_light *
GetBeta();
133 std::cout <<
m_className <<
"::NewTrack:\n Track starts at (" << x0
134 <<
", " << y0 <<
", " << z0 <<
") at time " << t0 <<
"\n";
139 Heed::point p0((x0 - m_cX) * 10., (y0 - m_cY) * 10., (z0 - m_cZ) * 10.);
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();
183 m_fieldMap.get(), m_coulombScattering);
184 if (m_useBfieldAuto) {
185 m_fieldMap->UseBfield(
m_sensor->HasMagneticField());
189 m_radStraight * Heed::CLHEP::cm,
190 m_stepAngleStraight * Heed::CLHEP::rad,
191 m_stepAngleCurved * Heed::CLHEP::rad);
193 std::vector<Heed::gparticle*> particleBank;
195 particle.
fly(particleBank,
true);
197 particle.
fly(particleBank);
201 std::sort(particleBank.begin(), particleBank.end(),
203 return p1->time() < p2->time(); });
205 for (
auto gp : particleBank) {
208 if (!virtualPhoton) {
210 <<
" Particle is not a virtual photon. Program bug!\n";
214 if (!AddCluster(virtualPhoton, m_clusters))
break;
216 ClearBank(particleBank);
218 m_cluster = m_clusters.size() + 2;
220 m_hasActiveTrack =
true;
225 for (
const auto& cluster : m_clusters) {
235 std::cerr <<
m_className <<
"::GetClusterDensity:\n"
236 <<
" Ionisation cross-section is not available.\n";
239 return m_transferCs->quanC;
245 std::cerr <<
m_className <<
"::GetStoppingPower:\n"
246 <<
" Ionisation cross-section is not available.\n";
249 return m_transferCs->meanC1 * 1.e6;
253 std::vector<Cluster>& clusters) {
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();
263 if (!IsInside(xc, yc, zc))
return false;
271 cluster.energy = virtualPhoton->
m_energy * 1.e6;
279 cluster.ions.push_back(std::move(ion));
282 std::vector<Heed::gparticle*> secondaries;
283 virtualPhoton->
fly(secondaries);
284 while (!secondaries.empty()) {
285 std::vector<Heed::gparticle*> newSecondaries;
287 for (
auto secondary : secondaries) {
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) {
298 delta->fly(newSecondaries);
300 AddElectrons(delta->conduction_electrons, cluster.electrons);
301 AddElectrons(delta->conduction_ions, cluster.ions);
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));
318 auto photon =
dynamic_cast<Heed::HeedPhoton*
>(secondary);
321 <<
" Particle is neither an electron nor a photon.\n";
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;
330 if (m_doPhotonReabsorption) {
331 photon->fly(newSecondaries);
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));
345 for (
auto secondary : secondaries)
346 if (secondary)
delete secondary;
348 secondaries.swap(newSecondaries);
350 clusters.push_back(std::move(cluster));
354void TrackHeed::AddElectrons(
355 const std::vector<Heed::HeedCondElectron>& conductionElectrons,
356 std::vector<Electron>& electrons) {
358 for (
const auto& conductionElectron : conductionElectrons) {
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));
370 double& ec,
double& extra) {
372 return GetCluster(xc, yc, zc, tc, ne, ni, np, ec, extra);
376 double& tc,
int& ne,
int& ni,
377 double& ec,
double& extra) {
379 return GetCluster(xc, yc, zc, tc, ne, ni, np, ec, extra);
383 double& tc,
int& ne,
int& ni,
int& np,
384 double& ec,
double& extra) {
386 xc = yc = zc = tc = ec = extra = 0.;
389 if (m_clusters.empty())
return false;
391 if (m_cluster < m_clusters.size()) {
393 }
else if (m_cluster > m_clusters.size()) {
396 if (m_cluster >= m_clusters.size())
return false;
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;
411 double& z,
double& t,
double& e,
double& dx,
412 double& dy,
double& dz) {
414 if (m_clusters.empty() || m_cluster >= m_clusters.size())
return false;
416 if (i >= m_clusters[m_cluster].electrons.size()) {
417 std::cerr <<
m_className <<
"::GetElectron: Index out of range.\n";
420 const auto& electron = m_clusters[m_cluster].electrons[i];
434 if (m_clusters.empty() || m_cluster >= m_clusters.size())
return false;
436 if (i >= m_clusters[m_cluster].ions.size()) {
437 std::cerr <<
m_className <<
"::GetIon: Index out of range.\n";
440 const auto& ion = m_clusters[m_cluster].ions[i];
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;
453 if (i >= m_clusters[m_cluster].photons.size()) {
454 std::cerr <<
m_className <<
"::GetPhoton: Index out of range.\n";
457 const auto& photon = m_clusters[m_cluster].photons[i];
470 const double z0,
const double t0,
471 const double e0,
const double dx0,
472 const double dy0,
const double dz0,
479 const double z0,
const double t0,
480 const double e0,
const double dx0,
481 const double dy0,
const double dz0,
485 if (!m_doDeltaTransport) {
486 std::cerr <<
m_className <<
"::TransportDeltaElectron:\n"
487 <<
" Delta electron transport has been switched off.\n";
493 std::cerr <<
m_className <<
"::TransportDeltaElectron:\n"
494 <<
" Sensor is not defined.\n";
499 if (!UpdateBoundingBox(update))
return;
504 std::cerr <<
m_className <<
"::TransportDeltaElectron:\n"
505 <<
" No ionisable medium at initial position.\n";
510 if (medium->
GetName() != m_mediumName ||
514 m_hasActiveTrack =
false;
520 m_mediumName = medium->
GetName();
528 Heed::point p0((x0 - m_cX) * 10., (y0 - m_cY) * 10., (z0 - m_cZ) * 10.);
539 cluster.
electrons.push_back(std::move(electron));
540 m_clusters.push_back(std::move(cluster));
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;
550 Heed::vec velocity = NormaliseDirection(dx0, dy0, dz0) * speed;
553 std::vector<Heed::gparticle*> secondaries;
556 delta.
fly(secondaries);
557 ClearBank(secondaries);
562 ni = cluster.
ions.size();
563 m_clusters.push_back(std::move(cluster));
567 const double z0,
const double t0,
568 const double e0,
const double dx0,
569 const double dy0,
const double dz0,
int& ne) {
571 TransportPhoton(x0, y0, z0, t0, e0, dx0, dy0, dz0, ne, ni, np);
575 const double z0,
const double t0,
576 const double e0,
const double dx0,
577 const double dy0,
const double dz0,
int& ne,
580 TransportPhoton(x0, y0, z0, t0, e0, dx0, dy0, dz0, ne, ni, np);
584 const double z0,
const double t0,
585 const double e0,
const double dx0,
586 const double dy0,
const double dz0,
int& ne,
592 <<
" Photon energy must be positive.\n";
598 std::cerr <<
m_className <<
"::TransportPhoton: Sensor is not defined.\n";
603 if (!UpdateBoundingBox(update))
return;
609 <<
" No ionisable medium at initial position.\n";
614 if (medium->
GetName() != m_mediumName ||
623 m_mediumName = medium->
GetName();
628 m_hasActiveTrack =
false;
634 Heed::vec velocity = NormaliseDirection(dx0, dy0, dz0);
635 velocity = velocity * Heed::CLHEP::c_light;
639 Heed::point p0((x0 - m_cX) * 10., (y0 - m_cY) * 10., (z0 - m_cZ) * 10.);
644 std::vector<Heed::gparticle*> secondaries;
645 photon.
fly(secondaries);
646 if (secondaries.empty()) {
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;
656 cluster.
photons.push_back(std::move(unabsorbedPhoton));
659 while (!secondaries.empty()) {
660 std::vector<Heed::gparticle*> newSecondaries;
662 for (
auto gp : secondaries) {
666 if (m_doDeltaTransport) {
668 delta->fly(newSecondaries);
670 AddElectrons(delta->conduction_electrons, cluster.
electrons);
671 AddElectrons(delta->conduction_ions, cluster.
ions);
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));
689 if (!fluorescencePhoton) {
691 <<
" Unknown secondary particle.\n";
692 ClearBank(secondaries);
693 ClearBank(newSecondaries);
696 if (m_doPhotonReabsorption) {
697 fluorescencePhoton->fly(newSecondaries);
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));
711 secondaries.swap(newSecondaries);
712 ClearBank(newSecondaries);
714 ClearBank(secondaries);
716 ni = cluster.
ions.size();
718 m_clusters.push_back(std::move(cluster));
725 m_fieldMap->UseBfield(
true);
726 m_useBfieldAuto =
false;
730 m_fieldMap->UseBfield(
false);
731 m_useBfieldAuto =
false;
736 if (fabs(e1 - e0) < Small) {
738 <<
" Invalid energy range:\n"
739 <<
" " << e0 <<
" < E [eV] < " << e1 <<
"\n";
745 <<
" Number of intervals must be > 0.\n";
749 m_emin = 1.e-6 * std::min(e0, e1);
750 m_emax = 1.e-6 * std::max(e0, e1);
751 m_nEnergyIntervals = nsteps;
755 if (fabs(z) < Small) {
757 <<
" Particle cannot have zero charge.\n";
762 <<
" Particle mass must be greater than zero.\n";
773 std::string databasePath;
774 char* dbPath = std::getenv(
"HEED_DATABASE");
776 databasePath = dbPath;
779 dbPath = std::getenv(
"GARFIELD_INSTALL");
781 databasePath = std::string(dbPath) +
"/share/Heed/database";
784 dbPath = std::getenv(
"GARFIELD_HOME");
786 databasePath = std::string(dbPath) +
"/Heed/heed++/database";
791 if (databasePath.empty()) {
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";
799 if (databasePath.back() !=
'/') databasePath.append(
"/");
803 <<
" Database path: " << databasePath <<
"\n";
808 std::cerr <<
m_className <<
"::Initialise: Null pointer.\n";
813 m_energyMesh.reset(
new Heed::EnergyMesh(m_emin, m_emax, m_nEnergyIntervals));
815 if (medium->
IsGas()) {
816 if (!SetupGas(medium))
return false;
818 if (!SetupMaterial(medium))
return false;
826 if (!m_transferCs->m_ok) {
828 <<
" Problems occured when calculating the differential"
829 <<
" cross-section table.\n"
830 <<
" Results will be inaccurate.\n";
832 if (!SetupDelta(databasePath))
return false;
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;
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";
852 m_chamber.reset(
new HeedChamber(primSys, m_lX, m_lY, m_lZ,
853 *m_transferCs.get(), *m_deltaCs.get()));
858bool TrackHeed::SetupGas(
Medium* medium) {
861 pressure = (pressure / AtmosphericPressure) * Heed::CLHEP::atmosphere;
865 if (nComponents < 1) {
867 <<
" Gas " << medium->
GetName() <<
" has zero constituents.\n";
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) {
878 if (gasname ==
"paraH2" || gasname ==
"orthoD2" ||
881 }
else if (gasname ==
"He-3") {
883 }
else if (gasname ==
"CD4") {
885 }
else if (gasname ==
"iC4H10" || gasname ==
"nC4H10") {
887 }
else if (gasname ==
"neoC5H12" || gasname ==
"nC5H12") {
889 }
else if (gasname ==
"cC3H6") {
891 }
else if (gasname ==
"nC3H7OH") {
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") {
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,
954 }
else if (gasname ==
"C2H5OH") {
955 mpacs.emplace_back(makeMPACS(
"C for C2H6", 2,
"H for H2", 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));
991 <<
" Photoabsorption cross-section for "
992 << gasname <<
" is not implemented.\n";
995 notations.push_back(gasname);
996 fractions.push_back(frac);
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();
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) <<
" ";
1015 const std::string gasname = medium->
GetName();
1016 m_gas.reset(
new Heed::GasDef(gasname, gasname, nComponents, notations,
1017 fractions, pressure, temperature, -1.));
1019 const double w = std::max(medium->
GetW() * 1.e-6, 0.);
1024 new Heed::HeedMatterDef(m_energyMesh.get(), m_gas.get(), mpacs, w, f));
1028bool TrackHeed::SetupMaterial(
Medium* medium) {
1030 double temperature = medium->GetTemperature();
1031 const double density =
1032 medium->GetMassDensity() * Heed::CLHEP::gram / Heed::CLHEP::cm3;
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;
1041 medium->GetComponent(i, materialName, frac);
1042 if (materialName ==
"C") {
1043 if (medium->GetName() ==
"Diamond") {
1048 }
else if (materialName ==
"Si") {
1051 }
else if (materialName ==
"Ga") {
1053 }
else if (materialName ==
"Ge") {
1055 }
else if (materialName ==
"As") {
1057 }
else if (materialName ==
"Cd") {
1059 }
else if (materialName ==
"Te") {
1063 <<
" Photoabsorption cross-section for " << materialName
1064 <<
" is not implemented.\n";
1067 notations.push_back(materialName);
1068 fractions.push_back(frac);
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();
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)
1087 const std::string materialName = medium->GetName();
1088 m_material.reset(
new Heed::MatterDef(materialName, materialName, nComponents,
1089 notations, fractions, density,
1092 double w = medium->GetW() * 1.e-6;
1094 double f = medium->GetFanoFactor();
1097 m_matter.reset(
new Heed::HeedMatterDef(m_energyMesh.get(), m_material.get(),
1103bool TrackHeed::SetupDelta(
const std::string& databasePath) {
1105 std::string filename = databasePath +
"cbdel.dat";
1106 m_elScat.reset(
new Heed::ElElasticScat(filename));
1108 filename = databasePath +
"elastic_disp.dat";
1109 m_lowSigma.reset(
new Heed::ElElasticScatLowSigma(m_elScat.get(), filename));
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));
1118 m_deltaCs.reset(
new Heed::HeedDeltaElectronCS(
1119 m_matter.get(), m_elScat.get(), m_lowSigma.get(), m_pairProd.get()));
1128 if (!m_matter)
return 0.;
1130 const double e = 1.e-6 * en;
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;
1141bool TrackHeed::IsInside(
const double x,
const double y,
const double z) {
1146 if (!medium)
return false;
1148 if (medium->
GetName() != m_mediumName ||
1156bool TrackHeed::UpdateBoundingBox(
bool& update) {
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";
1165 const double lx =
fabs(xmax - xmin);
1166 const double ly =
fabs(ymax - ymin);
1167 const double lz =
fabs(zmax - zmin);
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";
1175 if (
fabs(lx - m_lX) > Small ||
fabs(ly - m_lY) > Small ||
1176 fabs(lz - m_lZ) > Small) {
1182 m_hasActiveTrack =
false;
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);
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";
1197 m_fieldMap->SetCentre(m_cX, m_cY, m_cZ);
Abstract base class for media.
virtual void GetComponent(const unsigned int i, std::string &label, double &f)
Get the name and fraction of a given component.
double GetW() const
Get the W value.
virtual double GetMassDensity() const
Get the mass density [g/cm3].
double GetPressure() const
unsigned int GetNumberOfComponents() const
Get number of components of the medium.
const std::string & GetName() const
Get the medium name/identifier.
virtual bool IsGas() const
Is this medium a gas?
bool IsIonisable() const
Is charge deposition by charged particles/photon enabled in this medium?
double GetTemperature() const
Get the temperature [K].
double GetFanoFactor() const
Get the Fano factor.
Medium * GetMedium(const double x, const double y, const double z)
Get the medium at (x, y, z).
bool IsInArea(const double x, const double y, const double z)
Check if a point is inside the user area.
void EnableMagneticField()
Take the magnetic field into account in the stepping algorithm.
void SetEnergyMesh(const double e0, const double e1, const int nsteps)
void EnableElectricField()
Take the electric field into account in the stepping algorithm.
void DisableMagneticField()
Do not take the magnetic field into account in the stepping algorithm.
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)
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
bool NewTrack(const double x0, const double y0, const double z0, const double t0, const double dx0, const double dy0, const double dz0) override
bool Initialise(Medium *medium, const bool verbose=false)
Compute the differential cross-section for a given medium.
bool GetElectron(const unsigned int i, double &x, double &y, double &z, double &t, double &e, double &dx, double &dy, double &dz)
bool GetIon(const unsigned int i, double &x, double &y, double &z, double &t) const
virtual ~TrackHeed()
Destructor.
TrackHeed()
Default constructor.
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)
double GetW() const
Return the W value of the medium (of the last simulated track).
void SetParticleUser(const double m, const double z)
bool GetCluster(double &xc, double &yc, double &zc, double &tc, int &nc, double &ec, double &extra) override
double GetClusterDensity() override
void DisableElectricField()
Do not take the electric field into account in the stepping algorithm.
double GetStoppingPower() override
Get the stopping power (mean energy loss [eV] per cm).
double GetBeta() const
Return the speed ( ) of the projectile.
double GetGamma() const
Return the Lorentz factor of the projectile.
std::string m_particleName
void PlotCluster(const double x0, const double y0, const double z0)
Track()=delete
Default constructor.
void PlotNewTrack(const double x0, const double y0, const double z0)
std::vector< HeedCondElectron > conduction_electrons
std::vector< HeedCondElectron > conduction_ions
Retrieve electric and magnetic field from Sensor.
double m_energy
Photon energy [MeV].
static AtomPhotoAbsCS * getAPACS(const std::string &name)
static void reset_counter()
Reset the counter.
vfloat time() const
Get the current time of the particle.
const vec & position() const
Get the current position of the particle.
const vec & direction() const
Get the current direction of the particle.
virtual void fly(std::vector< gparticle * > &secondaries)
Transport the particle.
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.
void RndmDirection(double &dx, double &dy, double &dz, const double length=1.)
Draw a random (isotropic) direction vector.
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)
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
DoubleAc sqrt(const DoubleAc &f)
particle_def proton_def("proton", "p+", proton_mass_c2/c_squared, eplus, 0.5)
std::vector< Electron > ions
std::vector< Photon > photons
std::vector< Electron > electrons