16void Rotate(
const std::array<double, 3>& f,
17 const std::array<double, 3>& t,
18 std::array<double, 3>& a) {
25 std::array<double, 3> v = {f[1] * t[2] - f[2] * t[1],
26 f[2] * t[0] - f[0] * t[2],
27 f[0] * t[1] - f[1] * t[0]};
28 const auto v2 = std::inner_product(v.cbegin(), v.cend(), v.cbegin(), 0.);
29 if (v2 < Garfield::Small)
return;
30 const double c = std::inner_product(f.cbegin(), f.cend(), t.cbegin(), 0.);
31 const double h = (1. - c) / v2;
32 std::array<std::array<double, 3>, 3> r;
35 r[0][0] = h * v[0] * v[0] + c;
36 r[0][1] = h * v[0] * v[1] - v[2];
37 r[0][2] = h * v[0] * v[2] + v[1];
38 r[1][0] = h * v[0] * v[1] + v[2];
39 r[1][1] = h * v[1] * v[1] + c;
40 r[1][2] = h * v[1] * v[2] - v[0];
41 r[2][0] = h * v[0] * v[2] - v[1];
42 r[2][1] = h * v[1] * v[2] + v[0];
43 r[2][2] = h * v[2] * v[2] + c;
46 std::array<double, 3>
x = {0, 0, 0};
54 const std::array<double, 3> u = {
x[0] - f[0],
x[1] - f[1],
x[2] - f[2]};
55 const std::array<double, 3> g = {
x[0] - t[0],
x[1] - t[1],
x[2] - t[2]};
56 const double u2 = std::inner_product(u.cbegin(), u.cend(), u.cbegin(), 0.);
57 const double g2 = std::inner_product(g.cbegin(), g.cend(), g.cbegin(), 0.);
58 const double ug = std::inner_product(u.cbegin(), u.cend(), g.cbegin(), 0.);
59 const double c0 = -2. / u2;
60 const double c1 = -2. / g2;
61 const double c2 = 4. * ug / (u2 * g2);
62 for (
size_t i = 0; i < 3; ++i) {
63 for (
size_t j = 0; j < 3; ++j) {
64 r[i][j] = c0 * u[i] * u[j] + c1 * g[i] * g[j] + c2 * g[i] * u[j];
65 if (i == j) r[i][j] += 1.;
70 std::array<double, 3> b = {0., 0., 0.};
71 for (
size_t i = 0; i < 3; ++i) {
72 for (
size_t j = 0; j < 3; ++j) {
73 b[i] += r[i][j] * a[j];
79double Speed(
const double ekin,
const double mass) {
80 const double rk = ekin / mass;
81 const double gamma = 1. + rk;
82 const double beta2 = rk > 1.e-5 ? 1. - 1. / (gamma * gamma) : 2. * rk;
83 return sqrt(beta2) * Garfield::SpeedOfLight;
96 std::cerr <<
m_className <<
"::SetParticle: Not applicable.\n";
100 const unsigned int nIons,
const unsigned int nSkip) {
111 std::ifstream infile(filename);
114 <<
" Unable to open the EXYZ file (" << filename <<
").\n";
118 constexpr double Angstrom = 1.e-8;
119 unsigned int nRead = 0;
120 unsigned int ionNumber = 0;
123 std::vector<float> x;
124 std::vector<float> y;
125 std::vector<float> z;
126 std::vector<float> dedx;
127 std::vector<float> ekin;
128 for (std::string line; std::getline(infile, line);) {
129 if (line.find(
"------- ") != std::string::npos) {
134 if (line.find(
"Ion Data: ") != std::string::npos) {
136 std::getline(infile, line);
138 if (words.size() >= 3) {
140 mass = std::stod(words[1]);
141 auto pos = words[2].find(
"keV");
142 if (pos != std::string::npos) {
143 m_ekin = 1.e3 * std::stod(words[2].substr(0, pos));
151 if (words.size() < 6) {
152 std::cerr <<
m_className <<
"::ReadFile: Unexpected line:\n"
156 if (ionNumber != std::stoul(words[0])) {
159 if (nRead >= nSkip)
AddIon(x, y, z, dedx, ekin);
167 if (nIons > 0 &&
m_ions.size() >= nIons)
break;
169 ionNumber = std::stoi(words[0]);
171 if (nRead < nSkip)
continue;
173 x.push_back(std::stof(words[2]) * Angstrom);
174 y.push_back(std::stof(words[3]) * Angstrom);
175 z.push_back(std::stof(words[4]) * Angstrom);
177 dedx.push_back(std::stof(words[5]) / Angstrom);
179 ekin.push_back(std::stof(words[1]) * 1.e3);
182 AddIon(x, y, z, dedx, ekin);
183 std::cout <<
m_className <<
"::ReadFile: Read energy vs position for "
184 <<
m_ions.size() <<
" ions.\n";
185 if (
m_ekin > 0. && mass > 0.) {
186 std::cout <<
" Initial kinetic energy set to "
187 <<
m_ekin * 1.e-3 <<
" keV. Mass number: " << mass <<
".\n";
188 m_mass = AtomicMassUnitElectronVolt * mass;
195 const std::vector<float>& y,
196 const std::vector<float>& z,
197 const std::vector<float>& dedx,
198 const std::vector<float>& ekin) {
200 const size_t nPoints = x.size();
201 if (nPoints < 2)
return;
202 std::vector<std::array<float, 6> > path;
203 for (
size_t i = 0; i < nPoints - 1; ++i) {
204 const float dx = x[i + 1] - x[i];
205 const float dy = y[i + 1] - y[i];
206 const float dz = z[i + 1] - z[i];
207 const float dmag = sqrt(dx * dx + dy * dy + dz * dz);
208 const float scale = dmag > 0. ? 1. / dmag : 1.;
210 if (i == 0 && dedx[i] > 10. * dedx[i + 1]) {
211 eloss = dmag * dedx[i + 1];
213 eloss = dmag * dedx[i];
215 const float dekin = ekin[i] - ekin[i + 1];
216 if (dekin > 0.) eloss = std::min(eloss, dekin);
217 path.push_back({dx * scale, dy * scale, dz * scale, dmag, eloss, ekin[i]});
219 m_ions.push_back(std::move(path));
225 std::cerr <<
" No TRIM data present.\n";
229 <<
m_ekin * 1.e-3 <<
" keV\n"
230 <<
" Number of tracks: " <<
m_ions.size() <<
"\n";
232 std::cout <<
" Work function: " <<
m_work <<
" eV\n";
234 std::cout <<
" Work function: Automatic\n";
237 std::cout <<
" Fano factor: " <<
m_fano <<
"\n";
239 std::cout <<
" Fano factor: Automatic\n";
244 const double t0,
const double dx0,
const double dy0,
const double dz0) {
249 std::cerr <<
m_className <<
"::NewTrack: No TRIM data present.\n";
255 std::cout <<
m_className <<
"::NewTrack: Rewinding.\n";
261 std::cerr <<
m_className <<
"::NewTrack: Sensor is not defined.\n";
266 std::array<double, 3> v = {dx0, dy0, dz0};
267 const double d0 = sqrt(dx0 * dx0 + dy0 * dy0 + dz0 * dz0);
270 std::cout <<
m_className <<
"::NewTrack: Sampling initial direction.\n";
275 const double scale = 1. / d0;
276 for (
size_t i = 0; i < 3; ++i) v[i] *= scale;
281 std::cerr <<
m_className <<
"::NewTrack: No medium at initial point.\n";
288 <<
"Work function at initial point is not defined.\n";
291 fano = std::max(fano, 0.);
309 const size_t nPoints = path.size();
310 std::array<double, 3> x = {x0, y0, z0};
312 for (
size_t i = 0; i < nPoints; ++i) {
314 double ekin = path[i][5];
315 if (ekin > ekin0)
continue;
318 Rotate({1, 0, 0}, {path[i][0], path[i][1], path[i][2]}, v);
320 Rotate({path[i - 1][0], path[i - 1][1], path[i - 1][2]},
321 {path[i][0], path[i][1], path[i][2]}, v);
324 double dmag = path[i][3];
325 double eloss = path[i][4];
326 std::array<double, 3> d = {dmag * v[0], dmag * v[1], dmag * v[2]};
328 unsigned int nSteps = 1;
336 const double scale = 1. / nSteps;
337 for (
size_t j = 0; j < 3; ++j) d[j] *= scale;
342 const double vmag = Speed(ekin,
m_mass);
344 const double dt = vmag > 0. ? dmag / vmag : 0.;
345 for (
unsigned int j = 0; j < nSteps; ++j) {
348 double bx = 0., by = 0., bz = 0.;
350 m_sensor->MagneticField(x[0], x[1], x[2], bx, by, bz, status);
351 d =
StepBfield(dt, qoverm, vmag, bx, by, bz, v);
353 cluster.
x = x[0] + d[0];
354 cluster.
y = x[1] + d[1];
355 cluster.
z = x[2] + d[2];
357 x = {cluster.
x, cluster.
y, cluster.
z};
360 medium =
m_sensor->GetMedium(cluster.
x, cluster.
y, cluster.
z);
363 if (w < Small)
continue;
366 cluster.
n = int((eloss + epool) / w);
367 cluster.
energy = w * cluster.
n;
369 double ec = eloss + epool;
381 cluster.
ekin = i < nPoints - 1 ? path[i + 1][5] : ekin;
382 epool += eloss - cluster.
energy;
383 if (cluster.
n == 0)
continue;
394 double& tcls,
int& n,
double& e,
double& extra) {
410 extra = cluster.ekin;
Abstract base class for media.
double GetW() const
Get the W value.
bool IsIonisable() const
Is charge deposition by charged particles/photon enabled in this medium?
double GetFanoFactor() const
Get the Fano factor.
double m_ekin
Projectile energy [eV].
size_t m_cluster
Index of the next cluster to be returned.
void SetParticle(const std::string &part) 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)
TrackTrim()
Default constructor.
std::vector< Cluster > m_clusters
Clusters on the current track.
bool m_fset
Has the Fano factor been set?
double m_fano
Fano factor [-] of the target.
size_t m_ion
Index of the current track.
double m_work
Work function [eV] of the target.
bool GetCluster(double &xc, double &yc, double &zc, double &tc, int &nc, double &ec, double &extra) override
void Print()
Print a summary of the available TRIM data.
double m_maxLossPerStep
Energy loss limit per step.
bool ReadFile(const std::string &file, const unsigned int nIons=0, const unsigned int nSkip=0)
Load data from an EXYZ.txt file.
double m_maxStepSize
Step size limit.
bool NewTrack(const double x0, const double y0, const double z0, const double t0, const double dx0, const double dy0, const double dz0) override
std::vector< std::vector< std::array< float, 6 > > > m_ions
List of tracks.
double GetKineticEnergy() const
Return the kinetic energy of the projectile.
void SetKineticEnergy(const double ekin)
Set the kinetic energy of the particle.
static std::array< double, 3 > StepBfield(const double dt, const double qoverm, const double vmag, double bx, double by, double bz, std::array< double, 3 > &dir)
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)
double RndmHeedWF(const double w, const double f)
std::vector< std::string > tokenize(const std::string &line)
void RndmDirection(double &dx, double &dy, double &dz, const double length=1.)
Draw a random (isotropic) direction vector.
DoubleAc fabs(const DoubleAc &f)
DoubleAc sqrt(const DoubleAc &f)
double energy
Energy spent to make the cluster.
int n
Number of electrons in this cluster.
double ekin
Ion energy when cluster was created.