Garfield++ v2r0
A toolkit for the detailed simulation of particle detectors based on ionisation measurement in gases and semiconductors
Loading...
Searching...
No Matches
Garfield::TrackSrim Class Reference

#include <TrackSrim.hh>

+ Inheritance diagram for Garfield::TrackSrim:

Classes

struct  cluster
 

Public Member Functions

 TrackSrim ()
 Constructor.
 
virtual ~TrackSrim ()
 Destructor.
 
void SetWorkFunction (const double w)
 Set/get the W value [eV].
 
double GetWorkFunction () const
 
void SetFanoFactor (const double f)
 Set/get the Fano factor.
 
double GetFanoFactor () const
 
void SetDensity (const double density)
 Set/get the density [g/cm3] of the target medium.
 
double GetDensity () const
 
void SetAtomicMassNumbers (const double a, const double z)
 Set/get A and Z of the target medium.
 
void GetAtomicMassMumbers (double &a, double &z) const
 
void SetModel (const int m)
 
int GetModel () const
 
void EnableTransverseStraggling ()
 
void DisableTransverseStraggling ()
 
void EnableLongitudinalStraggling ()
 
void DisableLongitudinalStraggling ()
 
void EnablePreciseVavilov ()
 
void DisablePreciseVavilov ()
 
void SetTargetClusterSize (const int n)
 
int GetTargetClusterSize () const
 
void SetClustersMaximum (const int n)
 
int SetClustersMaximum () const
 
bool ReadFile (const std::string &file)
 
void Print ()
 
void PlotEnergyLoss ()
 
void PlotRange ()
 
void PlotStraggling ()
 
virtual bool NewTrack (const double x0, const double y0, const double z0, const double t0, const double dx0, const double dy0, const double dz0)
 
virtual bool GetCluster (double &xcls, double &ycls, double &zcls, double &tcls, int &n, double &e, double &extra)
 
- Public Member Functions inherited from Garfield::Track
 Track ()
 Constructor.
 
virtual ~Track ()
 Destructor.
 
virtual void SetParticle (const std::string &part)
 Set the type of particle.
 
void SetEnergy (const double e)
 Set the particle energy.
 
void SetBetaGamma (const double bg)
 Set the relative momentum of the particle.
 
void SetBeta (const double beta)
 Set the speed ( $\beta = v/c$) of the particle.
 
void SetGamma (const double gamma)
 Set the Lorentz factor of the particle.
 
void SetMomentum (const double p)
 Set the particle momentum.
 
void SetKineticEnergy (const double ekin)
 Set the kinetic energy of the particle.
 
double GetEnergy () const
 
double GetBetaGamma () const
 
double GetBeta () const
 
double GetGamma () const
 
double GetMomentum () const
 
double GetKineticEnergy () const
 
double GetCharge () const
 Get the charge of the projectile.
 
double GetMass () const
 Get the mass [eV / c2] of the projectile.
 
void SetSensor (Sensor *s)
 
virtual bool NewTrack (const double x0, const double y0, const double z0, const double t0, const double dx0, const double dy0, const double dz0)=0
 
virtual bool GetCluster (double &xcls, double &ycls, double &zcls, double &tcls, int &n, double &e, double &extra)=0
 
virtual double GetClusterDensity ()
 
virtual double GetStoppingPower ()
 Get the stopping power (mean energy loss [eV] per cm).
 
void EnablePlotting (ViewDrift *viewer)
 
void DisablePlotting ()
 
void EnableDebugging ()
 
void DisableDebugging ()
 

Protected Member Functions

double DedxEM (const double e) const
 
double DedxHD (const double e) const
 
bool PreciseLoss (const double step, const double estart, double &deem, double &dehd) const
 
bool EstimateRange (const double ekin, const double step, double &stpmax)
 
bool SmallestStep (double ekin, double de, double step, double &stpmin)
 
double RndmEnergyLoss (const double ekin, const double de, const double step) const
 
- Protected Member Functions inherited from Garfield::Track
void PlotNewTrack (const double x0, const double y0, const double z0)
 
void PlotCluster (const double x0, const double y0, const double z0)
 

Protected Attributes

bool m_precisevavilov
 Use precise Vavilov generator.
 
bool m_useTransStraggle
 Include transverse straggling.
 
bool m_useLongStraggle
 Include longitudinal straggling.
 
bool m_chargeset
 Charge gas been defined.
 
double m_density
 Density [g/cm3].
 
double m_work
 Work function [eV].
 
double m_fano
 Fano factor [-].
 
double m_q
 Charge of ion.
 
double m_mass
 Mass of ion [MeV].
 
double m_a
 A and Z of the gas.
 
double m_z
 
int m_maxclusters
 Maximum number of clusters allowed (infinite if 0)
 
std::vector< double > m_ekin
 Energy in energy loss table [MeV].
 
std::vector< double > m_emloss
 EM energy loss [MeV cm2/g].
 
std::vector< double > m_hdloss
 Hadronic energy loss [MeV cm2/g].
 
std::vector< double > m_range
 Projected range [cm].
 
std::vector< double > m_transstraggle
 Transverse straggling [cm].
 
std::vector< double > m_longstraggle
 Longitudinal straggling [cm].
 
unsigned int m_currcluster
 Index of the next cluster to be returned.
 
unsigned int m_model
 
int m_nsize
 Targeted cluster size.
 
std::vector< clusterm_clusters
 
- Protected Attributes inherited from Garfield::Track
std::string m_className
 
double m_q
 
int m_spin
 
double m_mass
 
double m_energy
 
double m_beta2
 
bool m_isElectron
 
std::string m_particleName
 
Sensorm_sensor
 
bool m_isChanged
 
bool m_usePlotting
 
ViewDriftm_viewer
 
bool m_debug
 
int m_plotId
 

Detailed Description

Generate tracks based on SRIM energy loss, range and straggling tables.

Definition at line 11 of file TrackSrim.hh.

Constructor & Destructor Documentation

◆ TrackSrim()

Garfield::TrackSrim::TrackSrim ( )

Constructor.

Definition at line 122 of file TrackSrim.cc.

123 : Track(),
124 m_precisevavilov(false),
125 m_useTransStraggle(true),
126 m_useLongStraggle(false),
127 m_chargeset(false),
128 m_density(-1.0),
129 m_work(-1.),
130 m_fano(-1.),
131 m_a(-1.),
132 m_z(-1.),
133 m_maxclusters(-1),
134 m_model(4),
135 m_nsize(-1) {
136 m_className = "TrackSrim";
137 m_mass = -1.;
138}
double m_mass
Mass of ion [MeV].
Definition: TrackSrim.hh:88
bool m_chargeset
Charge gas been defined.
Definition: TrackSrim.hh:78
double m_fano
Fano factor [-].
Definition: TrackSrim.hh:84
bool m_precisevavilov
Use precise Vavilov generator.
Definition: TrackSrim.hh:71
double m_work
Work function [eV].
Definition: TrackSrim.hh:82
unsigned int m_model
Definition: TrackSrim.hh:112
double m_density
Density [g/cm3].
Definition: TrackSrim.hh:80
bool m_useTransStraggle
Include transverse straggling.
Definition: TrackSrim.hh:73
int m_nsize
Targeted cluster size.
Definition: TrackSrim.hh:114
bool m_useLongStraggle
Include longitudinal straggling.
Definition: TrackSrim.hh:75
double m_a
A and Z of the gas.
Definition: TrackSrim.hh:90
int m_maxclusters
Maximum number of clusters allowed (infinite if 0)
Definition: TrackSrim.hh:94
std::string m_className
Definition: Track.hh:80
Track()
Constructor.
Definition: Track.cc:11

◆ ~TrackSrim()

virtual Garfield::TrackSrim::~TrackSrim ( )
inlinevirtual

Destructor.

Definition at line 17 of file TrackSrim.hh.

17{}

Member Function Documentation

◆ DedxEM()

double Garfield::TrackSrim::DedxEM ( const double  e) const
protected

Definition at line 513 of file TrackSrim.cc.

513 {
514 return Interpolate(e, m_ekin, m_emloss);
515}
std::vector< double > m_emloss
EM energy loss [MeV cm2/g].
Definition: TrackSrim.hh:98
std::vector< double > m_ekin
Energy in energy loss table [MeV].
Definition: TrackSrim.hh:96

Referenced by NewTrack(), and PreciseLoss().

◆ DedxHD()

double Garfield::TrackSrim::DedxHD ( const double  e) const
protected

Definition at line 517 of file TrackSrim.cc.

517 {
518 return Interpolate(e, m_ekin, m_hdloss);
519}
std::vector< double > m_hdloss
Hadronic energy loss [MeV cm2/g].
Definition: TrackSrim.hh:100

Referenced by NewTrack(), and PreciseLoss().

◆ DisableLongitudinalStraggling()

void Garfield::TrackSrim::DisableLongitudinalStraggling ( )
inline

Definition at line 46 of file TrackSrim.hh.

46{ m_useLongStraggle = false; }

◆ DisablePreciseVavilov()

void Garfield::TrackSrim::DisablePreciseVavilov ( )
inline

Definition at line 49 of file TrackSrim.hh.

49{ m_precisevavilov = false; }

◆ DisableTransverseStraggling()

void Garfield::TrackSrim::DisableTransverseStraggling ( )
inline

Definition at line 44 of file TrackSrim.hh.

44{ m_useTransStraggle = false; }

◆ EnableLongitudinalStraggling()

void Garfield::TrackSrim::EnableLongitudinalStraggling ( )
inline

Definition at line 45 of file TrackSrim.hh.

45{ m_useLongStraggle = true; }

◆ EnablePreciseVavilov()

void Garfield::TrackSrim::EnablePreciseVavilov ( )
inline

Definition at line 48 of file TrackSrim.hh.

48{ m_precisevavilov = true; }

◆ EnableTransverseStraggling()

void Garfield::TrackSrim::EnableTransverseStraggling ( )
inline

Definition at line 43 of file TrackSrim.hh.

43{ m_useTransStraggle = true; }

◆ EstimateRange()

bool Garfield::TrackSrim::EstimateRange ( const double  ekin,
const double  step,
double &  stpmax 
)
protected

Definition at line 602 of file TrackSrim.cc.

603 {
604 // Find distance over which the ion just does not lose all its energy
605 // ekin : Kinetic energy [MeV]
606 // step : Step length as guessed [cm]
607 // stpmax : Maximum step
608 // SRMDEZ
609
610 const std::string hdr = m_className + "::EstimateRange: ";
611 // Initial estimate
612 stpmax = step;
613
614 // Find the energy loss expected for the present step length.
615 double st1 = step;
616 double deem = 0., dehd = 0.;
617 PreciseLoss(st1, ekin, deem, dehd);
618 double de1 = deem + dehd;
619 // Do nothing if this is ok
620 if (de1 < ekin) {
621 if (m_debug) std::cout << hdr << "Initial step OK.\n";
622 return true;
623 }
624 // Find a smaller step for which the energy loss is less than EKIN.
625 double st2 = 0.5 * step;
626 double de2 = de1;
627 const unsigned int nMaxIter = 20;
628 for (unsigned int iter = 0; iter < nMaxIter; ++iter) {
629 // See where we stand
630 PreciseLoss(st2, ekin, deem, dehd);
631 de2 = deem + dehd;
632 // Below the kinetic energy: done
633 if (de2 < ekin) break;
634 // Not yet below the kinetic energy: new iteration.
635 st1 = st2;
636 de1 = de2;
637 st2 *= 0.5;
638 }
639 if (de2 >= ekin) {
640 std::cerr << hdr << "\n Did not find a smaller step in " << nMaxIter
641 << " iterations. Abandoned.\n";
642 stpmax = 0.5 * (st1 + st2);
643 return false;
644 }
645 if (m_debug)
646 printf("\tstep 1 = %g cm, de 1 = %g MeV\n\tstep 2 = %g cm, de 2 = %g MeV\n",
647 st1, de1 - ekin, st2, de2 - ekin);
648
649 // Now perform a bisection
650 for (unsigned int iter = 0; iter < nMaxIter; ++iter) {
651 // Avoid division by zero.
652 if (de2 == de1) {
653 if (m_debug) {
654 std::cerr << hdr << "Bisection failed due to equal energy loss for "
655 << "two step sizes. Abandoned.\n";
656 }
657 stpmax = 0.5 * (st1 + st2);
658 return false;
659 }
660 // Estimate step to give total energy loss.
661 double st3;
662 if ((fabs(de1 - ekin) < 0.01 * fabs(de2 - de1)) ||
663 (fabs(de1 - ekin) > 0.99 * fabs(de2 - de1))) {
664 st3 = 0.5 * (st1 + st2);
665 } else {
666 st3 = st1 - (st2 - st1) * (de1 - ekin) / (de2 - de1);
667 }
668 // See how well we are doing.
669 PreciseLoss(st3, ekin, deem, dehd);
670 const double de3 = deem + dehd;
671 if (m_debug) printf("\tStep 1 = %g cm, dE 1 = %g MeV\n", st1, de1 - ekin);
672 if (m_debug) printf("\tStep 2 = %g cm, dE 2 = %g MeV\n", st2, de2 - ekin);
673 if (m_debug) printf("\tStep 3 = %g cm, dE 3 = %g MeV\n", st3, de3 - ekin);
674 // Update the estimates above and below.
675 if (de3 > ekin) {
676 st1 = st3;
677 de1 = de3;
678 } else {
679 st2 = st3;
680 de2 = de3;
681 }
682 // See whether we've converged.
683 if (fabs(de3 - ekin) < 1e-3 * (fabs(de3) + fabs(ekin)) ||
684 fabs(st1 - st2) < 1e-3 * (fabs(st1) + fabs(st2))) {
685 stpmax = st1 - (st2 - st1) * (de1 - ekin) / (de2 - de1);
686 return true;
687 }
688 }
689 if (m_debug) {
690 std::cout << hdr << "Bisection did not converge in " << nMaxIter
691 << " steps. Abandoned.\n";
692 }
693 stpmax = st1 - (st2 - st1) * (de1 - ekin) / (de2 - de1);
694 return false;
695}
bool PreciseLoss(const double step, const double estart, double &deem, double &dehd) const
Definition: TrackSrim.cc:521
bool m_debug
Definition: Track.hh:97
DoubleAc fabs(const DoubleAc &f)
Definition: DoubleAc.h:615

Referenced by NewTrack().

◆ GetAtomicMassMumbers()

void Garfield::TrackSrim::GetAtomicMassMumbers ( double &  a,
double &  z 
) const
inline

Definition at line 33 of file TrackSrim.hh.

33 {
34 a = m_a;
35 z = m_z;
36 }

◆ GetCluster()

bool Garfield::TrackSrim::GetCluster ( double &  xcls,
double &  ycls,
double &  zcls,
double &  tcls,
int &  n,
double &  e,
double &  extra 
)
virtual

Get the next "cluster" (ionising collision of the charged particle).

Parameters
xcls,ycls,zclscoordinates of the collision
tclstime of the collision
nnumber of electrons produced
edeposited energy
extraadditional information (not always implemented)

Implements Garfield::Track.

Definition at line 1330 of file TrackSrim.cc.

1331 {
1332 if (m_debug) {
1333 printf("Current cluster: %d, array size: %ld",
1334 m_currcluster, m_clusters.size());
1335 }
1336 // Stop if we have exhausted the list of clusters.
1337 if (m_currcluster >= m_clusters.size()) return false;
1338
1339 xcls = m_clusters[m_currcluster].x;
1340 ycls = m_clusters[m_currcluster].y;
1341 zcls = m_clusters[m_currcluster].z;
1342 tcls = m_clusters[m_currcluster].t;
1343
1344 n = m_clusters[m_currcluster].electrons;
1345 e = m_clusters[m_currcluster].ec;
1346 extra = m_clusters[m_currcluster].kinetic;
1347 // Move to next cluster
1348 ++m_currcluster;
1349 return true;
1350}
unsigned int m_currcluster
Index of the next cluster to be returned.
Definition: TrackSrim.hh:109
std::vector< cluster > m_clusters
Definition: TrackSrim.hh:121

◆ GetDensity()

double Garfield::TrackSrim::GetDensity ( ) const
inline

Definition at line 27 of file TrackSrim.hh.

27{ return m_density; }

◆ GetFanoFactor()

double Garfield::TrackSrim::GetFanoFactor ( ) const
inline

Definition at line 24 of file TrackSrim.hh.

24{ return m_fano; }

◆ GetModel()

int Garfield::TrackSrim::GetModel ( ) const
inline

Definition at line 41 of file TrackSrim.hh.

41{ return m_model; }

◆ GetTargetClusterSize()

int Garfield::TrackSrim::GetTargetClusterSize ( ) const
inline

Definition at line 52 of file TrackSrim.hh.

52{ return m_nsize; }

◆ GetWorkFunction()

double Garfield::TrackSrim::GetWorkFunction ( ) const
inline

Definition at line 21 of file TrackSrim.hh.

21{ return m_work; }

◆ NewTrack()

bool Garfield::TrackSrim::NewTrack ( const double  x0,
const double  y0,
const double  z0,
const double  t0,
const double  dx0,
const double  dy0,
const double  dz0 
)
virtual

Calculate a new track starting from (x0, y0, z0) at time t0 in direction (dx0, dy0, dz0).

Implements Garfield::Track.

Definition at line 698 of file TrackSrim.cc.

700 {
701
702 // Generates electrons for a SRIM track
703 // SRMGEN
704 const std::string hdr = m_className + "::NewTrack: ";
705
706 // Verify that a sensor has been set.
707 if (!m_sensor) {
708 std::cerr << hdr << "\n Sensor is not defined.\n";
709 return false;
710 }
711
712 // Get the bounding box.
713 double xmin = 0., ymin = 0., zmin = 0.;
714 double xmax = 0., ymax = 0., zmax = 0.;
715 if (!m_sensor->GetArea(xmin, ymin, zmin, xmax, ymax, zmax)) {
716 std::cerr << hdr << "\n Drift area is not set.\n";
717 return false;
718 } else if (x0 < xmin || x0 > xmax ||
719 y0 < ymin || y0 > ymax || z0 < zmin || z0 > zmax) {
720 std::cerr << hdr << "\n Initial position outside bounding box.\n";
721 return false;
722 }
723
724 // Make sure the initial position is inside an ionisable medium.
725 Medium* medium = NULL;
726 if (!m_sensor->GetMedium(x0, y0, z0, medium)) {
727 std::cerr << hdr << "\n No medium at initial position.\n";
728 return false;
729 } else if (!medium->IsIonisable()) {
730 std::cerr << hdr << "\n Medium at initial position is not ionisable.\n";
731 return false;
732 }
733
734 // Normalise and store the direction.
735 const double normdir = sqrt(dx0 * dx0 + dy0 * dy0 + dz0 * dz0);
736 double xdir = dx0;
737 double ydir = dy0;
738 double zdir = dz0;
739 if (normdir < Small) {
740 if (m_debug) {
741 std::cout << hdr << "\n Direction vector has zero norm.\n"
742 << " Initial direction is randomized.\n";
743 }
744 // Null vector. Sample the direction isotropically.
745 RndmDirection(xdir, ydir, zdir);
746 } else {
747 // Normalise the direction vector.
748 xdir /= normdir;
749 ydir /= normdir;
750 zdir /= normdir;
751 }
752
753 // Make sure all necessary parameters have been set.
754 if (m_mass < Small) {
755 std::cerr << hdr << "\n Particle mass not set.\n";
756 return false;
757 } else if (!m_chargeset) {
758 std::cerr << hdr << "\n Particle charge not set.\n";
759 return false;
760 } else if (m_energy < Small) {
761 std::cerr << hdr << "\n Initial particle energy not set.\n";
762 return false;
763 } else if (m_work < Small) {
764 std::cerr << hdr << "\n Work function not set.\n";
765 return false;
766 } else if (m_a < Small || m_z < Small) {
767 std::cerr << hdr << "\n A and/or Z not set.\n";
768 return false;
769 }
770 // Check the initial energy (in MeV).
771 const double ekin0 = 1.e-6 * GetKineticEnergy();
772 if (ekin0 < 1.e-14 * m_mass || ekin0 < 1.e-3 * m_work) {
773 if (m_debug) {
774 std::cout << hdr << "Initial kinetic energy E = " << ekin0
775 << " MeV such that beta2 = 0 or E << W; particle stopped.\n";
776 }
777 return true;
778 }
779
780 // Get an upper limit for the track length.
781 const double tracklength = 10 * Interpolate(ekin0, m_ekin, m_range);
782
783 // Header of debugging output.
784 if (m_debug) {
785 std::cout << hdr << "Track generation with the following parameters:\n";
786 const unsigned int nTable = m_ekin.size();
787 printf(" Table size %u\n", nTable);
788 printf(" Particle kin. energy %g MeV\n", ekin0);
789 printf(" Particle mass %g MeV\n", 1.e-6 * m_mass);
790 printf(" Particle charge %g\n", m_q);
791 printf(" Work function %g eV\n", m_work);
792 if (m_fano > 0.) {
793 printf(" Fano factor %g\n", m_fano);
794 } else {
795 std::cout << " Fano factor Not set\n";
796 }
797 printf(" Long. straggling: %d\n", m_useLongStraggle);
798 printf(" Trans. straggling: %d\n", m_useTransStraggle);
799 // printf(" Vavilov generator: %d\n", m_precisevavilov);
800 printf(" Cluster size %d\n", m_nsize);
801 }
802
803 // Reset the cluster count
804 m_currcluster = 0;
805 m_clusters.clear();
806
807
808 // Initial situation: starting position
809 double x = x0;
810 double y = y0;
811 double z = z0;
812
813 // Store the energy [MeV].
814 double e = ekin0;
815 // Total distance covered
816 double dsum = 0.0;
817 // Pool of unused energy
818 double epool = 0.0;
819
820 // Loop generating clusters
821 int iter = 0;
822 while (iter < m_maxclusters || m_maxclusters < 0) {
823 // Work out what the energy loss per cm, straggling and projected range are
824 // at the start of the step.
825 const double dedxem = DedxEM(e) * m_density;
826 const double dedxhd = DedxHD(e) * m_density;
827 const double prange = Interpolate(e, m_ekin, m_range);
828 double strlon = Interpolate(e, m_ekin, m_longstraggle);
829 double strlat = Interpolate(e, m_ekin, m_transstraggle);
830
831 if (!m_useLongStraggle) strlon = 0;
832 if (!m_useTransStraggle) strlat = 0;
833
834 if (m_debug) {
835 std::cout << hdr << "\n Energy = " << e << " MeV,\n dEdx em, hd = "
836 << dedxem << ", " << dedxhd << " MeV/cm,\n e-/cm = "
837 << 1.e6 * dedxem / m_work << ".\n Straggling long/lat: "
838 << strlon << ", " << strlat << " cm\n";
839 }
840 // Find the step size for which we get approximately the target # clusters.
841 double step;
842 if (m_nsize > 0) {
843 step = m_nsize * 1.e-6 * m_work / dedxem;
844 } else {
845 const double ncls = m_maxclusters > 0 ? 0.5 * m_maxclusters : 100;
846 step = ekin0 / (ncls * (dedxem + dedxhd));
847 }
848 // Truncate if this step exceeds the length.
849 bool finish = false;
850 // Make an accurate integration of the energy loss over the step.
851 double deem = 0., dehd = 0.;
852 PreciseLoss(step, e, deem, dehd);
853 // If the energy loss exceeds the particle energy, truncate step.
854 double stpmax;
855 if (deem + dehd > e) {
856 EstimateRange(e, step, stpmax);
857 step = stpmax;
858 PreciseLoss(step, e, deem, dehd);
859 deem = e * deem / (dehd + deem);
860 dehd = e - deem;
861 finish = true;
862 if (m_debug) std::cout << hdr << "Finish raised. Track length reached.\n";
863 } else {
864 stpmax = tracklength - dsum;
865 }
866
867 // Ensure that this is larger than the minimum modelable step size.
868 double stpmin;
869 if (!SmallestStep(e, deem, step, stpmin)) {
870 std::cerr << hdr << "\n Failure computing the minimum step size."
871 << "\n Clustering abandoned.\n";
872 return false;
873 }
874
875 double eloss;
876 if (stpmin > stpmax) {
877 // No way to find a suitable step size: use fixed energy loss
878 if (m_debug) std::cout << hdr << "stpmin > stpmax. Deposit all energy.\n";
879 eloss = deem;
880 if (e - eloss - dehd < 0) eloss = e - dehd;
881 finish = true;
882 if (m_debug) std::cout << hdr << "Finish raised. Single deposit.\n";
883 } else if (step < stpmin) {
884 // If needed enlarge the step size
885 if (m_debug) std::cout << hdr << "Enlarging step size.\n";
886 step = stpmin;
887 PreciseLoss(step, e, deem, dehd);
888 if (deem + dehd > e) {
889 if (m_debug) std::cout << hdr << "Excess loss. Recomputing stpmax.\n";
890 EstimateRange(e, step, stpmax);
891 step = stpmax;
892 PreciseLoss(step, e, deem, dehd);
893 deem = e * deem / (dehd + deem);
894 dehd = e - deem;
895 eloss = deem;
896 } else {
897 eloss = RndmEnergyLoss(e, deem, step);
898 }
899 } else {
900 // Draw an actual energy loss for such a step.
901 if (m_debug) std::cout << hdr << "Using existing step size.\n";
902 eloss = RndmEnergyLoss(e, deem, step);
903 }
904 // Ensure we are neither below 0 nor above the total energy.
905 if (eloss < 0) {
906 if (m_debug) std::cout << hdr << "Truncating negative energy loss.\n";
907 eloss = 0;
908 } else if (eloss > e - dehd) {
909 if (m_debug) std::cout << hdr << "Excess energy loss, using mean.\n";
910 eloss = deem;
911 if (e - eloss - dehd < 0) {
912 eloss = e - dehd;
913 finish = true;
914 if (m_debug) std::cout << hdr << "Finish raised. Using mean energy.\n";
915 }
916 }
917 if (m_debug) {
918 std::cout << hdr << "\n Step length = " << step << " cm.\n "
919 << "Mean loss = " << deem << " MeV.\n "
920 << "Actual loss = " << eloss << " MeV.\n";
921 }
922
923 // Check that the cluster is in an ionisable medium and within bounding box
924 if (!m_sensor->GetMedium(x, y, z, medium)) {
925 if (m_debug) {
926 std::cout << hdr << "No medium at position ("
927 << x << "," << y << "," << z << ").\n";
928 }
929 break;
930 } else if (!medium->IsIonisable()) {
931 if (m_debug) {
932 std::cout << hdr << "Medium at ("
933 << x << "," << y << "," << z << ") is not ionisable.\n";
934 }
935 break;
936 } else if (!m_sensor->IsInArea(x, y, z)) {
937 if (m_debug) {
938 std::cout << hdr << "Cluster at ("
939 << x << "," << y << "," << z << ") outside bounding box.\n";
940 }
941 break;
942 }
943 // Add a cluster.
944 cluster newcluster;
945 newcluster.x = x;
946 newcluster.y = y;
947 newcluster.z = z;
948 newcluster.t = t0;
949 if (m_fano < Small) {
950 // No fluctuations.
951 newcluster.electrons = int((eloss + epool) / (1.e-6 * m_work));
952 newcluster.ec = m_work * newcluster.electrons;
953 } else {
954 double ecl = 1.e6 * (eloss + epool);
955 newcluster.electrons = 0.0;
956 newcluster.ec = 0.0;
957 while (true) {
958 // if (newcluster.ec < 100) printf("ec = %g\n", newcluster.ec);
959 const double ernd1 = RndmHeedWF(m_work, m_fano);
960 if (ernd1 > ecl) break;
961 newcluster.electrons++;
962 newcluster.ec += ernd1;
963 ecl -= ernd1;
964 }
965 // printf("ec = %g DONE\n", newcluster.ec);
966 if (m_debug)
967 std::cout << hdr << "EM + pool: " << 1.e6 * (eloss + epool)
968 << " eV, W: " << m_work << " eV, E/w: "
969 << (eloss + epool) / (1.0e-6 * m_work) << ", n: "
970 << newcluster.electrons << ".\n";
971 }
972 newcluster.kinetic = e;
973 epool += eloss - 1.e-6 * newcluster.ec;
974 if (m_debug) {
975 std::cout << hdr << "Cluster " << m_clusters.size() << "\n at ("
976 << newcluster.x << ", " << newcluster.y << ", " << newcluster.z
977 << "),\n e = " << newcluster.ec << ",\n n = "
978 << newcluster.electrons << ",\n pool = "
979 << epool << " MeV.\n";
980 }
981 m_clusters.push_back(newcluster);
982
983 // Keep track of the length and energy
984 dsum += step;
985 e -= eloss + dehd;
986 if (finish) {
987 // Stop if the flag is raised
988 if (m_debug) std::cout << hdr << "Finishing flag raised.\n";
989 break;
990 } else if (e < ekin0 * 1.e-9) {
991 // No energy left
992 if (m_debug) std::cout << hdr << "Energy exhausted.\n";
993 break;
994 }
995 // Draw scattering distances
996 const double scale = sqrt(step / prange);
997 const double sigt1 = RndmGaussian(0., scale * strlat);
998 const double sigt2 = RndmGaussian(0., scale * strlat);
999 const double sigl = RndmGaussian(0., scale * strlon);
1000 if (m_debug) std::cout << hdr << "sigma l, t1, t2: "
1001 << sigl << ", " << sigt1 << ", " << sigt2 << "\n";
1002 // Rotation angles to bring z-axis in line
1003 double theta, phi;
1004 if (xdir * xdir + zdir * zdir <= 0) {
1005 if (ydir < 0) {
1006 theta = -HalfPi;
1007 } else if (ydir > 0) {
1008 theta = +HalfPi;
1009 } else {
1010 std::cerr << hdr << "\n Zero step length; clustering abandoned.\n";
1011 return false;
1012 }
1013 phi = 0;
1014 } else {
1015 phi = atan2(xdir, zdir);
1016 theta = atan2(ydir, sqrt(xdir * xdir + zdir * zdir));
1017 }
1018
1019 // Update position
1020 const double cp = cos(phi);
1021 const double ct = cos(theta);
1022 const double sp = sin(phi);
1023 const double st = sin(theta);
1024 x += step * xdir + cp * sigt1 - sp * st * sigt2 + sp * ct * sigl;
1025 y += step * ydir + ct * sigt2 + st * sigl;
1026 z += step * zdir - sp * sigt1 - cp * st * sigt2 + cp * ct * sigl;
1027
1028 // (Do not) update direction
1029 if (false) {
1030 xdir = step * xdir + cp * sigt1 - sp * st * sigt2 + sp * ct * sigl;
1031 ydir = step * ydir + ct * sigt2 + st * sigl;
1032 zdir = step * zdir - sp * sigt1 - cp * st * sigt2 + cp * ct * sigl;
1033 double dnorm = sqrt(xdir * xdir + ydir * ydir + zdir * zdir);
1034 if (dnorm <= 0) {
1035 std::cerr << hdr << "\n Zero step length; clustering abandoned.\n";
1036 return false;
1037 }
1038 xdir = xdir / dnorm;
1039 ydir = ydir / dnorm;
1040 zdir = zdir / dnorm;
1041 }
1042 // Next cluster
1043 iter++;
1044 }
1045 if (iter == m_maxclusters) {
1046 std::cerr << hdr << "Exceeded maximum number of clusters.\n";
1047 }
1048 return true;
1049 // finished generating
1050}
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
std::vector< double > m_longstraggle
Longitudinal straggling [cm].
Definition: TrackSrim.hh:106
double RndmEnergyLoss(const double ekin, const double de, const double step) const
Definition: TrackSrim.cc:1226
double m_q
Charge of ion.
Definition: TrackSrim.hh:86
std::vector< double > m_range
Projected range [cm].
Definition: TrackSrim.hh:102
double DedxHD(const double e) const
Definition: TrackSrim.cc:517
bool SmallestStep(double ekin, double de, double step, double &stpmin)
Definition: TrackSrim.cc:1052
std::vector< double > m_transstraggle
Transverse straggling [cm].
Definition: TrackSrim.hh:104
bool EstimateRange(const double ekin, const double step, double &stpmax)
Definition: TrackSrim.cc:602
double DedxEM(const double e) const
Definition: TrackSrim.cc:513
double GetKineticEnergy() const
Definition: Track.hh:43
Sensor * m_sensor
Definition: Track.hh:90
double m_energy
Definition: Track.hh:85
double RndmHeedWF(const double w, const double f)
Definition: Random.cc:659
void RndmDirection(double &dx, double &dy, double &dz, const double length=1.)
Draw a random (isotropic) direction vector.
Definition: Random.hh:106
double RndmGaussian()
Draw a Gaussian random variate with mean zero and standard deviation one.
Definition: Random.hh:25
DoubleAc cos(const DoubleAc &f)
Definition: DoubleAc.cpp:432
DoubleAc sin(const DoubleAc &f)
Definition: DoubleAc.cpp:384
DoubleAc sqrt(const DoubleAc &f)
Definition: DoubleAc.cpp:314

◆ PlotEnergyLoss()

void Garfield::TrackSrim::PlotEnergyLoss ( )

Definition at line 367 of file TrackSrim.cc.

367 {
368
369 // Make a graph for the 3 curves to plot
370 double xmin = 0., xmax = 0.;
371 double ymin = 0., ymax = 0.;
372 const unsigned int nPoints = m_ekin.size();
373 TGraph* grem = new TGraph(nPoints);
374 TGraph* grhd = new TGraph(nPoints);
375 TGraph* grtot = new TGraph(nPoints);
376 for (unsigned int i = 0; i < nPoints; ++i) {
377 const double eplot = m_ekin[i];
378 if (eplot < xmin) xmin = eplot;
379 if (eplot > xmax) xmax = eplot;
380 const double emplot = m_emloss[i] * m_density;
381 const double hdplot = m_hdloss[i] * m_density;
382 const double totplot = emplot + hdplot;
383 if (totplot < ymin) ymin = totplot;
384 if (totplot > ymax) ymax = totplot;
385 grem->SetPoint(i, eplot, emplot);
386 grhd->SetPoint(i, eplot, hdplot);
387 grtot->SetPoint(i, eplot, totplot);
388 }
389
390 // Prepare a plot frame
391 TCanvas* celoss = new TCanvas();
392 celoss->SetLogx();
393 frame(celoss, xmin, 0.0, xmax, ymax * 1.05, "Ion energy [MeV]",
394 "Energy loss [MeV/cm]");
395 TLegend* legend = new TLegend(0.65, 0.75, 0.94, 0.95);
396 legend->SetShadowColor(0);
397 legend->SetTextFont(22);
398 legend->SetTextSize(0.044);
399 legend->SetFillColor(kWhite);
400 legend->SetBorderSize(1);
401
402 grem->SetLineColor(kBlue);
403 grem->SetLineStyle(kSolid);
404 grem->SetLineWidth(2.0);
405 grem->SetMarkerStyle(21);
406 grem->SetMarkerColor(kBlack);
407 grem->Draw("SAME");
408 legend->AddEntry(grem, "EM energy loss", "l");
409
410 grhd->SetLineColor(kGreen + 2);
411 grhd->SetLineStyle(kSolid);
412 grhd->SetLineWidth(2.0);
413 grhd->SetMarkerStyle(21);
414 grhd->SetMarkerColor(kBlack);
415 grhd->Draw("SAME");
416 legend->AddEntry(grhd, "HD energy loss", "l");
417
418 grtot->SetLineColor(kOrange);
419 grtot->SetLineStyle(kSolid);
420 grtot->SetLineWidth(2.0);
421 grtot->SetMarkerStyle(21);
422 grtot->SetMarkerColor(kBlack);
423 grtot->Draw("SAME");
424 legend->AddEntry(grtot, "Total energy loss", "l");
425
426 legend->Draw();
427}

◆ PlotRange()

void Garfield::TrackSrim::PlotRange ( )

Definition at line 429 of file TrackSrim.cc.

429 {
430
431 // Make a graph
432 double xmin = 0., xmax = 0.;
433 double ymin = 0., ymax = 0.;
434 const unsigned int nPoints = m_ekin.size();
435 TGraph* grrange = new TGraph(nPoints);
436 for (unsigned int i = 0; i < nPoints; ++i) {
437 const double eplot = m_ekin[i];
438 if (eplot < xmin) xmin = eplot;
439 if (eplot > xmax) xmax = eplot;
440 const double rangeplot = m_range[i];
441 if (rangeplot < ymin) ymin = rangeplot;
442 if (rangeplot > ymax) ymax = rangeplot;
443 grrange->SetPoint(i, eplot, rangeplot);
444 }
445
446 // Prepare a plot frame
447 TCanvas* crange = new TCanvas();
448 crange->SetLogx();
449 frame(crange, xmin, 0.0, xmax, ymax * 1.05, "Ion energy [MeV]",
450 "Projected range [cm]");
451
452 grrange->SetLineColor(kOrange);
453 grrange->SetLineStyle(kSolid);
454 grrange->SetLineWidth(2.0);
455 grrange->SetMarkerStyle(21);
456 grrange->SetMarkerColor(kBlack);
457 grrange->Draw("SAME");
458}

◆ PlotStraggling()

void Garfield::TrackSrim::PlotStraggling ( )

Definition at line 460 of file TrackSrim.cc.

460 {
461
462 // Make a graph for the 2 curves to plot
463 double xmin = 0., xmax = 0.;
464 double ymin = 0., ymax = 0.;
465 const unsigned int nPoints = m_ekin.size();
466 TGraph* grlong = new TGraph(nPoints);
467 TGraph* grtrans = new TGraph(nPoints);
468 for (unsigned int i = 0; i < nPoints; ++i) {
469 const double eplot = m_ekin[i];
470 if (eplot < xmin) xmin = eplot;
471 if (eplot > xmax) xmax = eplot;
472 const double longplot = m_longstraggle[i];
473 const double transplot = m_transstraggle[i];
474 if (longplot < ymin) ymin = longplot;
475 if (longplot > ymax) ymax = longplot;
476 if (transplot < ymin) ymin = transplot;
477 if (transplot > ymax) ymax = transplot;
478 grlong->SetPoint(i, eplot, longplot);
479 grtrans->SetPoint(i, eplot, transplot);
480 }
481
482 // Prepare a plot frame
483 TCanvas* cstraggle = new TCanvas();
484 cstraggle->SetLogx();
485 frame(cstraggle, xmin, 0.0, xmax, ymax * 1.05, "Ion energy [MeV]",
486 "Straggling [cm]");
487 TLegend* legend = new TLegend(0.15, 0.75, 0.44, 0.95);
488 legend->SetShadowColor(0);
489 legend->SetTextFont(22);
490 legend->SetTextSize(0.044);
491 legend->SetFillColor(kWhite);
492 legend->SetBorderSize(1);
493
494 grlong->SetLineColor(kOrange);
495 grlong->SetLineStyle(kSolid);
496 grlong->SetLineWidth(2.0);
497 grlong->SetMarkerStyle(21);
498 grlong->SetMarkerColor(kBlack);
499 grlong->Draw("SAME");
500 legend->AddEntry(grlong, "Longitudinal", "l");
501
502 grtrans->SetLineColor(kGreen + 2);
503 grtrans->SetLineStyle(kSolid);
504 grtrans->SetLineWidth(2.0);
505 grtrans->SetMarkerStyle(21);
506 grtrans->SetMarkerColor(kBlack);
507 grtrans->Draw("SAME");
508 legend->AddEntry(grtrans, "Transversal", "l");
509
510 legend->Draw();
511}

◆ PreciseLoss()

bool Garfield::TrackSrim::PreciseLoss ( const double  step,
const double  estart,
double &  deem,
double &  dehd 
) const
protected

Definition at line 521 of file TrackSrim.cc.

522 {
523 // SRMRKS
524
525 const std::string hdr = m_className + "::PreciseLoss: ";
526 // Debugging
527 if (m_debug) {
528 std::cout << hdr << "\n"
529 << " Initial energy: " << estart << " MeV\n"
530 << " Step: " << step << " cm\n";
531 }
532 // Precision aimed for.
533 const double eps = 1.0e-2;
534 // Number of intervals.
535 unsigned int ndiv = 1;
536 // Loop until precision achieved
537 const unsigned int nMaxIter = 10;
538 bool converged = false;
539 for (unsigned int iter = 0; iter < nMaxIter; ++iter) {
540 double e4 = estart;
541 double e2 = estart;
542 deem = 0.;
543 dehd = 0.;
544 // Compute rk2 and rk4 over the number of sub-divisions
545 const double s = m_density * step / ndiv;
546 for (unsigned int i = 0; i < ndiv; i++) {
547 // rk2: initial point
548 const double em21 = s * DedxEM(e2);
549 const double hd21 = s * DedxHD(e2);
550 const double de21 = em21 + hd21;
551 // Mid-way point
552 const double em22 = s * DedxEM(e2 - 0.5 * de21);
553 const double hd22 = s * DedxHD(e2 - 0.5 * de21);
554 // Trace the rk2 energy
555 e2 -= em22 + hd22;
556 // rk4: initial point
557 const double em41 = s * DedxEM(e4);
558 const double hd41 = s * DedxHD(e4);
559 const double de41 = em41 + hd41;
560 // Mid-way point
561 const double em42 = s * DedxEM(e4 - 0.5 * de41);
562 const double hd42 = s * DedxHD(e4 - 0.5 * de41);
563 const double de42 = em42 + hd42;
564 // Second mid-point estimate
565 const double em43 = s * DedxEM(e4 - 0.5 * de42);
566 const double hd43 = s * DedxHD(e4 - 0.5 * de42);
567 const double de43 = em43 + hd43;
568 // End point estimate
569 const double em44 = s * DedxEM(e4 - de43);
570 const double hd44 = s * DedxHD(e4 - de43);
571 const double de44 = em44 + hd44;
572 // Store the energy loss terms (according to rk4)
573 deem += (em41 + em44) / 6. + (em42 + em43) / 3.;
574 dehd += (hd41 + hd44) / 6. + (hd42 + hd43) / 3.;
575 // Store the new energy computed with rk4
576 e4 -= (de41 + de44) / 6. + (de42 + de43) / 3.;
577 }
578 if (m_debug) {
579 std::cout << hdr << "\n Iteration " << iter << " has " << ndiv
580 << " division(s). Losses:\n";
581 printf("\tde4 = %12g, de2 = %12g MeV\n", estart - e2, estart - e4);
582 printf("\tem4 = %12g, hd4 = %12g MeV\n", deem, dehd);
583 }
584 // Compare the two estimates
585 if (fabs(e2 - e4) > eps * (fabs(e2) + fabs(e4) + fabs(estart))) {
586 // Repeat with twice the number of steps.
587 ndiv *= 2;
588 } else {
589 converged = true;
590 break;
591 }
592 }
593
594 if (!converged) {
595 std::cerr << hdr << "No convergence achieved integrating energy loss.\n";
596 } else if (m_debug) {
597 std::cout << hdr << "Convergence at eps = " << eps << "\n";
598 }
599 return converged;
600}

Referenced by EstimateRange(), and NewTrack().

◆ Print()

void Garfield::TrackSrim::Print ( )

Definition at line 345 of file TrackSrim.cc.

345 {
346
347 std::cout << "TrackSrim::Print:\n SRIM energy loss table\n\n"
348 << " Energy EM Loss HD loss Range "
349 << "l straggle t straggle\n"
350 << " [MeV] [MeV/cm] [MeV/cm] [cm] "
351 << " [cm] [cm]\n\n";
352 const unsigned int nPoints = m_emloss.size();
353 for (unsigned int i = 0; i < nPoints; ++i) {
354 printf("%10g %10g %10g %10g %10g %10g\n", m_ekin[i],
357 }
358 std::cout << "\n";
359 printf(" Work function: %g eV\n", m_work);
360 printf(" Fano factor: %g\n", m_fano);
361 printf(" Ion charge: %g\n", m_q);
362 printf(" Mass: %g MeV\n", 1.e-6 * m_mass);
363 printf(" Density: %g g/cm3\n", m_density);
364 printf(" A, Z: %g, %g\n", m_a, m_z);
365}

◆ ReadFile()

bool Garfield::TrackSrim::ReadFile ( const std::string &  file)

Definition at line 140 of file TrackSrim.cc.

140 {
141 // SRMREA
142
143 const std::string hdr = m_className + "::ReadFile:\n ";
144 // Open the material list.
145 std::ifstream fsrim;
146 fsrim.open(file.c_str(), std::ios::in);
147 if (fsrim.fail()) {
148 std::cerr << hdr << "Could not open SRIM file " << file
149 << " for reading.\n The file perhaps does not exist.\n";
150 return false;
151 }
152 unsigned int nread = 0;
153
154 // Read the header
155 if (m_debug) {
156 std::cout << hdr << "SRIM header records from file " << file << "\n";
157 }
158 const int size = 100;
159 char line[size];
160 while (fsrim.getline(line, 100, '\n')) {
161 nread++;
162 if (strstr(line, "SRIM version") != NULL) {
163 if (m_debug) std::cout << "\t" << line << "\n";
164 } else if (strstr(line, "Calc. date") != NULL) {
165 if (m_debug) std::cout << "\t" << line << "\n";
166 } else if (strstr(line, "Ion =") != NULL) {
167 break;
168 }
169 }
170
171 // Identify the ion
172 char* token = NULL;
173 token = strtok(line, " []=");
174 token = strtok(NULL, " []=");
175 token = strtok(NULL, " []=");
176 // Set the ion charge.
177 m_q = std::atof(token);
178 m_chargeset = true;
179 token = strtok(NULL, " []=");
180 token = strtok(NULL, " []=");
181 token = strtok(NULL, " []=");
182 // Set the ion mass (convert amu to eV).
183 m_mass = std::atof(token) * AtomicMassUnitElectronVolt;
184
185 // Find the target density
186 if (!fsrim.getline(line, 100, '\n')) {
187 std::cerr << hdr << "Premature EOF looking for target density (line "
188 << nread << ").\n";
189 return false;
190 }
191 nread++;
192 if (!fsrim.getline(line, 100, '\n')) {
193 std::cerr << hdr << "Premature EOF looking for target density (line "
194 << nread << ").\n";
195 return false;
196 }
197 nread++;
198 token = strtok(line, " ");
199 token = strtok(NULL, " ");
200 token = strtok(NULL, " ");
201 token = strtok(NULL, " ");
202 SetDensity(std::atof(token));
203
204 // Check the stopping units
205 while (fsrim.getline(line, 100, '\n')) {
206 nread++;
207 if (strstr(line, "Stopping Units") == NULL) continue;
208 if (strstr(line, "Stopping Units = MeV / (mg/cm2)") != NULL) {
209 if (m_debug) {
210 std::cout << hdr << "Stopping units: MeV / (mg/cm2) as expected.\n";
211 }
212 break;
213 }
214 std::cerr << hdr << "Unknown stopping units. Aborting (line " << nread
215 << ").\n";
216 return false;
217 }
218
219 // Skip to the table
220 while (fsrim.getline(line, 100, '\n')) {
221 nread++;
222 if (strstr(line, "-----------") != NULL) break;
223 }
224
225 // Read the table line by line
226 m_ekin.clear();
227 m_emloss.clear();
228 m_hdloss.clear();
229 m_range.clear();
230 m_transstraggle.clear();
231 m_longstraggle.clear();
232 unsigned int ntable = 0;
233 while (fsrim.getline(line, 100, '\n')) {
234 nread++;
235 if (strstr(line, "-----------") != NULL) break;
236 // Energy
237 token = strtok(line, " ");
238 m_ekin.push_back(atof(token));
239 token = strtok(NULL, " ");
240 if (strcmp(token, "eV") == 0) {
241 m_ekin[ntable] *= 1.0e-6;
242 } else if (strcmp(token, "keV") == 0) {
243 m_ekin[ntable] *= 1.0e-3;
244 } else if (strcmp(token, "GeV") == 0) {
245 m_ekin[ntable] *= 1.0e3;
246 } else if (strcmp(token, "MeV") != 0) {
247 std::cerr << hdr << "Unknown energy unit " << token << "; aborting\n";
248 return false;
249 }
250 // EM loss
251 token = strtok(NULL, " ");
252 m_emloss.push_back(atof(token));
253 // HD loss
254 token = strtok(NULL, " ");
255 m_hdloss.push_back(atof(token));
256 // Projected range
257 token = strtok(NULL, " ");
258 m_range.push_back(atof(token));
259 token = strtok(NULL, " ");
260 if (strcmp(token, "A") == 0) {
261 m_range[ntable] *= 1.0e-8;
262 } else if (strcmp(token, "um") == 0) {
263 m_range[ntable] *= 1.0e-4;
264 } else if (strcmp(token, "mm") == 0) {
265 m_range[ntable] *= 1.0e-1;
266 } else if (strcmp(token, "m") == 0) {
267 m_range[ntable] *= 1.0e2;
268 } else if (strcmp(token, "km") == 0) {
269 m_range[ntable] *= 1.0e5;
270 } else if (strcmp(token, "cm") != 0) {
271 std::cerr << hdr << "Unknown distance unit " << token << "; aborting\n";
272 return false;
273 }
274 // Longitudinal straggling
275 token = strtok(NULL, " ");
276 m_longstraggle.push_back(atof(token));
277 token = strtok(NULL, " ");
278 if (strcmp(token, "A") == 0) {
279 m_longstraggle[ntable] *= 1.0e-8;
280 } else if (strcmp(token, "um") == 0) {
281 m_longstraggle[ntable] *= 1.0e-4;
282 } else if (strcmp(token, "mm") == 0) {
283 m_longstraggle[ntable] *= 1.0e-1;
284 } else if (strcmp(token, "m") == 0) {
285 m_longstraggle[ntable] *= 1.0e2;
286 } else if (strcmp(token, "km") == 0) {
287 m_longstraggle[ntable] *= 1.0e5;
288 } else if (strcmp(token, "cm") != 0) {
289 std::cerr << hdr << "Unknown distance unit " << token << "; aborting\n";
290 return false;
291 }
292 // Transverse straggling
293 token = strtok(NULL, " ");
294 m_transstraggle.push_back(atof(token));
295 token = strtok(NULL, " ");
296 if (strcmp(token, "A") == 0) {
297 m_transstraggle[ntable] *= 1.0e-8;
298 } else if (strcmp(token, "um") == 0) {
299 m_transstraggle[ntable] *= 1.0e-4;
300 } else if (strcmp(token, "mm") == 0) {
301 m_transstraggle[ntable] *= 1.0e-1;
302 } else if (strcmp(token, "m") == 0) {
303 m_transstraggle[ntable] *= 1.0e2;
304 } else if (strcmp(token, "km") == 0) {
305 m_transstraggle[ntable] *= 1.0e5;
306 } else if (strcmp(token, "cm") != 0) {
307 std::cerr << hdr << "Unknown distance unit " << token << "; aborting\n";
308 return false;
309 }
310
311 // Increment table line counter
312 ++ntable;
313 }
314
315 // Find the scaling factor and convert to MeV/cm
316 double scale = -1.;
317 while (fsrim.getline(line, 100, '\n')) {
318 nread++;
319 if (strstr(line, "=============") != NULL) {
320 break;
321 } else if (strstr(line, "MeV / (mg/cm2)") != NULL ||
322 strstr(line, "MeV/(mg/cm2)") != NULL) {
323 token = strtok(line, " ");
324 scale = std::atof(token);
325 }
326 }
327 if (scale < 0) {
328 std::cerr << hdr << "Did not find stopping unit scaling; aborting.\n";
329 return false;
330 }
331 scale *= 1.e3;
332 for (unsigned int i = 0; i < ntable; ++i) {
333 m_emloss[i] *= scale;
334 m_hdloss[i] *= scale;
335 }
336
337 // Seems to have worked
338 if (m_debug) {
339 std::cout << hdr << "Successfully read " << file << "(" << nread
340 << " lines).\n";
341 }
342 return true;
343}
void SetDensity(const double density)
Set/get the density [g/cm3] of the target medium.
Definition: TrackSrim.hh:26

◆ RndmEnergyLoss()

double Garfield::TrackSrim::RndmEnergyLoss ( const double  ekin,
const double  de,
const double  step 
) const
protected

Definition at line 1226 of file TrackSrim.cc.

1227 {
1228 // RNDDE - Generates a random energy loss.
1229 // VARIABLES : EKIN : Kinetic energy [MeV]
1230 // DE : Mean energy loss over the step [MeV]
1231 // STEP : Step length [cm]
1232 // BETA2 : Velocity-squared
1233 // GAMMA : Projectile gamma
1234 // EMAX : Maximum energy transfer per collision [MeV]
1235 // XI : Rutherford term [MeV]
1236 // FCONST : Proportionality constant
1237 // EMASS : Electron mass [MeV]
1238 // (Last changed on 26/10/07.)
1239
1240 const std::string hdr = "TrackSrim::RndmEnergyLoss: ";
1241 // Check correctness.
1242 if (ekin <= 0 || de <= 0 || step <= 0) {
1243 std::cerr << hdr << "\n Input parameters not valid.\n Ekin = "
1244 << ekin << " MeV, dE = " << de << " MeV, step length = "
1245 << step << " cm.\n";
1246 return 0.;
1247 } else if (m_mass <= 0 || fabs(m_q) <= 0) {
1248 std::cerr << hdr << "\n Track parameters not valid.\n Mass = "
1249 << m_mass << " MeV, charge = " << m_q << ".\n";
1250 return 0.;
1251 } else if (m_a <= 0 || m_z <= 0 || m_density <= 0) {
1252 std::cerr << hdr << "\n Material parameters not valid.\n A = " << m_a
1253 << ", Z = " << m_z << ", density = " << m_density << " g/cm3.\n";
1254 return 0.;
1255 }
1256 // Basic kinematic parameters
1257 const double rkin = 1.e6 * ekin / m_mass;
1258 const double gamma = 1. + rkin;
1259 const double beta2 = rkin > 1.e-5 ? 1. - 1. / (gamma * gamma) : 2. * rkin;
1260
1261 // Compute maximum energy transfer
1262 const double rm = ElectronMass / m_mass;
1263 const double emax = 2 * ElectronMass * 1.e-6 * beta2 * gamma * gamma /
1264 (1. + 2 * gamma * rm + rm * rm);
1265 // Compute the Rutherford term
1266 const double fconst = 0.1534;
1267 const double xi = fconst * m_q * m_q * m_z * m_density * step / (m_a * beta2);
1268 // Compute the scaling parameter
1269 const double rkappa = xi / emax;
1270 // Debugging output.
1271 if (m_debug) {
1272 PrintSettings(hdr, de, step, ekin, beta2, gamma, m_a, m_z, m_density, m_q,
1273 m_mass, emax, xi, rkappa);
1274 }
1275 double rndde = de;
1276 if (m_model <= 0 || m_model > 4) {
1277 // No fluctuations.
1278 if (m_debug) std::cout << hdr << "Fixed energy loss.\n";
1279 } else if (m_model == 1) {
1280 // Landau distribution
1281 if (m_debug) std::cout << hdr << "Landau imposed.\n";
1282 const double xlmean = -(log(rkappa) + beta2 + 1. - Gamma);
1283 rndde += xi * (RndmLandau() - xlmean);
1284 } else if (m_model == 2) {
1285 // Vavilov distribution, ensure we are in range.
1286 if (m_debug) std::cout << hdr << "Vavilov imposed.\n";
1287 if (rkappa > 0.01 && rkappa < 12) {
1288 const double xvav = RndmVavilov(rkappa, beta2);
1289 rndde += xi * (xvav + log(rkappa) + beta2 + (1 - Gamma));
1290 }
1291 } else if (m_model == 3) {
1292 // Gaussian model
1293 if (m_debug) std::cout << hdr << "Gaussian imposed.\n";
1294 rndde += RndmGaussian(0., sqrt(xi * emax * (1 - 0.5 * beta2)));
1295 } else if (rkappa < 0.05) {
1296 // Combined model: for low kappa, use the landau distribution.
1297 if (m_debug) std::cout << hdr << "Landau automatic.\n";
1298 const double xlmean = -(log(rkappa) + beta2 + (1 - Gamma));
1299 const double par[] = {0.50884, 1.26116, 0.0346688, 1.46314,
1300 0.15088e-2, 1.00324, -0.13049e-3};
1301 const double xlmax = par[0] + par[1] * xlmean + par[2] * xlmean * xlmean +
1302 par[6] * xlmean * xlmean * xlmean +
1303 (par[3] + xlmean * par[4]) * exp(par[5] * xlmean);
1304 double xlan = RndmLandau();
1305 for (unsigned int iter = 0; iter < 100; ++iter) {
1306 if (xlan < xlmax) break;
1307 xlan = RndmLandau();
1308 }
1309 rndde += xi * (xlan - xlmean);
1310 } else if (rkappa < 5) {
1311 // For medium kappa, use the Vavilov distribution, precise
1312 // } else if (m_precisevavilov && rkappa < 5) {
1313 // printf("Vavilov slow automatic\n");
1314 // rndde = de+xi*(rndvvl(rkappa,beta2) + log(xi/emax)+beta2+(1-Gamma));
1315 // // ... or fast.
1316 if (m_debug) std::cout << hdr << "Vavilov fast automatic.\n";
1317 const double xvav = RndmVavilov(rkappa, beta2);
1318 rndde += xi * (xvav + log(rkappa) + beta2 + (1 - Gamma));
1319 } else {
1320 // And for large kappa, use the Gaussian values.
1321 if (m_debug) std::cout << hdr << "Gaussian automatic.\n";
1322 rndde = RndmGaussian(de, sqrt(xi * emax * (1 - 0.5 * beta2)));
1323 }
1324 // Debugging output
1325 if (m_debug)
1326 std::cout << hdr << "Energy loss generated = " << rndde << " MeV.\n";
1327 return rndde;
1328}
double RndmVavilov(const double rkappa, const double beta2)
Draw a random number from a Vavilov distribution.
Definition: Random.cc:283
double RndmLandau()
Draw a random number from a Landau distribution.
Definition: Random.cc:87
DoubleAc exp(const DoubleAc &f)
Definition: DoubleAc.cpp:377

Referenced by NewTrack().

◆ SetAtomicMassNumbers()

void Garfield::TrackSrim::SetAtomicMassNumbers ( const double  a,
const double  z 
)
inline

Set/get A and Z of the target medium.

Definition at line 29 of file TrackSrim.hh.

29 {
30 m_a = a;
31 m_z = z;
32 }

◆ SetClustersMaximum() [1/2]

int Garfield::TrackSrim::SetClustersMaximum ( ) const
inline

Definition at line 55 of file TrackSrim.hh.

55{ return m_maxclusters; }

◆ SetClustersMaximum() [2/2]

void Garfield::TrackSrim::SetClustersMaximum ( const int  n)
inline

Definition at line 54 of file TrackSrim.hh.

54{ m_maxclusters = n; }

◆ SetDensity()

void Garfield::TrackSrim::SetDensity ( const double  density)
inline

Set/get the density [g/cm3] of the target medium.

Definition at line 26 of file TrackSrim.hh.

26{ m_density = density; }

Referenced by ReadFile().

◆ SetFanoFactor()

void Garfield::TrackSrim::SetFanoFactor ( const double  f)
inline

Set/get the Fano factor.

Definition at line 23 of file TrackSrim.hh.

23{ m_fano = f; }

◆ SetModel()

void Garfield::TrackSrim::SetModel ( const int  m)
inline

Set/get the fluctuation model (0 = none, 1 = Landau, 2 = Vavilov, 3 = Gaussian, 4 = Combined)

Definition at line 40 of file TrackSrim.hh.

40{ m_model = m; }

◆ SetTargetClusterSize()

void Garfield::TrackSrim::SetTargetClusterSize ( const int  n)
inline

Definition at line 51 of file TrackSrim.hh.

51{ m_nsize = n; }

◆ SetWorkFunction()

void Garfield::TrackSrim::SetWorkFunction ( const double  w)
inline

Set/get the W value [eV].

Definition at line 20 of file TrackSrim.hh.

20{ m_work = w; }

◆ SmallestStep()

bool Garfield::TrackSrim::SmallestStep ( double  ekin,
double  de,
double  step,
double &  stpmin 
)
protected

Definition at line 1052 of file TrackSrim.cc.

1053 {
1054 // Determines the smallest step size for which there is little
1055 // or no risk of finding negative energy fluctuations.
1056 // SRMMST
1057
1058 const std::string hdr = m_className + "::SmallestStep: ";
1059 const double expmax = 30;
1060
1061 // By default, assume the step is right.
1062 stpmin = step;
1063 // Check correctness.
1064 if (ekin <= 0 || de <= 0 || step <= 0) {
1065 std::cerr << hdr << "\n Input parameters not valid.\n Ekin = "
1066 << ekin << " MeV, dE = " << de << " MeV, step length = "
1067 << step << " cm.\n";
1068 return false;
1069 } else if (m_mass <= 0 || fabs(m_q) <= 0) {
1070 std::cerr << hdr << "\n Track parameters not valid.\n Mass = "
1071 << m_mass << " eV, charge = " << m_q << ".\n";
1072 return false;
1073 } else if (m_a <= 0 || m_z <= 0 || m_density <= 0) {
1074 std::cerr << hdr << "\n Gas parameters not valid.\n A = " << m_a
1075 << ", Z = " << m_z << " density = " << m_density << " g/cm3.\n";
1076 return false;
1077 }
1078
1079 // Basic kinematic parameters
1080 const double rkin = 1.e6 * ekin / m_mass;
1081 const double gamma = 1. + rkin;
1082 const double beta2 = rkin > 1.e-5 ? 1. - 1. / (gamma * gamma) : 2. * rkin;
1083
1084 // Compute maximum energy transfer [MeV]
1085 const double rm = ElectronMass / m_mass;
1086 const double emax = 2 * ElectronMass * 1.e-6 * beta2 * gamma * gamma /
1087 (1. + 2 * gamma * rm + rm * rm);
1088 // Compute the Rutherford term
1089 const double fconst = 0.1534;
1090 double xi = fconst * m_q * m_q * m_z * m_density * step / (m_a * beta2);
1091 // Compute the scaling parameter
1092 double rkappa = xi / emax;
1093 // Step size and energy loss
1094 double denow = de;
1095 double stpnow = step;
1096 const unsigned int nMaxIter = 10;
1097 for (unsigned int iter = 0; iter < nMaxIter; ++iter) {
1098 bool retry = false;
1099 // Debugging output.
1100 if (m_debug) {
1101 PrintSettings(hdr, denow, stpnow, ekin, beta2, gamma, m_a, m_z, m_density,
1102 m_q, m_mass, emax, xi, rkappa);
1103 }
1104 double xinew = xi;
1105 double rknew = rkappa;
1106 if (m_model <= 0 || m_model > 4) {
1107 // No fluctuations: any step is permitted
1108 stpmin = stpnow;
1109 } else if (m_model == 1) {
1110 // Landau distribution
1111 const double xlmin = -3.;
1112 const double exponent = -xlmin - 1. + Gamma - beta2 - denow / xi;
1113 const double rklim = exponent < -expmax ? 0. : exp(exponent);
1114 stpmin = stpnow * (rklim / rkappa);
1115 if (m_debug) {
1116 std::cout << hdr << "Landau distribution is imposed.\n kappa_min = "
1117 << rklim << ", d_min = " << stpmin << " cm.\n";
1118 }
1119 } else if (m_model == 2) {
1120 // Vavilov distribution, ensure we're in range.
1121 const double xlmin = StepVavilov(rkappa);
1122 const double exponent = -xlmin - 1. + Gamma - beta2 - denow / xi;
1123 const double rklim = exponent < -expmax ? 0. : exp(exponent);
1124 stpmin = stpnow * (rklim / rkappa);
1125 xinew = fconst * m_q * m_q * m_z * m_density * stpmin / (m_a * beta2);
1126 rknew = xinew / emax;
1127 if (m_debug) {
1128 std::cout << hdr << "Vavilov distribution is imposed.\n kappa_min = "
1129 << rklim << ", d_min = " << stpmin << " cm\n kappa_new = "
1130 << rknew << ", xi_new = " << xinew << " MeV.\n";
1131 }
1132 if (stpmin > stpnow * 1.1) {
1133 if (m_debug) std::cout << hdr << "Step size increase. New pass.\n";
1134 retry = true;
1135 }
1136 } else if (m_model == 3) {
1137 // Gaussian model
1138 stpmin = stpnow * 16 * xi * emax * (1 - 0.5 * beta2) / (denow * denow);
1139 if (m_debug) {
1140 std::cout << hdr << "Gaussian distribution is imposed.\n";
1141 printf("\td_min = %g cm.\n\tsigma/mu_old = %g, sigma/mu_min = %g\n",
1142 stpmin, sqrt(xi * emax * (1 - 0.5 * beta2)) / de,
1143 sqrt((fconst * m_q * m_q * m_z * m_density * stpmin /
1144 (m_a * beta2)) *
1145 emax * (1 - 0.5 * beta2)) /
1146 (stpmin * denow / stpnow));
1147 }
1148 } else if (rkappa < 0.05) {
1149 // Combined model: for low kappa, use the Landau distribution.
1150 const double xlmin = -3.;
1151 const double exponent = -xlmin - 1. + Gamma - beta2 - denow / xi;
1152 const double rklim = exponent < -expmax ? 0. : exp(exponent);
1153 stpmin = stpnow * (rklim / rkappa);
1154 xinew = fconst * m_q * m_q * m_z * m_density * stpmin / (m_a * beta2);
1155 rknew = xinew / emax;
1156 if (m_debug) {
1157 std::cout << hdr << "Landau distribution automatic.\n kappa_min = "
1158 << rklim << ", d_min = " << stpmin << " cm.\n";
1159 }
1160 if (rknew > 0.05 || stpmin > stpnow * 1.1) {
1161 retry = true;
1162 if (m_debug) {
1163 std::cout << hdr << "Model change or step increase. New pass.\n";
1164 }
1165 }
1166 } else if (rkappa < 5) {
1167 // For medium kappa, use the Vavilov distribution
1168 const double xlmin = StepVavilov(rkappa);
1169 const double exponent = -xlmin - 1. + Gamma - beta2 - denow / xi;
1170 const double rklim = exponent < -expmax ? 0. : exp(exponent);
1171 stpmin = stpnow * (rklim / rkappa);
1172 xinew = fconst * m_q * m_q * m_z * m_density * stpmin / (m_a * beta2);
1173 rknew = xinew / emax;
1174 if (m_debug) {
1175 std::cout << hdr << "Vavilov distribution automatic.\n kappa_min = "
1176 << rklim << ", d_min = " << stpmin << " cm\n kappa_new = "
1177 << rknew << ", xi_new = " << xinew << " MeV.\n";
1178 }
1179 if (rknew > 5 || stpmin > stpnow * 1.1) {
1180 retry = true;
1181 if (m_debug) {
1182 std::cout << hdr << "Model change or step increase. New pass.\n";
1183 }
1184 }
1185 } else {
1186 // And for large kappa, use the Gaussian values.
1187 stpmin = stpnow * 16 * xi * emax * (1 - 0.5 * beta2) / (denow * denow);
1188 if (m_debug) {
1189 std::cout << hdr << "Gaussian distribution automatic.\n";
1190 printf("\td_min = %g cm.\n\tsigma/mu_old = %g, sigma/mu_min = %g\n",
1191 stpmin, sqrt(xi * emax * (1 - 0.5 * beta2)) / de,
1192 sqrt((fconst * m_q * m_q * m_z * m_density * stpmin /
1193 (m_a * beta2)) *
1194 emax * (1 - 0.5 * beta2)) /
1195 (stpmin * denow / stpnow));
1196 }
1197 }
1198 // See whether we should do another pass.
1199 if (stpnow > stpmin) {
1200 if (m_debug) {
1201 std::cout << hdr << "Step size ok, minimum: " << stpmin << " cm\n";
1202 }
1203 break;
1204 }
1205 if (!retry) {
1206 if (m_debug) {
1207 std::cerr << hdr << "\nStep size must be increased to "
1208 << stpmin << "cm.\n";
1209 }
1210 break;
1211 }
1212 // New iteration
1213 rkappa = rknew;
1214 xi = xinew;
1215 denow *= stpmin / stpnow;
1216 stpnow = stpmin;
1217 if (m_debug) std::cout << hdr << "Iteration " << iter << "\n";
1218 if (iter == nMaxIter - 1) {
1219 // Need interation, but ran out of tries
1220 std::cerr << hdr << "\n No convergence reached on step size.\n";
1221 }
1222 }
1223 return true;
1224}

Referenced by NewTrack().

Member Data Documentation

◆ m_a

double Garfield::TrackSrim::m_a
protected

A and Z of the gas.

Definition at line 90 of file TrackSrim.hh.

Referenced by GetAtomicMassMumbers(), NewTrack(), Print(), RndmEnergyLoss(), SetAtomicMassNumbers(), and SmallestStep().

◆ m_chargeset

bool Garfield::TrackSrim::m_chargeset
protected

Charge gas been defined.

Definition at line 78 of file TrackSrim.hh.

Referenced by NewTrack(), and ReadFile().

◆ m_clusters

std::vector<cluster> Garfield::TrackSrim::m_clusters
protected

Definition at line 121 of file TrackSrim.hh.

Referenced by GetCluster(), and NewTrack().

◆ m_currcluster

unsigned int Garfield::TrackSrim::m_currcluster
protected

Index of the next cluster to be returned.

Definition at line 109 of file TrackSrim.hh.

Referenced by GetCluster(), and NewTrack().

◆ m_density

double Garfield::TrackSrim::m_density
protected

Density [g/cm3].

Definition at line 80 of file TrackSrim.hh.

Referenced by GetDensity(), NewTrack(), PlotEnergyLoss(), PreciseLoss(), Print(), RndmEnergyLoss(), SetDensity(), and SmallestStep().

◆ m_ekin

std::vector<double> Garfield::TrackSrim::m_ekin
protected

Energy in energy loss table [MeV].

Definition at line 96 of file TrackSrim.hh.

Referenced by DedxEM(), DedxHD(), NewTrack(), PlotEnergyLoss(), PlotRange(), PlotStraggling(), Print(), and ReadFile().

◆ m_emloss

std::vector<double> Garfield::TrackSrim::m_emloss
protected

EM energy loss [MeV cm2/g].

Definition at line 98 of file TrackSrim.hh.

Referenced by DedxEM(), PlotEnergyLoss(), Print(), and ReadFile().

◆ m_fano

double Garfield::TrackSrim::m_fano
protected

Fano factor [-].

Definition at line 84 of file TrackSrim.hh.

Referenced by GetFanoFactor(), NewTrack(), Print(), and SetFanoFactor().

◆ m_hdloss

std::vector<double> Garfield::TrackSrim::m_hdloss
protected

Hadronic energy loss [MeV cm2/g].

Definition at line 100 of file TrackSrim.hh.

Referenced by DedxHD(), PlotEnergyLoss(), Print(), and ReadFile().

◆ m_longstraggle

std::vector<double> Garfield::TrackSrim::m_longstraggle
protected

Longitudinal straggling [cm].

Definition at line 106 of file TrackSrim.hh.

Referenced by NewTrack(), PlotStraggling(), Print(), and ReadFile().

◆ m_mass

double Garfield::TrackSrim::m_mass
protected

Mass of ion [MeV].

Definition at line 88 of file TrackSrim.hh.

Referenced by NewTrack(), Print(), ReadFile(), RndmEnergyLoss(), SmallestStep(), and TrackSrim().

◆ m_maxclusters

int Garfield::TrackSrim::m_maxclusters
protected

Maximum number of clusters allowed (infinite if 0)

Definition at line 94 of file TrackSrim.hh.

Referenced by NewTrack(), and SetClustersMaximum().

◆ m_model

unsigned int Garfield::TrackSrim::m_model
protected

Fluctuation model (0 = none, 1 = Landau, 2 = Vavilov, 3 = Gaussian, 4 = Combined)

Definition at line 112 of file TrackSrim.hh.

Referenced by GetModel(), RndmEnergyLoss(), SetModel(), and SmallestStep().

◆ m_nsize

int Garfield::TrackSrim::m_nsize
protected

Targeted cluster size.

Definition at line 114 of file TrackSrim.hh.

Referenced by GetTargetClusterSize(), NewTrack(), and SetTargetClusterSize().

◆ m_precisevavilov

bool Garfield::TrackSrim::m_precisevavilov
protected

Use precise Vavilov generator.

Definition at line 71 of file TrackSrim.hh.

Referenced by DisablePreciseVavilov(), and EnablePreciseVavilov().

◆ m_q

double Garfield::TrackSrim::m_q
protected

Charge of ion.

Definition at line 86 of file TrackSrim.hh.

Referenced by NewTrack(), Print(), ReadFile(), RndmEnergyLoss(), and SmallestStep().

◆ m_range

std::vector<double> Garfield::TrackSrim::m_range
protected

Projected range [cm].

Definition at line 102 of file TrackSrim.hh.

Referenced by NewTrack(), PlotRange(), Print(), and ReadFile().

◆ m_transstraggle

std::vector<double> Garfield::TrackSrim::m_transstraggle
protected

Transverse straggling [cm].

Definition at line 104 of file TrackSrim.hh.

Referenced by NewTrack(), PlotStraggling(), Print(), and ReadFile().

◆ m_useLongStraggle

bool Garfield::TrackSrim::m_useLongStraggle
protected

Include longitudinal straggling.

Definition at line 75 of file TrackSrim.hh.

Referenced by DisableLongitudinalStraggling(), EnableLongitudinalStraggling(), and NewTrack().

◆ m_useTransStraggle

bool Garfield::TrackSrim::m_useTransStraggle
protected

Include transverse straggling.

Definition at line 73 of file TrackSrim.hh.

Referenced by DisableTransverseStraggling(), EnableTransverseStraggling(), and NewTrack().

◆ m_work

double Garfield::TrackSrim::m_work
protected

Work function [eV].

Definition at line 82 of file TrackSrim.hh.

Referenced by GetWorkFunction(), NewTrack(), Print(), and SetWorkFunction().

◆ m_z

double Garfield::TrackSrim::m_z
protected

The documentation for this class was generated from the following files: