Garfield++ 3.0
A toolkit for the detailed simulation of particle detectors based on ionisation measurement in gases and semiconductors
Loading...
Searching...
No Matches
TrackBichsel.cc
Go to the documentation of this file.
1#include <algorithm>
2#include <array>
3#include <cstdlib>
4#include <fstream>
5#include <iostream>
6#include <sstream>
7
10#include "Garfield/Random.hh"
11#include "Garfield/Sensor.hh"
13#include "Garfield/Utilities.hh"
14
15namespace Garfield {
16
18 : Track(), m_speed(SpeedOfLight * m_bg / sqrt(1. + m_bg * m_bg)) {
19 m_className = "TrackBichsel";
20}
21
22bool TrackBichsel::NewTrack(const double x0, const double y0, const double z0,
23 const double t0, const double dx0, const double dy0,
24 const double dz0) {
25 // Make sure a sensor has been defined.
26 if (!m_sensor) {
27 std::cerr << m_className << "::NewTrack: Sensor is not defined.\n";
28 m_isInMedium = false;
29 return false;
30 }
31
32 // If not yet done, load the cross-section table from file.
33 if (!m_isInitialised) {
34 if (!LoadCrossSectionTable(m_datafile)) {
35 std::cerr << m_className << "::NewTrack:\n"
36 << " Cross-section table could not be loaded.\n";
37 return false;
38 }
39 m_isInitialised = true;
40 }
41
42 // Make sure we are inside a medium.
43 Medium* medium;
44 if (!m_sensor->GetMedium(x0, y0, z0, medium)) {
45 std::cerr << m_className << "::NewTrack:\n"
46 << " No medium at initial position.\n";
47 m_isInMedium = false;
48 return false;
49 }
50
51 // Check if the medium is silicon.
52 if (medium->GetName() != "Si") {
53 std::cerr << m_className << "::NewTrack:\n"
54 << " Medium at initial position is not silicon.\n";
55 m_isInMedium = false;
56 return false;
57 }
58
59 // Check if primary ionisation has been enabled.
60 if (!medium->IsIonisable()) {
61 std::cerr << m_className << "::NewTrack:\n"
62 << " Medium at initial position is not ionisable.\n";
63 m_isInMedium = false;
64 return false;
65 }
66
67 m_isInMedium = true;
68 m_x = x0;
69 m_y = y0;
70 m_z = z0;
71 m_t = t0;
72
73 // Normalise the direction vector.
74 const double d = sqrt(dx0 * dx0 + dy0 * dy0 + dz0 * dz0);
75 if (d < Small) {
76 // In case of a null vector, choose a random direction.
77 RndmDirection(m_dx, m_dy, m_dz);
78 } else {
79 m_dx = dx0 / d;
80 m_dy = dy0 / d;
81 m_dz = dz0 / d;
82 }
83
84 // If the particle properties have changed, update the cross-section table.
85 if (m_isChanged) {
86 m_bg = GetBetaGamma();
87 m_imfp = GetClusterDensity();
88 m_speed = SpeedOfLight * GetBeta();
89 SelectCrossSectionTable();
90 m_isChanged = false;
91 }
92
93 return true;
94}
95
96bool TrackBichsel::GetCluster(double& xcls, double& ycls, double& zcls,
97 double& tcls, int& n, double& e, double& extra) {
98 if (!m_isInitialised || !m_isInMedium) return false;
99
100 const double d = -log(RndmUniformPos()) / m_imfp;
101 m_x += m_dx * d;
102 m_y += m_dy * d;
103 m_z += m_dz * d;
104 m_t += d / m_speed;
105
106 xcls = m_x;
107 ycls = m_y;
108 zcls = m_z;
109 tcls = m_t;
110 n = 0;
111 e = 0.;
112 extra = 0.;
113
114 Medium* medium;
115 if (!m_sensor->GetMedium(m_x, m_y, m_z, medium)) {
116 m_isInMedium = false;
117 if (m_debug) {
118 std::cout << m_className << "::GetCluster: Particle left the medium.\n";
119 }
120 return false;
121 }
122
123 if (medium->GetName() != "Si" || !medium->IsIonisable()) {
124 m_isInMedium = false;
125 if (m_debug) {
126 std::cout << m_className << "::GetCluster: Particle left the medium.\n";
127 }
128 return false;
129 }
130
131 const double u = m_nCdfEntries * RndmUniform();
132 const int j = int(u);
133 if (j == 0) {
134 e = 0. + u * m_cdf[0][m_iCdf];
135 } else if (j >= m_nCdfEntries) {
136 e = m_cdf[m_nCdfEntries - 1][m_iCdf];
137 } else {
138 e = m_cdf[j - 1][m_iCdf] +
139 (u - j) * (m_cdf[j][m_iCdf] - m_cdf[j - 1][m_iCdf]);
140 }
141
142 return true;
143}
144
146 constexpr unsigned int nEntries = 38;
147 constexpr std::array<double, nEntries> tabBg = {
148 {0.316, 0.398, 0.501, 0.631, 0.794, 1.000, 1.259, 1.585,
149 1.995, 2.512, 3.162, 3.981, 5.012, 6.310, 7.943, 10.000,
150 12.589, 15.849, 19.953, 25.119, 31.623, 39.811, 50.119, 63.096,
151 79.433, 100.000, 125.893, 158.489, 199.526, 251.189, 316.228, 398.107,
152 501.187, 630.958, 794.329, 1000.000, 1258.926, 1584.894}};
153 constexpr std::array<double, nEntries> tabImfp = {
154 {30.32496, 21.14965, 15.06555, 11.05635, 8.43259, 6.72876, 5.63184,
155 4.93252, 4.49174, 4.21786, 4.05090, 3.95186, 3.89531, 3.86471,
156 3.84930, 3.84226, 3.83952, 3.83887, 3.83912, 3.83970, 3.84035,
157 3.84095, 3.84147, 3.84189, 3.84223, 3.84249, 3.84269, 3.84283,
158 3.84293, 3.84300, 3.84304, 3.84308, 3.84310, 3.84311, 3.84312,
159 3.84313, 3.84313, 3.84314}};
160
161 if (m_isChanged) m_bg = GetBetaGamma();
162
163 if (m_bg < tabBg.front()) {
164 if (m_debug) {
165 std::cerr << m_className << "::GetClusterDensity:\n"
166 << " Bg is below the tabulated range.\n";
167 }
168 return tabImfp.front() * 1.e4;
169 } else if (m_bg > tabBg.back()) {
170 return tabImfp.back() * 1.e4;
171 }
172
173 // Locate the requested energy in the table.
174 const auto it1 = std::upper_bound(tabBg.cbegin(), tabBg.cend(), m_bg);
175 if (it1 == tabBg.cbegin()) return 1.e4 * tabImfp.front();
176 const auto it0 = std::prev(it1);
177 const double x0 = *it0;
178 const double x1 = *it1;
179 const double y0 = tabImfp[it0 - tabBg.cbegin()];
180 const double y1 = tabImfp[it1 - tabBg.cbegin()];
181 const double tol = 1.e-6 * (x1 - x0);
182 if (fabs(m_bg - x0) < tol) return y0 * 1.e4;
183 if (fabs(m_bg - x1) < tol) return y1 * 1.e4;
184
185 // Log-log interpolation
186 const double lnx0 = log(x0);
187 const double lnx1 = log(x1);
188 const double lny0 = log(y0);
189 const double lny1 = log(y1);
190 const double d = lny0 + (log(m_bg) - lnx0) * (lny1 - lny0) / (lnx1 - lnx0);
191 return 1.e4 * exp(d);
192}
193
195 constexpr unsigned int nEntries = 51;
196 constexpr std::array<double, nEntries> tabBg = {
197 {0.316, 0.398, 0.501, 0.631, 0.794, 1.000,
198 1.259, 1.585, 1.995, 2.512, 3.162, 3.981,
199 5.012, 6.310, 7.943, 10.000, 12.589, 15.849,
200 19.953, 25.119, 31.623, 39.811, 50.119, 63.096,
201 79.433, 100.000, 125.893, 158.489, 199.526, 251.189,
202 316.228, 398.107, 501.187, 630.958, 794.329, 1000.000,
203 1258.926, 1584.894, 1995.263, 2511.888, 3162.280, 3981.074,
204 5011.875, 6309.578, 7943.287, 10000.010, 12589.260, 15848.940,
205 19952.640, 25118.880, 31622.800}};
206 constexpr std::array<double, nEntries> tabdEdx = {
207 {2443.71800, 1731.65600, 1250.93400, 928.69920, 716.37140, 578.28850,
208 490.83670, 437.33820, 406.58490, 390.95170, 385.29000, 386.12000,
209 391.07730, 398.53930, 407.39420, 416.90860, 426.63010, 436.30240,
210 445.78980, 455.02530, 463.97370, 472.61410, 480.92980, 488.90240,
211 496.51900, 503.77130, 510.65970, 517.19570, 523.39830, 529.29120,
212 534.90670, 540.27590, 545.42880, 550.39890, 555.20800, 559.88820,
213 564.45780, 568.93850, 573.34700, 577.69140, 581.99010, 586.25090,
214 590.47720, 594.68660, 598.86880, 603.03510, 607.18890, 611.33250,
215 615.46810, 619.59740, 623.72150}};
216
217 if (m_isChanged) m_bg = GetBetaGamma();
218
219 if (m_bg < tabBg.front()) {
220 if (m_debug) {
221 std::cerr << m_className << "::GetStoppingPower:\n"
222 << " Bg is below the tabulated range.\n";
223 }
224 return tabdEdx.front() * 1.e4;
225 } else if (m_bg > tabBg.back()) {
226 return tabdEdx.back() * 1.e4;
227 }
228
229 // Locate the requested energy in the table.
230 const auto it1 = std::upper_bound(tabBg.cbegin(), tabBg.cend(), m_bg);
231 if (it1 == tabBg.cbegin()) return 1.e4 * tabdEdx.front();
232 const auto it0 = std::prev(it1);
233 const double x0 = *it0;
234 const double x1 = *it1;
235 if (m_debug) {
236 std::cout << m_className << "::GetStoppingPower:\n"
237 << " Bg = " << m_bg << "\n"
238 << " Interpolating between " << x0 << " and " << x1 << "\n";
239 }
240 const double y0 = tabdEdx[it0 - tabBg.cbegin()];
241 const double y1 = tabdEdx[it1 - tabBg.cbegin()];
242 const double tol = 1.e-6 * (x1 - x0);
243 if (fabs(m_bg - x0) < tol) return y0 * 1.e4;
244 if (fabs(m_bg - x1) < tol) return y1 * 1.e4;
245
246 // Log-log interpolation
247 const double lnx0 = log(x0);
248 const double lnx1 = log(x1);
249 const double lny0 = log(y0);
250 const double lny1 = log(y1);
251 const double dedx = lny0 + (log(m_bg) - lnx0) * (lny1 - lny0) / (lnx1 - lnx0);
252 return 1.e4 * exp(dedx);
253}
254
255bool TrackBichsel::LoadCrossSectionTable(const std::string& filename) {
256 const int nRows = 10000;
257 const int nBlocks = 2;
258 const int nColumns = 5;
259
260 const int iSwitch = 99999;
261
262 // Get the path to the data directory.
263 char* pPath = getenv("GARFIELD_HOME");
264 if (pPath == 0) {
265 std::cerr << m_className << "::LoadCrossSectionTable:\n";
266 std::cerr << " Environment variable GARFIELD_HOME is not set.\n";
267 return false;
268 }
269 std::string filepath = pPath;
270 filepath = filepath + "/Data/" + filename;
271
272 // Open the file.
273 std::ifstream infile;
274 infile.open(filepath.c_str(), std::ios::in);
275 // Check if the file could be opened.
276 if (!infile) {
277 std::cerr << m_className << "::LoadCrossSectionTable:\n";
278 std::cerr << " Error opening file " << filename << ".\n";
279 return false;
280 }
281
282 // Initialise the cumulative distribution table.
283 m_cdf.assign(nRows, std::vector<double>(nBlocks * nColumns, 0.));
284
285 std::string line;
286 std::istringstream data;
287 int dummy1 = 0;
288 double dummy2 = 0.;
289
290 double val[nColumns];
291 int iBlock = 0;
292 int iRow = 0;
293
294 while (!infile.eof() && !infile.fail()) {
295 // Read the line.
296 std::getline(infile, line);
297 // Strip white space from the beginning of the line.
298 ltrim(line);
299 if (line.empty()) continue;
300 // Skip comments.
301 if (line[0] == '#' || line[0] == '*' || (line[0] == '/' && line[1] == '/'))
302 continue;
303 // Extract the values.
304 data.str(line);
305 data >> dummy1 >> dummy2;
306 for (int j = 0; j < nColumns; ++j) data >> val[j];
307 // 99999 indicates the end of a data block.
308 if (dummy1 == iSwitch) {
309 ++iBlock;
310 if (iBlock >= nBlocks) break;
311 // Reset the row counter.
312 iRow = 0;
313 continue;
314 } else if (dummy1 != iRow + 1) {
315 std::cerr << m_className << "::LoadCrossSectionTable:\n";
316 std::cerr << " Error reading file " << filename << ".\n";
317 std::cerr << " Expected entry " << iRow + 1 << ", got entry " << dummy1
318 << ".\n";
319 infile.close();
320 m_cdf.clear();
321 return false;
322 }
323 if (iRow >= nRows) {
324 std::cerr << m_className << "::LoadCrossSectionTable:\n";
325 std::cerr << " Table in file is longer than expected.\n";
326 infile.close();
327 m_cdf.clear();
328 return false;
329 }
330 for (int j = nColumns; j--;) m_cdf[iRow][nColumns * iBlock + j] = val[j];
331 ++iRow;
332 }
333
334 if (infile.fail()) {
335 std::cerr << m_className << "::LoadCrossSectionTable:\n";
336 std::cerr << " Error reading file " << filename << ".\n";
337 infile.close();
338 m_cdf.clear();
339 return false;
340 }
341 infile.close();
342
343 if (m_debug) {
344 std::cout << m_className << "::LoadCrossSectionTable:\n";
345 std::cout << " Input file: " << filename << std::endl;
346 std::cout << " Successfully loaded cross-section table from file.\n";
347 }
348 m_nCdfEntries = nRows;
349 return true;
350}
351
352void TrackBichsel::SelectCrossSectionTable() {
353 constexpr unsigned int nTables = 10;
354 const double tabBg[nTables] = {0.31623, 1.00000, 3.16228, 10.00000,
355 31.62278, 100.00000, 316.22780, 1000.00000,
356 3162.27800, 10000.00000};
357
358 bool gotValue = false;
359 // Select the table which is closest to the value of bg.
360 for (unsigned int i = 0; i < nTables - 1; ++i) {
361 double split = exp(0.5 * (log(tabBg[i]) + log(tabBg[i + 1])));
362 if (m_bg < split) {
363 m_iCdf = i;
364 gotValue = true;
365 break;
366 }
367 }
368 if (!gotValue) m_iCdf = nTables - 1;
369
370 if (m_debug) {
371 std::cout << m_className << "::SelectCrossSectionTable:\n";
372 std::cout << " Requested value: bg = " << m_bg << "\n";
373 std::cout << " Used table: bg = " << tabBg[m_iCdf] << "\n";
374 }
375}
376}
Abstract base class for media.
Definition: Medium.hh:13
const std::string & GetName() const
Get the medium name/identifier.
Definition: Medium.hh:23
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:166
virtual double GetStoppingPower()
Get the stopping power (mean energy loss [eV] per cm).
virtual bool GetCluster(double &xcls, double &ycls, double &zcls, double &tcls, int &n, double &e, double &extra)
Definition: TrackBichsel.cc:96
virtual double GetClusterDensity()
virtual bool NewTrack(const double x0, const double y0, const double z0, const double t0, const double dx0, const double dy0, const double dz0)
Definition: TrackBichsel.cc:22
TrackBichsel()
Constructor.
Definition: TrackBichsel.cc:17
Abstract base class for track generation.
Definition: Track.hh:14
double GetBetaGamma() const
Return the of the projectile.
Definition: Track.hh:52
Sensor * m_sensor
Definition: Track.hh:112
bool m_debug
Definition: Track.hh:119
double GetBeta() const
Return the speed ( ) of the projectile.
Definition: Track.hh:54
bool m_isChanged
Definition: Track.hh:114
std::string m_className
Definition: Track.hh:102
double RndmUniform()
Draw a random number uniformly distributed in the range [0, 1).
Definition: Random.hh:14
void ltrim(std::string &line)
Definition: Utilities.hh:10
void RndmDirection(double &dx, double &dy, double &dz, const double length=1.)
Draw a random (isotropic) direction vector.
Definition: Random.hh:107
double RndmUniformPos()
Draw a random number uniformly distributed in the range (0, 1).
Definition: Random.hh:17
DoubleAc exp(const DoubleAc &f)
Definition: DoubleAc.cpp:377