14std::vector<std::string> tokenize(
const std::string& line) {
16 std::vector<std::string> words;
17 std::istringstream ss(line);
18 for (std::string word; ss >> word;) {
19 words.push_back(word);
31 const unsigned int nIons,
const unsigned int nSkip) {
42 infile.open(filename.c_str(), std::ios::in);
45 <<
" Unable to open the EXYZ file (" << filename <<
").\n";
49 constexpr double Angstrom = 1.e-8;
50 unsigned int nRead = 0;
51 unsigned int ionNumber = 0;
56 std::vector<float> dedx;
57 std::vector<float> ekin;
58 for (std::string line; std::getline(infile, line);) {
59 if (line.find(
"------- ") != std::string::npos) {
64 if (line.find(
"Ion Data: ") != std::string::npos) {
66 std::getline(infile, line);
67 auto words = tokenize(line);
68 if (words.size() >= 3) {
70 auto pos = words[2].find(
"keV");
71 if (pos != std::string::npos) {
72 m_ekin = 1.e3 * std::stod(words[2].substr(0, pos));
79 auto words = tokenize(line);
80 if (words.size() < 6) {
81 std::cerr <<
m_className <<
"::ReadFile: Unexpected line:\n"
85 if (ionNumber != std::stoul(words[0])) {
88 if (nRead >= nSkip)
AddIon(x, y, z, dedx, ekin);
96 if (nIons > 0 &&
m_ions.size() >= nIons)
break;
98 ionNumber = std::stoi(words[0]);
100 if (nRead < nSkip)
continue;
102 x.push_back(std::stof(words[2]) * Angstrom);
103 y.push_back(std::stof(words[3]) * Angstrom);
104 z.push_back(std::stof(words[4]) * Angstrom);
106 dedx.push_back(std::stof(words[5]) / Angstrom);
108 ekin.push_back(std::stof(words[1]) * 1.e3);
111 AddIon(x, y, z, dedx, ekin);
112 std::cout <<
m_className <<
"::ReadFile: Read energy vs position for "
113 <<
m_ions.size() <<
" ions.\n";
118 const std::vector<float>& y,
119 const std::vector<float>& z,
120 const std::vector<float>& dedx,
121 const std::vector<float>& ekin) {
123 const size_t nPoints = x.size();
124 if (nPoints < 2)
return;
125 std::vector<std::array<float, 5> > path;
126 for (
size_t i = 0; i < nPoints; ++i) {
128 if (i < nPoints - 1) {
129 const float dx = x[i + 1] - x[i];
130 const float dy = y[i + 1] - y[i];
131 const float dz = z[i + 1] - z[i];
132 const float step = sqrt(dx * dx + dy * dy + dz * dz);
133 if (i == 0 && dedx[i] > 10. * dedx[i + 1]) {
134 eloss = step * dedx[i + 1];
136 eloss = step * dedx[i];
138 const float dekin = ekin[i] - ekin[i + 1];
139 if (dekin > 0.) eloss = std::min(eloss, dekin);
141 path.push_back({x[i], y[i], z[i], eloss, ekin[i]});
149 std::cerr <<
" No TRIM data present.\n";
153 <<
m_ekin * 1.e-3 <<
" keV\n"
154 <<
" Number of tracks: " <<
m_ions.size() <<
"\n"
155 <<
" Work function: " <<
m_work <<
" eV\n"
156 <<
" Fano factor: " <<
m_fano <<
"\n";
160 const double t0,
const double dx0,
const double dy0,
const double dz0) {
165 std::cerr <<
m_className <<
"::NewTrack: No TRIM data present.\n";
171 std::cout <<
m_className <<
"::NewTrack: Rewinding.\n";
177 std::cerr <<
m_className <<
"::NewTrack: Sensor is not defined.\n";
182 const double d0 = sqrt(dx0 * dx0 + dy0 * dy0 + dz0 * dz0);
188 std::cout <<
m_className <<
"::NewTrack: Randomizing initial direction.\n";
198 const double dt = sqrt(dx * dx + dy * dy);
203 theta = atan2(dz, dt);
205 theta = dz < 0. ? -HalfPi : HalfPi;
207 const double ctheta = cos(theta);
208 const double stheta = sin(theta);
209 const double cphi = cos(phi);
210 const double sphi = sin(phi);
214 std::cerr <<
m_className <<
"::NewTrack: Work function not set.\n";
229 const size_t nPoints = path.size();
230 for (
size_t i = 1; i < nPoints; ++i) {
231 const double x = path[i][0];
232 const double y = path[i][1];
233 const double z = path[i][2];
235 cluster.
x = x0 + cphi * ctheta * x - sphi * y - cphi * stheta * z;
236 cluster.
y = y0 + sphi * ctheta * x + cphi * y - sphi * stheta * z;
237 cluster.
z = z0 + stheta * x + ctheta * z;
245 double eloss = path[i - 1][3];
251 double ec = eloss + epool;
262 cluster.
ekin = path[i][4];
263 epool += eloss - cluster.
ec;
273 double& tcls,
int& n,
double& e,
double& extra) {
287 n = cluster.electrons;
289 extra = cluster.ekin;
Abstract base class for media.
bool IsIonisable() const
Is charge deposition by charged particles/photon enabled in this medium?
bool GetMedium(const double x, const double y, const double z, Medium *&medium)
Get the medium at (x, y, z).
double m_ekin
Projectile energy [eV].
std::vector< std::vector< std::array< float, 5 > > > m_ions
List of tracks.
size_t m_cluster
Index of the next cluster to be returned.
bool GetCluster(double &xcls, double &ycls, double &zcls, double &tcls, int &n, double &e, double &extra) override
void AddIon(const std::vector< float > &x, const std::vector< float > &y, const std::vector< float > &z, const std::vector< float > &dedx, const std::vector< float > &ekin)
std::vector< Cluster > m_clusters
Clusters on the current track.
double m_fano
Fano factor [-].
size_t m_ion
Index of the current track.
double m_work
Work function [eV].
void Print()
Print a summary of the available TRIM data.
bool ReadFile(const std::string &file, const unsigned int nIons=0, const unsigned int nSkip=0)
Load data from an EXYZ.txt file.
bool NewTrack(const double x0, const double y0, const double z0, const double t0, const double dx0, const double dy0, const double dz0) override
Abstract base class for track generation.
std::string m_particleName
void PlotCluster(const double x0, const double y0, const double z0)
void PlotNewTrack(const double x0, const double y0, const double z0)
double RndmHeedWF(const double w, const double f)
void RndmDirection(double &dx, double &dy, double &dz, const double length=1.)
Draw a random (isotropic) direction vector.
double t
Cluster location and time.
double ekin
Ion energy when cluster was created.
int electrons
Number of electrons in this cluster.
double ec
Energy spent to make the cluster.