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