Garfield++ 4.0
A toolkit for the detailed simulation of particle detectors based on ionisation measurement in gases and semiconductors
Loading...
Searching...
No Matches
TrackTrim.cc
Go to the documentation of this file.
1#include <fstream>
2#include <iostream>
3#include <sstream>
4#include <algorithm>
5
8#include "Garfield/Random.hh"
9#include "Garfield/Sensor.hh"
10#include "Garfield/TrackTrim.hh"
11
12namespace {
13
14std::vector<std::string> tokenize(const std::string& line) {
15
16 std::vector<std::string> words;
17 std::istringstream ss(line);
18 for (std::string word; ss >> word;) {
19 words.push_back(word);
20 }
21 return words;
22}
23
24}
25
26namespace Garfield {
27
28TrackTrim::TrackTrim() : Track() { m_className = "TrackTrim"; }
29
30bool TrackTrim::ReadFile(const std::string& filename,
31 const unsigned int nIons, const unsigned int nSkip) {
32
33 // TRMREE - Reads the TRIM EXYZ file.
34
35 // Reset.
36 m_ions.clear();
37 m_ion = 0;
38 m_clusters.clear();
39 m_cluster = 0;
40
41 std::ifstream infile;
42 infile.open(filename.c_str(), std::ios::in);
43 if (infile.fail()) {
44 std::cerr << m_className << "::ReadFile:\n"
45 << " Unable to open the EXYZ file (" << filename << ").\n";
46 return false;
47 }
48
49 constexpr double Angstrom = 1.e-8;
50 unsigned int nRead = 0;
51 unsigned int ionNumber = 0;
52 bool header = true;
53 std::vector<float> x;
54 std::vector<float> y;
55 std::vector<float> z;
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) {
60 // Reached the end of the header.
61 header = false;
62 continue;
63 } else if (header) {
64 if (line.find("Ion Data: ") != std::string::npos) {
65 // Read the next line.
66 std::getline(infile, line);
67 auto words = tokenize(line);
68 if (words.size() >= 3) {
69 m_particleName = words[0];
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));
73 }
74 }
75 }
76 // Otherwise, skip the header.
77 continue;
78 }
79 auto words = tokenize(line);
80 if (words.size() < 6) {
81 std::cerr << m_className << "::ReadFile: Unexpected line:\n"
82 << line << "\n";
83 continue;
84 }
85 if (ionNumber != std::stoul(words[0])) {
86 // New ion.
87 if (ionNumber > 0) {
88 if (nRead >= nSkip) AddIon(x, y, z, dedx, ekin);
89 x.clear();
90 y.clear();
91 z.clear();
92 dedx.clear();
93 ekin.clear();
94 ++nRead;
95 // Stop if we are done reading the requested number of ions.
96 if (nIons > 0 && m_ions.size() >= nIons) break;
97 }
98 ionNumber = std::stoi(words[0]);
99 }
100 if (nRead < nSkip) continue;
101 // Convert coordinates to cm.
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);
105 // Convert stopping power from eV/A to eV/cm.
106 dedx.push_back(std::stof(words[5]) / Angstrom);
107 // Convert ion energy from keV to eV.
108 ekin.push_back(std::stof(words[1]) * 1.e3);
109 }
110 infile.close();
111 AddIon(x, y, z, dedx, ekin);
112 std::cout << m_className << "::ReadFile: Read energy vs position for "
113 << m_ions.size() << " ions.\n";
114 return true;
115}
116
117void TrackTrim::AddIon(const std::vector<float>& x,
118 const std::vector<float>& y,
119 const std::vector<float>& z,
120 const std::vector<float>& dedx,
121 const std::vector<float>& ekin) {
122
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) {
127 float eloss = 0.;
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];
135 } else {
136 eloss = step * dedx[i];
137 }
138 const float dekin = ekin[i] - ekin[i + 1];
139 if (dekin > 0.) eloss = std::min(eloss, dekin);
140 }
141 path.push_back({x[i], y[i], z[i], eloss, ekin[i]});
142 }
143 m_ions.push_back(path);
144}
145
147 std::cout << m_className << "::Print:\n";
148 if (m_ions.empty()) {
149 std::cerr << " No TRIM data present.\n";
150 return;
151 }
152 std::cout << " Projectile: " << m_particleName << ", "
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";
157}
158
159bool TrackTrim::NewTrack(const double x0, const double y0, const double z0,
160 const double t0, const double dx0, const double dy0, const double dz0) {
161
162 // TRMGEN - Generates TRIM clusters
163
164 if (m_ions.empty()) {
165 std::cerr << m_className << "::NewTrack: No TRIM data present.\n";
166 return false;
167 }
168
169 if (m_ion >= m_ions.size()) {
170 // Rewind.
171 std::cout << m_className << "::NewTrack: Rewinding.\n";
172 m_ion = 0;
173 }
174
175 // Verify that a sensor has been set.
176 if (!m_sensor) {
177 std::cerr << m_className << "::NewTrack: Sensor is not defined.\n";
178 return false;
179 }
180
181 // Normalise and store the direction.
182 const double d0 = sqrt(dx0 * dx0 + dy0 * dy0 + dz0 * dz0);
183 double dx = dx0;
184 double dy = dy0;
185 double dz = dz0;
186 if (d0 < Small) {
187 if (m_debug) {
188 std::cout << m_className << "::NewTrack: Randomizing initial direction.\n";
189 }
190 // Null vector. Sample the direction isotropically.
191 RndmDirection(dx, dy, dz);
192 } else {
193 // Normalise the direction vector.
194 dx /= d0;
195 dy /= d0;
196 dz /= d0;
197 }
198 const double dt = sqrt(dx * dx + dy * dy);
199 double phi = 0.;
200 double theta = 0.;
201 if (dt > 0.) {
202 phi = atan2(dy, dx);
203 theta = atan2(dz, dt);
204 } else {
205 theta = dz < 0. ? -HalfPi : HalfPi;
206 }
207 const double ctheta = cos(theta);
208 const double stheta = sin(theta);
209 const double cphi = cos(phi);
210 const double sphi = sin(phi);
211
212 // Make sure all necessary parameters have been set.
213 if (m_work < Small) {
214 std::cerr << m_className << "::NewTrack: Work function not set.\n";
215 return false;
216 }
217
218 // Plot.
219 if (m_viewer) PlotNewTrack(x0, y0, z0);
220
221 // Reset the cluster count.
222 m_cluster = 0;
223 m_clusters.clear();
224
225 // Pool of unused energy
226 double epool = 0.0;
227
228 const auto& path = m_ions[m_ion];
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];
234 Cluster cluster;
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;
238 // Is this point inside an ionisable medium?
239 Medium* medium = nullptr;
240 if (!m_sensor->GetMedium(cluster.x, cluster.y, cluster.z, medium)) {
241 continue;
242 }
243 if (!medium || !medium->IsIonisable()) continue;
244 cluster.t = t0;
245 double eloss = path[i - 1][3];
246 if (m_fano < Small) {
247 // No fluctuations.
248 cluster.electrons = int((eloss + epool) / m_work);
249 cluster.ec = m_work * cluster.electrons;
250 } else {
251 double ec = eloss + epool;
252 cluster.electrons = 0;
253 cluster.ec = 0.0;
254 while (true) {
255 const double er = RndmHeedWF(m_work, m_fano);
256 if (er > ec) break;
257 cluster.electrons++;
258 cluster.ec += er;
259 ec -= er;
260 }
261 }
262 cluster.ekin = path[i][4];
263 epool += eloss - cluster.ec;
264 m_clusters.push_back(std::move(cluster));
265 if (m_viewer) PlotCluster(cluster.x, cluster.y, cluster.z);
266 }
267 // Move to the next ion in the list.
268 ++m_ion;
269 return true;
270}
271
272bool TrackTrim::GetCluster(double& xcls, double& ycls, double& zcls,
273 double& tcls, int& n, double& e, double& extra) {
274 if (m_debug) {
275 std::cout << m_className << "::GetCluster: Cluster " << m_cluster
276 << " of " << m_clusters.size() << "\n";
277 }
278 // Stop if we have exhausted the list of clusters.
279 if (m_cluster >= m_clusters.size()) return false;
280
281 const auto& cluster = m_clusters[m_cluster];
282 xcls = cluster.x;
283 ycls = cluster.y;
284 zcls = cluster.z;
285 tcls = cluster.t;
286
287 n = cluster.electrons;
288 e = cluster.ec;
289 extra = cluster.ekin;
290 // Move to the next cluster.
291 ++m_cluster;
292 return true;
293}
294}
Abstract base class for media.
Definition: Medium.hh:13
bool IsIonisable() const
Is charge deposition by charged particles/photon enabled in this medium?
Definition: Medium.hh:78
bool GetMedium(const double x, const double y, const double z, Medium *&medium)
Get the medium at (x, y, z).
Definition: Sensor.cc:159
double m_ekin
Projectile energy [eV].
Definition: TrackTrim.hh:49
std::vector< std::vector< std::array< float, 5 > > > m_ions
List of tracks.
Definition: TrackTrim.hh:51
size_t m_cluster
Index of the next cluster to be returned.
Definition: TrackTrim.hh:64
bool GetCluster(double &xcls, double &ycls, double &zcls, double &tcls, int &n, double &e, double &extra) override
Definition: TrackTrim.cc:272
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)
Definition: TrackTrim.cc:117
TrackTrim()
Constructor.
Definition: TrackTrim.cc:28
std::vector< Cluster > m_clusters
Clusters on the current track.
Definition: TrackTrim.hh:62
double m_fano
Fano factor [-].
Definition: TrackTrim.hh:46
size_t m_ion
Index of the current track.
Definition: TrackTrim.hh:53
double m_work
Work function [eV].
Definition: TrackTrim.hh:44
void Print()
Print a summary of the available TRIM data.
Definition: TrackTrim.cc:146
bool ReadFile(const std::string &file, const unsigned int nIons=0, const unsigned int nSkip=0)
Load data from an EXYZ.txt file.
Definition: TrackTrim.cc:30
bool NewTrack(const double x0, const double y0, const double z0, const double t0, const double dx0, const double dy0, const double dz0) override
Definition: TrackTrim.cc:159
Abstract base class for track generation.
Definition: Track.hh:14
Sensor * m_sensor
Definition: Track.hh:112
bool m_debug
Definition: Track.hh:118
ViewDrift * m_viewer
Definition: Track.hh:116
std::string m_particleName
Definition: Track.hh:110
void PlotCluster(const double x0, const double y0, const double z0)
Definition: Track.cc:192
std::string m_className
Definition: Track.hh:102
void PlotNewTrack(const double x0, const double y0, const double z0)
Definition: Track.cc:187
double RndmHeedWF(const double w, const double f)
Definition: Random.cc:699
void RndmDirection(double &dx, double &dy, double &dz, const double length=1.)
Draw a random (isotropic) direction vector.
Definition: Random.hh:107
double t
Cluster location and time.
Definition: TrackTrim.hh:56
double ekin
Ion energy when cluster was created.
Definition: TrackTrim.hh:58
int electrons
Number of electrons in this cluster.
Definition: TrackTrim.hh:59
double ec
Energy spent to make the cluster.
Definition: TrackTrim.hh:57