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