Garfield++ v2r0
A toolkit for the detailed simulation of particle detectors based on ionisation measurement in gases and semiconductors
Loading...
Searching...
No Matches
TrackSrim.cc
Go to the documentation of this file.
1#include <iostream>
2#include <fstream>
3
4#include <TCanvas.h>
5#include <TGraph.h>
6#include <TH1.h>
7#include <TLegend.h>
8
9#include "TrackSrim.hh"
11#include "GarfieldConstants.hh"
12#include "Numerics.hh"
13#include "Random.hh"
14#include "Sensor.hh"
15
16namespace {
17
18void frame(TCanvas* c, double xmin, double ymin, double xmax, double ymax,
19 const std::string& xtitle, const std::string& ytitle) {
20 c->Range(340.8701, -0.1167765, 1233.468, 4.061288);
21 c->SetFillColor(0);
22 c->SetBorderMode(0);
23 c->SetBorderSize(2);
24 c->SetGridx();
25 c->SetGridy();
26 c->SetTickx();
27 c->SetTicky();
28 c->SetRightMargin(0.018);
29 c->SetLeftMargin(0.098);
30 c->SetTopMargin(0.015);
31 c->SetBottomMargin(0.09);
32 c->SetFrameLineWidth(2);
33 c->SetFrameBorderMode(0);
34 c->SetFrameBorderSize(12);
35 c->SetFrameLineWidth(2);
36 c->SetFrameBorderMode(0);
37 c->SetFrameBorderSize(12);
38 TH1* hframe = new TH1F("hframe", "", 10, xmin, xmax);
39 hframe->SetMinimum(ymin);
40 hframe->SetMaximum(ymax);
41 hframe->SetDirectory(0);
42 hframe->SetStats(0);
43 hframe->GetXaxis()->SetTitle(xtitle.c_str());
44 hframe->GetXaxis()->SetLabelFont(22);
45 hframe->GetXaxis()->SetLabelOffset(0.007);
46 hframe->GetXaxis()->SetLabelSize(0.038);
47 hframe->GetXaxis()->SetTitleSize(0.044);
48 hframe->GetXaxis()->SetTickLength(0.035);
49 hframe->GetXaxis()->SetTitleOffset(0.9);
50 hframe->GetXaxis()->SetTitleFont(22);
51 hframe->GetYaxis()->SetTitle(ytitle.c_str());
52 hframe->GetYaxis()->SetLabelFont(22);
53 hframe->GetYaxis()->SetLabelOffset(0.01);
54 hframe->GetYaxis()->SetLabelSize(0.038);
55 hframe->GetYaxis()->SetTitleSize(0.044);
56 hframe->GetYaxis()->SetTickLength(0.035);
57 hframe->GetYaxis()->SetTitleOffset(0.9);
58 hframe->GetYaxis()->SetTitleFont(22);
59 hframe->Draw(" ");
60}
61
62double StepVavilov(const double rkappa) {
63
64 double xlmin = -3.7;
65 if (rkappa < 0.1) {
66 xlmin = -2.7;
67 } else if (rkappa < 1) {
68 xlmin = -2.9;
69 } else if (rkappa < 2) {
70 xlmin = -3.0;
71 } else if (rkappa < 3) {
72 xlmin = -3.1;
73 } else if (rkappa < 4) {
74 xlmin = -3.2;
75 } else if (rkappa < 5) {
76 xlmin = -3.3;
77 } else if (rkappa < 6) {
78 xlmin = -3.4;
79 } else if (rkappa < 7) {
80 xlmin = -3.5;
81 } else if (rkappa < 8) {
82 xlmin = -3.6;
83 }
84 return xlmin;
85}
86
87double Interpolate(const double x, const std::vector<double>& xtab,
88 const std::vector<double>& ytab) {
89
90 if (x < xtab[0]) {
91 return ytab[0];
92 } else if (x > xtab.back()) {
93 return ytab.back();
94 }
95 return Garfield::Numerics::Divdif(ytab, xtab, xtab.size(), x, 2);
96}
97
98void PrintSettings(const std::string& hdr,
99 const double de, const double step, const double ekin,
100 const double beta2, const double gamma,
101 const double agas, const double zgas, const double density,
102 const double qpart, const double mpart, const double emax,
103 const double xi, const double kappa) {
104
105 std::cout << hdr << "Settings:\n"
106 << " dE = " << de << " MeV,\n"
107 << " step = " << step << " cm.\n"
108 << " Ekin = " << ekin << " MeV,\n"
109 << " beta2 = " << beta2 << ",\n"
110 << " gamma = " << gamma << ".\n"
111 << " Agas = " << agas << ", Zgas = " << zgas << ",\n"
112 << " density = " << density << " g/cm3.\n"
113 << " Qpart = " << qpart << ", mpart = " << mpart << " MeV.\n"
114 << " Emax = " << emax << " MeV,\n"
115 << " xi = " << xi << " MeV,\n"
116 << " kappa = " << kappa << ".\n";
117}
118}
119
120namespace Garfield {
121
123 : Track(),
124 m_precisevavilov(false),
125 m_useTransStraggle(true),
126 m_useLongStraggle(false),
127 m_chargeset(false),
128 m_density(-1.0),
129 m_work(-1.),
130 m_fano(-1.),
131 m_a(-1.),
132 m_z(-1.),
133 m_maxclusters(-1),
134 m_model(4),
135 m_nsize(-1) {
136 m_className = "TrackSrim";
137 m_mass = -1.;
138}
139
140bool TrackSrim::ReadFile(const std::string& file) {
141 // SRMREA
142
143 const std::string hdr = m_className + "::ReadFile:\n ";
144 // Open the material list.
145 std::ifstream fsrim;
146 fsrim.open(file.c_str(), std::ios::in);
147 if (fsrim.fail()) {
148 std::cerr << hdr << "Could not open SRIM file " << file
149 << " for reading.\n The file perhaps does not exist.\n";
150 return false;
151 }
152 unsigned int nread = 0;
153
154 // Read the header
155 if (m_debug) {
156 std::cout << hdr << "SRIM header records from file " << file << "\n";
157 }
158 const int size = 100;
159 char line[size];
160 while (fsrim.getline(line, 100, '\n')) {
161 nread++;
162 if (strstr(line, "SRIM version") != NULL) {
163 if (m_debug) std::cout << "\t" << line << "\n";
164 } else if (strstr(line, "Calc. date") != NULL) {
165 if (m_debug) std::cout << "\t" << line << "\n";
166 } else if (strstr(line, "Ion =") != NULL) {
167 break;
168 }
169 }
170
171 // Identify the ion
172 char* token = NULL;
173 token = strtok(line, " []=");
174 token = strtok(NULL, " []=");
175 token = strtok(NULL, " []=");
176 // Set the ion charge.
177 m_q = std::atof(token);
178 m_chargeset = true;
179 token = strtok(NULL, " []=");
180 token = strtok(NULL, " []=");
181 token = strtok(NULL, " []=");
182 // Set the ion mass (convert amu to eV).
183 m_mass = std::atof(token) * AtomicMassUnitElectronVolt;
184
185 // Find the target density
186 if (!fsrim.getline(line, 100, '\n')) {
187 std::cerr << hdr << "Premature EOF looking for target density (line "
188 << nread << ").\n";
189 return false;
190 }
191 nread++;
192 if (!fsrim.getline(line, 100, '\n')) {
193 std::cerr << hdr << "Premature EOF looking for target density (line "
194 << nread << ").\n";
195 return false;
196 }
197 nread++;
198 token = strtok(line, " ");
199 token = strtok(NULL, " ");
200 token = strtok(NULL, " ");
201 token = strtok(NULL, " ");
202 SetDensity(std::atof(token));
203
204 // Check the stopping units
205 while (fsrim.getline(line, 100, '\n')) {
206 nread++;
207 if (strstr(line, "Stopping Units") == NULL) continue;
208 if (strstr(line, "Stopping Units = MeV / (mg/cm2)") != NULL) {
209 if (m_debug) {
210 std::cout << hdr << "Stopping units: MeV / (mg/cm2) as expected.\n";
211 }
212 break;
213 }
214 std::cerr << hdr << "Unknown stopping units. Aborting (line " << nread
215 << ").\n";
216 return false;
217 }
218
219 // Skip to the table
220 while (fsrim.getline(line, 100, '\n')) {
221 nread++;
222 if (strstr(line, "-----------") != NULL) break;
223 }
224
225 // Read the table line by line
226 m_ekin.clear();
227 m_emloss.clear();
228 m_hdloss.clear();
229 m_range.clear();
230 m_transstraggle.clear();
231 m_longstraggle.clear();
232 unsigned int ntable = 0;
233 while (fsrim.getline(line, 100, '\n')) {
234 nread++;
235 if (strstr(line, "-----------") != NULL) break;
236 // Energy
237 token = strtok(line, " ");
238 m_ekin.push_back(atof(token));
239 token = strtok(NULL, " ");
240 if (strcmp(token, "eV") == 0) {
241 m_ekin[ntable] *= 1.0e-6;
242 } else if (strcmp(token, "keV") == 0) {
243 m_ekin[ntable] *= 1.0e-3;
244 } else if (strcmp(token, "GeV") == 0) {
245 m_ekin[ntable] *= 1.0e3;
246 } else if (strcmp(token, "MeV") != 0) {
247 std::cerr << hdr << "Unknown energy unit " << token << "; aborting\n";
248 return false;
249 }
250 // EM loss
251 token = strtok(NULL, " ");
252 m_emloss.push_back(atof(token));
253 // HD loss
254 token = strtok(NULL, " ");
255 m_hdloss.push_back(atof(token));
256 // Projected range
257 token = strtok(NULL, " ");
258 m_range.push_back(atof(token));
259 token = strtok(NULL, " ");
260 if (strcmp(token, "A") == 0) {
261 m_range[ntable] *= 1.0e-8;
262 } else if (strcmp(token, "um") == 0) {
263 m_range[ntable] *= 1.0e-4;
264 } else if (strcmp(token, "mm") == 0) {
265 m_range[ntable] *= 1.0e-1;
266 } else if (strcmp(token, "m") == 0) {
267 m_range[ntable] *= 1.0e2;
268 } else if (strcmp(token, "km") == 0) {
269 m_range[ntable] *= 1.0e5;
270 } else if (strcmp(token, "cm") != 0) {
271 std::cerr << hdr << "Unknown distance unit " << token << "; aborting\n";
272 return false;
273 }
274 // Longitudinal straggling
275 token = strtok(NULL, " ");
276 m_longstraggle.push_back(atof(token));
277 token = strtok(NULL, " ");
278 if (strcmp(token, "A") == 0) {
279 m_longstraggle[ntable] *= 1.0e-8;
280 } else if (strcmp(token, "um") == 0) {
281 m_longstraggle[ntable] *= 1.0e-4;
282 } else if (strcmp(token, "mm") == 0) {
283 m_longstraggle[ntable] *= 1.0e-1;
284 } else if (strcmp(token, "m") == 0) {
285 m_longstraggle[ntable] *= 1.0e2;
286 } else if (strcmp(token, "km") == 0) {
287 m_longstraggle[ntable] *= 1.0e5;
288 } else if (strcmp(token, "cm") != 0) {
289 std::cerr << hdr << "Unknown distance unit " << token << "; aborting\n";
290 return false;
291 }
292 // Transverse straggling
293 token = strtok(NULL, " ");
294 m_transstraggle.push_back(atof(token));
295 token = strtok(NULL, " ");
296 if (strcmp(token, "A") == 0) {
297 m_transstraggle[ntable] *= 1.0e-8;
298 } else if (strcmp(token, "um") == 0) {
299 m_transstraggle[ntable] *= 1.0e-4;
300 } else if (strcmp(token, "mm") == 0) {
301 m_transstraggle[ntable] *= 1.0e-1;
302 } else if (strcmp(token, "m") == 0) {
303 m_transstraggle[ntable] *= 1.0e2;
304 } else if (strcmp(token, "km") == 0) {
305 m_transstraggle[ntable] *= 1.0e5;
306 } else if (strcmp(token, "cm") != 0) {
307 std::cerr << hdr << "Unknown distance unit " << token << "; aborting\n";
308 return false;
309 }
310
311 // Increment table line counter
312 ++ntable;
313 }
314
315 // Find the scaling factor and convert to MeV/cm
316 double scale = -1.;
317 while (fsrim.getline(line, 100, '\n')) {
318 nread++;
319 if (strstr(line, "=============") != NULL) {
320 break;
321 } else if (strstr(line, "MeV / (mg/cm2)") != NULL ||
322 strstr(line, "MeV/(mg/cm2)") != NULL) {
323 token = strtok(line, " ");
324 scale = std::atof(token);
325 }
326 }
327 if (scale < 0) {
328 std::cerr << hdr << "Did not find stopping unit scaling; aborting.\n";
329 return false;
330 }
331 scale *= 1.e3;
332 for (unsigned int i = 0; i < ntable; ++i) {
333 m_emloss[i] *= scale;
334 m_hdloss[i] *= scale;
335 }
336
337 // Seems to have worked
338 if (m_debug) {
339 std::cout << hdr << "Successfully read " << file << "(" << nread
340 << " lines).\n";
341 }
342 return true;
343}
344
346
347 std::cout << "TrackSrim::Print:\n SRIM energy loss table\n\n"
348 << " Energy EM Loss HD loss Range "
349 << "l straggle t straggle\n"
350 << " [MeV] [MeV/cm] [MeV/cm] [cm] "
351 << " [cm] [cm]\n\n";
352 const unsigned int nPoints = m_emloss.size();
353 for (unsigned int i = 0; i < nPoints; ++i) {
354 printf("%10g %10g %10g %10g %10g %10g\n", m_ekin[i],
357 }
358 std::cout << "\n";
359 printf(" Work function: %g eV\n", m_work);
360 printf(" Fano factor: %g\n", m_fano);
361 printf(" Ion charge: %g\n", m_q);
362 printf(" Mass: %g MeV\n", 1.e-6 * m_mass);
363 printf(" Density: %g g/cm3\n", m_density);
364 printf(" A, Z: %g, %g\n", m_a, m_z);
365}
366
368
369 // Make a graph for the 3 curves to plot
370 double xmin = 0., xmax = 0.;
371 double ymin = 0., ymax = 0.;
372 const unsigned int nPoints = m_ekin.size();
373 TGraph* grem = new TGraph(nPoints);
374 TGraph* grhd = new TGraph(nPoints);
375 TGraph* grtot = new TGraph(nPoints);
376 for (unsigned int i = 0; i < nPoints; ++i) {
377 const double eplot = m_ekin[i];
378 if (eplot < xmin) xmin = eplot;
379 if (eplot > xmax) xmax = eplot;
380 const double emplot = m_emloss[i] * m_density;
381 const double hdplot = m_hdloss[i] * m_density;
382 const double totplot = emplot + hdplot;
383 if (totplot < ymin) ymin = totplot;
384 if (totplot > ymax) ymax = totplot;
385 grem->SetPoint(i, eplot, emplot);
386 grhd->SetPoint(i, eplot, hdplot);
387 grtot->SetPoint(i, eplot, totplot);
388 }
389
390 // Prepare a plot frame
391 TCanvas* celoss = new TCanvas();
392 celoss->SetLogx();
393 frame(celoss, xmin, 0.0, xmax, ymax * 1.05, "Ion energy [MeV]",
394 "Energy loss [MeV/cm]");
395 TLegend* legend = new TLegend(0.65, 0.75, 0.94, 0.95);
396 legend->SetShadowColor(0);
397 legend->SetTextFont(22);
398 legend->SetTextSize(0.044);
399 legend->SetFillColor(kWhite);
400 legend->SetBorderSize(1);
401
402 grem->SetLineColor(kBlue);
403 grem->SetLineStyle(kSolid);
404 grem->SetLineWidth(2.0);
405 grem->SetMarkerStyle(21);
406 grem->SetMarkerColor(kBlack);
407 grem->Draw("SAME");
408 legend->AddEntry(grem, "EM energy loss", "l");
409
410 grhd->SetLineColor(kGreen + 2);
411 grhd->SetLineStyle(kSolid);
412 grhd->SetLineWidth(2.0);
413 grhd->SetMarkerStyle(21);
414 grhd->SetMarkerColor(kBlack);
415 grhd->Draw("SAME");
416 legend->AddEntry(grhd, "HD energy loss", "l");
417
418 grtot->SetLineColor(kOrange);
419 grtot->SetLineStyle(kSolid);
420 grtot->SetLineWidth(2.0);
421 grtot->SetMarkerStyle(21);
422 grtot->SetMarkerColor(kBlack);
423 grtot->Draw("SAME");
424 legend->AddEntry(grtot, "Total energy loss", "l");
425
426 legend->Draw();
427}
428
430
431 // Make a graph
432 double xmin = 0., xmax = 0.;
433 double ymin = 0., ymax = 0.;
434 const unsigned int nPoints = m_ekin.size();
435 TGraph* grrange = new TGraph(nPoints);
436 for (unsigned int i = 0; i < nPoints; ++i) {
437 const double eplot = m_ekin[i];
438 if (eplot < xmin) xmin = eplot;
439 if (eplot > xmax) xmax = eplot;
440 const double rangeplot = m_range[i];
441 if (rangeplot < ymin) ymin = rangeplot;
442 if (rangeplot > ymax) ymax = rangeplot;
443 grrange->SetPoint(i, eplot, rangeplot);
444 }
445
446 // Prepare a plot frame
447 TCanvas* crange = new TCanvas();
448 crange->SetLogx();
449 frame(crange, xmin, 0.0, xmax, ymax * 1.05, "Ion energy [MeV]",
450 "Projected range [cm]");
451
452 grrange->SetLineColor(kOrange);
453 grrange->SetLineStyle(kSolid);
454 grrange->SetLineWidth(2.0);
455 grrange->SetMarkerStyle(21);
456 grrange->SetMarkerColor(kBlack);
457 grrange->Draw("SAME");
458}
459
461
462 // Make a graph for the 2 curves to plot
463 double xmin = 0., xmax = 0.;
464 double ymin = 0., ymax = 0.;
465 const unsigned int nPoints = m_ekin.size();
466 TGraph* grlong = new TGraph(nPoints);
467 TGraph* grtrans = new TGraph(nPoints);
468 for (unsigned int i = 0; i < nPoints; ++i) {
469 const double eplot = m_ekin[i];
470 if (eplot < xmin) xmin = eplot;
471 if (eplot > xmax) xmax = eplot;
472 const double longplot = m_longstraggle[i];
473 const double transplot = m_transstraggle[i];
474 if (longplot < ymin) ymin = longplot;
475 if (longplot > ymax) ymax = longplot;
476 if (transplot < ymin) ymin = transplot;
477 if (transplot > ymax) ymax = transplot;
478 grlong->SetPoint(i, eplot, longplot);
479 grtrans->SetPoint(i, eplot, transplot);
480 }
481
482 // Prepare a plot frame
483 TCanvas* cstraggle = new TCanvas();
484 cstraggle->SetLogx();
485 frame(cstraggle, xmin, 0.0, xmax, ymax * 1.05, "Ion energy [MeV]",
486 "Straggling [cm]");
487 TLegend* legend = new TLegend(0.15, 0.75, 0.44, 0.95);
488 legend->SetShadowColor(0);
489 legend->SetTextFont(22);
490 legend->SetTextSize(0.044);
491 legend->SetFillColor(kWhite);
492 legend->SetBorderSize(1);
493
494 grlong->SetLineColor(kOrange);
495 grlong->SetLineStyle(kSolid);
496 grlong->SetLineWidth(2.0);
497 grlong->SetMarkerStyle(21);
498 grlong->SetMarkerColor(kBlack);
499 grlong->Draw("SAME");
500 legend->AddEntry(grlong, "Longitudinal", "l");
501
502 grtrans->SetLineColor(kGreen + 2);
503 grtrans->SetLineStyle(kSolid);
504 grtrans->SetLineWidth(2.0);
505 grtrans->SetMarkerStyle(21);
506 grtrans->SetMarkerColor(kBlack);
507 grtrans->Draw("SAME");
508 legend->AddEntry(grtrans, "Transversal", "l");
509
510 legend->Draw();
511}
512
513double TrackSrim::DedxEM(const double e) const {
514 return Interpolate(e, m_ekin, m_emloss);
515}
516
517double TrackSrim::DedxHD(const double e) const {
518 return Interpolate(e, m_ekin, m_hdloss);
519}
520
521bool TrackSrim::PreciseLoss(const double step, const double estart,
522 double& deem, double& dehd) const {
523 // SRMRKS
524
525 const std::string hdr = m_className + "::PreciseLoss: ";
526 // Debugging
527 if (m_debug) {
528 std::cout << hdr << "\n"
529 << " Initial energy: " << estart << " MeV\n"
530 << " Step: " << step << " cm\n";
531 }
532 // Precision aimed for.
533 const double eps = 1.0e-2;
534 // Number of intervals.
535 unsigned int ndiv = 1;
536 // Loop until precision achieved
537 const unsigned int nMaxIter = 10;
538 bool converged = false;
539 for (unsigned int iter = 0; iter < nMaxIter; ++iter) {
540 double e4 = estart;
541 double e2 = estart;
542 deem = 0.;
543 dehd = 0.;
544 // Compute rk2 and rk4 over the number of sub-divisions
545 const double s = m_density * step / ndiv;
546 for (unsigned int i = 0; i < ndiv; i++) {
547 // rk2: initial point
548 const double em21 = s * DedxEM(e2);
549 const double hd21 = s * DedxHD(e2);
550 const double de21 = em21 + hd21;
551 // Mid-way point
552 const double em22 = s * DedxEM(e2 - 0.5 * de21);
553 const double hd22 = s * DedxHD(e2 - 0.5 * de21);
554 // Trace the rk2 energy
555 e2 -= em22 + hd22;
556 // rk4: initial point
557 const double em41 = s * DedxEM(e4);
558 const double hd41 = s * DedxHD(e4);
559 const double de41 = em41 + hd41;
560 // Mid-way point
561 const double em42 = s * DedxEM(e4 - 0.5 * de41);
562 const double hd42 = s * DedxHD(e4 - 0.5 * de41);
563 const double de42 = em42 + hd42;
564 // Second mid-point estimate
565 const double em43 = s * DedxEM(e4 - 0.5 * de42);
566 const double hd43 = s * DedxHD(e4 - 0.5 * de42);
567 const double de43 = em43 + hd43;
568 // End point estimate
569 const double em44 = s * DedxEM(e4 - de43);
570 const double hd44 = s * DedxHD(e4 - de43);
571 const double de44 = em44 + hd44;
572 // Store the energy loss terms (according to rk4)
573 deem += (em41 + em44) / 6. + (em42 + em43) / 3.;
574 dehd += (hd41 + hd44) / 6. + (hd42 + hd43) / 3.;
575 // Store the new energy computed with rk4
576 e4 -= (de41 + de44) / 6. + (de42 + de43) / 3.;
577 }
578 if (m_debug) {
579 std::cout << hdr << "\n Iteration " << iter << " has " << ndiv
580 << " division(s). Losses:\n";
581 printf("\tde4 = %12g, de2 = %12g MeV\n", estart - e2, estart - e4);
582 printf("\tem4 = %12g, hd4 = %12g MeV\n", deem, dehd);
583 }
584 // Compare the two estimates
585 if (fabs(e2 - e4) > eps * (fabs(e2) + fabs(e4) + fabs(estart))) {
586 // Repeat with twice the number of steps.
587 ndiv *= 2;
588 } else {
589 converged = true;
590 break;
591 }
592 }
593
594 if (!converged) {
595 std::cerr << hdr << "No convergence achieved integrating energy loss.\n";
596 } else if (m_debug) {
597 std::cout << hdr << "Convergence at eps = " << eps << "\n";
598 }
599 return converged;
600}
601
602bool TrackSrim::EstimateRange(const double ekin, const double step,
603 double& stpmax) {
604 // Find distance over which the ion just does not lose all its energy
605 // ekin : Kinetic energy [MeV]
606 // step : Step length as guessed [cm]
607 // stpmax : Maximum step
608 // SRMDEZ
609
610 const std::string hdr = m_className + "::EstimateRange: ";
611 // Initial estimate
612 stpmax = step;
613
614 // Find the energy loss expected for the present step length.
615 double st1 = step;
616 double deem = 0., dehd = 0.;
617 PreciseLoss(st1, ekin, deem, dehd);
618 double de1 = deem + dehd;
619 // Do nothing if this is ok
620 if (de1 < ekin) {
621 if (m_debug) std::cout << hdr << "Initial step OK.\n";
622 return true;
623 }
624 // Find a smaller step for which the energy loss is less than EKIN.
625 double st2 = 0.5 * step;
626 double de2 = de1;
627 const unsigned int nMaxIter = 20;
628 for (unsigned int iter = 0; iter < nMaxIter; ++iter) {
629 // See where we stand
630 PreciseLoss(st2, ekin, deem, dehd);
631 de2 = deem + dehd;
632 // Below the kinetic energy: done
633 if (de2 < ekin) break;
634 // Not yet below the kinetic energy: new iteration.
635 st1 = st2;
636 de1 = de2;
637 st2 *= 0.5;
638 }
639 if (de2 >= ekin) {
640 std::cerr << hdr << "\n Did not find a smaller step in " << nMaxIter
641 << " iterations. Abandoned.\n";
642 stpmax = 0.5 * (st1 + st2);
643 return false;
644 }
645 if (m_debug)
646 printf("\tstep 1 = %g cm, de 1 = %g MeV\n\tstep 2 = %g cm, de 2 = %g MeV\n",
647 st1, de1 - ekin, st2, de2 - ekin);
648
649 // Now perform a bisection
650 for (unsigned int iter = 0; iter < nMaxIter; ++iter) {
651 // Avoid division by zero.
652 if (de2 == de1) {
653 if (m_debug) {
654 std::cerr << hdr << "Bisection failed due to equal energy loss for "
655 << "two step sizes. Abandoned.\n";
656 }
657 stpmax = 0.5 * (st1 + st2);
658 return false;
659 }
660 // Estimate step to give total energy loss.
661 double st3;
662 if ((fabs(de1 - ekin) < 0.01 * fabs(de2 - de1)) ||
663 (fabs(de1 - ekin) > 0.99 * fabs(de2 - de1))) {
664 st3 = 0.5 * (st1 + st2);
665 } else {
666 st3 = st1 - (st2 - st1) * (de1 - ekin) / (de2 - de1);
667 }
668 // See how well we are doing.
669 PreciseLoss(st3, ekin, deem, dehd);
670 const double de3 = deem + dehd;
671 if (m_debug) printf("\tStep 1 = %g cm, dE 1 = %g MeV\n", st1, de1 - ekin);
672 if (m_debug) printf("\tStep 2 = %g cm, dE 2 = %g MeV\n", st2, de2 - ekin);
673 if (m_debug) printf("\tStep 3 = %g cm, dE 3 = %g MeV\n", st3, de3 - ekin);
674 // Update the estimates above and below.
675 if (de3 > ekin) {
676 st1 = st3;
677 de1 = de3;
678 } else {
679 st2 = st3;
680 de2 = de3;
681 }
682 // See whether we've converged.
683 if (fabs(de3 - ekin) < 1e-3 * (fabs(de3) + fabs(ekin)) ||
684 fabs(st1 - st2) < 1e-3 * (fabs(st1) + fabs(st2))) {
685 stpmax = st1 - (st2 - st1) * (de1 - ekin) / (de2 - de1);
686 return true;
687 }
688 }
689 if (m_debug) {
690 std::cout << hdr << "Bisection did not converge in " << nMaxIter
691 << " steps. Abandoned.\n";
692 }
693 stpmax = st1 - (st2 - st1) * (de1 - ekin) / (de2 - de1);
694 return false;
695}
696
697
698bool TrackSrim::NewTrack(const double x0, const double y0, const double z0,
699 const double t0, const double dx0, const double dy0,
700 const double dz0) {
701
702 // Generates electrons for a SRIM track
703 // SRMGEN
704 const std::string hdr = m_className + "::NewTrack: ";
705
706 // Verify that a sensor has been set.
707 if (!m_sensor) {
708 std::cerr << hdr << "\n Sensor is not defined.\n";
709 return false;
710 }
711
712 // Get the bounding box.
713 double xmin = 0., ymin = 0., zmin = 0.;
714 double xmax = 0., ymax = 0., zmax = 0.;
715 if (!m_sensor->GetArea(xmin, ymin, zmin, xmax, ymax, zmax)) {
716 std::cerr << hdr << "\n Drift area is not set.\n";
717 return false;
718 } else if (x0 < xmin || x0 > xmax ||
719 y0 < ymin || y0 > ymax || z0 < zmin || z0 > zmax) {
720 std::cerr << hdr << "\n Initial position outside bounding box.\n";
721 return false;
722 }
723
724 // Make sure the initial position is inside an ionisable medium.
725 Medium* medium = NULL;
726 if (!m_sensor->GetMedium(x0, y0, z0, medium)) {
727 std::cerr << hdr << "\n No medium at initial position.\n";
728 return false;
729 } else if (!medium->IsIonisable()) {
730 std::cerr << hdr << "\n Medium at initial position is not ionisable.\n";
731 return false;
732 }
733
734 // Normalise and store the direction.
735 const double normdir = sqrt(dx0 * dx0 + dy0 * dy0 + dz0 * dz0);
736 double xdir = dx0;
737 double ydir = dy0;
738 double zdir = dz0;
739 if (normdir < Small) {
740 if (m_debug) {
741 std::cout << hdr << "\n Direction vector has zero norm.\n"
742 << " Initial direction is randomized.\n";
743 }
744 // Null vector. Sample the direction isotropically.
745 RndmDirection(xdir, ydir, zdir);
746 } else {
747 // Normalise the direction vector.
748 xdir /= normdir;
749 ydir /= normdir;
750 zdir /= normdir;
751 }
752
753 // Make sure all necessary parameters have been set.
754 if (m_mass < Small) {
755 std::cerr << hdr << "\n Particle mass not set.\n";
756 return false;
757 } else if (!m_chargeset) {
758 std::cerr << hdr << "\n Particle charge not set.\n";
759 return false;
760 } else if (m_energy < Small) {
761 std::cerr << hdr << "\n Initial particle energy not set.\n";
762 return false;
763 } else if (m_work < Small) {
764 std::cerr << hdr << "\n Work function not set.\n";
765 return false;
766 } else if (m_a < Small || m_z < Small) {
767 std::cerr << hdr << "\n A and/or Z not set.\n";
768 return false;
769 }
770 // Check the initial energy (in MeV).
771 const double ekin0 = 1.e-6 * GetKineticEnergy();
772 if (ekin0 < 1.e-14 * m_mass || ekin0 < 1.e-3 * m_work) {
773 if (m_debug) {
774 std::cout << hdr << "Initial kinetic energy E = " << ekin0
775 << " MeV such that beta2 = 0 or E << W; particle stopped.\n";
776 }
777 return true;
778 }
779
780 // Get an upper limit for the track length.
781 const double tracklength = 10 * Interpolate(ekin0, m_ekin, m_range);
782
783 // Header of debugging output.
784 if (m_debug) {
785 std::cout << hdr << "Track generation with the following parameters:\n";
786 const unsigned int nTable = m_ekin.size();
787 printf(" Table size %u\n", nTable);
788 printf(" Particle kin. energy %g MeV\n", ekin0);
789 printf(" Particle mass %g MeV\n", 1.e-6 * m_mass);
790 printf(" Particle charge %g\n", m_q);
791 printf(" Work function %g eV\n", m_work);
792 if (m_fano > 0.) {
793 printf(" Fano factor %g\n", m_fano);
794 } else {
795 std::cout << " Fano factor Not set\n";
796 }
797 printf(" Long. straggling: %d\n", m_useLongStraggle);
798 printf(" Trans. straggling: %d\n", m_useTransStraggle);
799 // printf(" Vavilov generator: %d\n", m_precisevavilov);
800 printf(" Cluster size %d\n", m_nsize);
801 }
802
803 // Reset the cluster count
804 m_currcluster = 0;
805 m_clusters.clear();
806
807
808 // Initial situation: starting position
809 double x = x0;
810 double y = y0;
811 double z = z0;
812
813 // Store the energy [MeV].
814 double e = ekin0;
815 // Total distance covered
816 double dsum = 0.0;
817 // Pool of unused energy
818 double epool = 0.0;
819
820 // Loop generating clusters
821 int iter = 0;
822 while (iter < m_maxclusters || m_maxclusters < 0) {
823 // Work out what the energy loss per cm, straggling and projected range are
824 // at the start of the step.
825 const double dedxem = DedxEM(e) * m_density;
826 const double dedxhd = DedxHD(e) * m_density;
827 const double prange = Interpolate(e, m_ekin, m_range);
828 double strlon = Interpolate(e, m_ekin, m_longstraggle);
829 double strlat = Interpolate(e, m_ekin, m_transstraggle);
830
831 if (!m_useLongStraggle) strlon = 0;
832 if (!m_useTransStraggle) strlat = 0;
833
834 if (m_debug) {
835 std::cout << hdr << "\n Energy = " << e << " MeV,\n dEdx em, hd = "
836 << dedxem << ", " << dedxhd << " MeV/cm,\n e-/cm = "
837 << 1.e6 * dedxem / m_work << ".\n Straggling long/lat: "
838 << strlon << ", " << strlat << " cm\n";
839 }
840 // Find the step size for which we get approximately the target # clusters.
841 double step;
842 if (m_nsize > 0) {
843 step = m_nsize * 1.e-6 * m_work / dedxem;
844 } else {
845 const double ncls = m_maxclusters > 0 ? 0.5 * m_maxclusters : 100;
846 step = ekin0 / (ncls * (dedxem + dedxhd));
847 }
848 // Truncate if this step exceeds the length.
849 bool finish = false;
850 // Make an accurate integration of the energy loss over the step.
851 double deem = 0., dehd = 0.;
852 PreciseLoss(step, e, deem, dehd);
853 // If the energy loss exceeds the particle energy, truncate step.
854 double stpmax;
855 if (deem + dehd > e) {
856 EstimateRange(e, step, stpmax);
857 step = stpmax;
858 PreciseLoss(step, e, deem, dehd);
859 deem = e * deem / (dehd + deem);
860 dehd = e - deem;
861 finish = true;
862 if (m_debug) std::cout << hdr << "Finish raised. Track length reached.\n";
863 } else {
864 stpmax = tracklength - dsum;
865 }
866
867 // Ensure that this is larger than the minimum modelable step size.
868 double stpmin;
869 if (!SmallestStep(e, deem, step, stpmin)) {
870 std::cerr << hdr << "\n Failure computing the minimum step size."
871 << "\n Clustering abandoned.\n";
872 return false;
873 }
874
875 double eloss;
876 if (stpmin > stpmax) {
877 // No way to find a suitable step size: use fixed energy loss
878 if (m_debug) std::cout << hdr << "stpmin > stpmax. Deposit all energy.\n";
879 eloss = deem;
880 if (e - eloss - dehd < 0) eloss = e - dehd;
881 finish = true;
882 if (m_debug) std::cout << hdr << "Finish raised. Single deposit.\n";
883 } else if (step < stpmin) {
884 // If needed enlarge the step size
885 if (m_debug) std::cout << hdr << "Enlarging step size.\n";
886 step = stpmin;
887 PreciseLoss(step, e, deem, dehd);
888 if (deem + dehd > e) {
889 if (m_debug) std::cout << hdr << "Excess loss. Recomputing stpmax.\n";
890 EstimateRange(e, step, stpmax);
891 step = stpmax;
892 PreciseLoss(step, e, deem, dehd);
893 deem = e * deem / (dehd + deem);
894 dehd = e - deem;
895 eloss = deem;
896 } else {
897 eloss = RndmEnergyLoss(e, deem, step);
898 }
899 } else {
900 // Draw an actual energy loss for such a step.
901 if (m_debug) std::cout << hdr << "Using existing step size.\n";
902 eloss = RndmEnergyLoss(e, deem, step);
903 }
904 // Ensure we are neither below 0 nor above the total energy.
905 if (eloss < 0) {
906 if (m_debug) std::cout << hdr << "Truncating negative energy loss.\n";
907 eloss = 0;
908 } else if (eloss > e - dehd) {
909 if (m_debug) std::cout << hdr << "Excess energy loss, using mean.\n";
910 eloss = deem;
911 if (e - eloss - dehd < 0) {
912 eloss = e - dehd;
913 finish = true;
914 if (m_debug) std::cout << hdr << "Finish raised. Using mean energy.\n";
915 }
916 }
917 if (m_debug) {
918 std::cout << hdr << "\n Step length = " << step << " cm.\n "
919 << "Mean loss = " << deem << " MeV.\n "
920 << "Actual loss = " << eloss << " MeV.\n";
921 }
922
923 // Check that the cluster is in an ionisable medium and within bounding box
924 if (!m_sensor->GetMedium(x, y, z, medium)) {
925 if (m_debug) {
926 std::cout << hdr << "No medium at position ("
927 << x << "," << y << "," << z << ").\n";
928 }
929 break;
930 } else if (!medium->IsIonisable()) {
931 if (m_debug) {
932 std::cout << hdr << "Medium at ("
933 << x << "," << y << "," << z << ") is not ionisable.\n";
934 }
935 break;
936 } else if (!m_sensor->IsInArea(x, y, z)) {
937 if (m_debug) {
938 std::cout << hdr << "Cluster at ("
939 << x << "," << y << "," << z << ") outside bounding box.\n";
940 }
941 break;
942 }
943 // Add a cluster.
944 cluster newcluster;
945 newcluster.x = x;
946 newcluster.y = y;
947 newcluster.z = z;
948 newcluster.t = t0;
949 if (m_fano < Small) {
950 // No fluctuations.
951 newcluster.electrons = int((eloss + epool) / (1.e-6 * m_work));
952 newcluster.ec = m_work * newcluster.electrons;
953 } else {
954 double ecl = 1.e6 * (eloss + epool);
955 newcluster.electrons = 0.0;
956 newcluster.ec = 0.0;
957 while (true) {
958 // if (newcluster.ec < 100) printf("ec = %g\n", newcluster.ec);
959 const double ernd1 = RndmHeedWF(m_work, m_fano);
960 if (ernd1 > ecl) break;
961 newcluster.electrons++;
962 newcluster.ec += ernd1;
963 ecl -= ernd1;
964 }
965 // printf("ec = %g DONE\n", newcluster.ec);
966 if (m_debug)
967 std::cout << hdr << "EM + pool: " << 1.e6 * (eloss + epool)
968 << " eV, W: " << m_work << " eV, E/w: "
969 << (eloss + epool) / (1.0e-6 * m_work) << ", n: "
970 << newcluster.electrons << ".\n";
971 }
972 newcluster.kinetic = e;
973 epool += eloss - 1.e-6 * newcluster.ec;
974 if (m_debug) {
975 std::cout << hdr << "Cluster " << m_clusters.size() << "\n at ("
976 << newcluster.x << ", " << newcluster.y << ", " << newcluster.z
977 << "),\n e = " << newcluster.ec << ",\n n = "
978 << newcluster.electrons << ",\n pool = "
979 << epool << " MeV.\n";
980 }
981 m_clusters.push_back(newcluster);
982
983 // Keep track of the length and energy
984 dsum += step;
985 e -= eloss + dehd;
986 if (finish) {
987 // Stop if the flag is raised
988 if (m_debug) std::cout << hdr << "Finishing flag raised.\n";
989 break;
990 } else if (e < ekin0 * 1.e-9) {
991 // No energy left
992 if (m_debug) std::cout << hdr << "Energy exhausted.\n";
993 break;
994 }
995 // Draw scattering distances
996 const double scale = sqrt(step / prange);
997 const double sigt1 = RndmGaussian(0., scale * strlat);
998 const double sigt2 = RndmGaussian(0., scale * strlat);
999 const double sigl = RndmGaussian(0., scale * strlon);
1000 if (m_debug) std::cout << hdr << "sigma l, t1, t2: "
1001 << sigl << ", " << sigt1 << ", " << sigt2 << "\n";
1002 // Rotation angles to bring z-axis in line
1003 double theta, phi;
1004 if (xdir * xdir + zdir * zdir <= 0) {
1005 if (ydir < 0) {
1006 theta = -HalfPi;
1007 } else if (ydir > 0) {
1008 theta = +HalfPi;
1009 } else {
1010 std::cerr << hdr << "\n Zero step length; clustering abandoned.\n";
1011 return false;
1012 }
1013 phi = 0;
1014 } else {
1015 phi = atan2(xdir, zdir);
1016 theta = atan2(ydir, sqrt(xdir * xdir + zdir * zdir));
1017 }
1018
1019 // Update position
1020 const double cp = cos(phi);
1021 const double ct = cos(theta);
1022 const double sp = sin(phi);
1023 const double st = sin(theta);
1024 x += step * xdir + cp * sigt1 - sp * st * sigt2 + sp * ct * sigl;
1025 y += step * ydir + ct * sigt2 + st * sigl;
1026 z += step * zdir - sp * sigt1 - cp * st * sigt2 + cp * ct * sigl;
1027
1028 // (Do not) update direction
1029 if (false) {
1030 xdir = step * xdir + cp * sigt1 - sp * st * sigt2 + sp * ct * sigl;
1031 ydir = step * ydir + ct * sigt2 + st * sigl;
1032 zdir = step * zdir - sp * sigt1 - cp * st * sigt2 + cp * ct * sigl;
1033 double dnorm = sqrt(xdir * xdir + ydir * ydir + zdir * zdir);
1034 if (dnorm <= 0) {
1035 std::cerr << hdr << "\n Zero step length; clustering abandoned.\n";
1036 return false;
1037 }
1038 xdir = xdir / dnorm;
1039 ydir = ydir / dnorm;
1040 zdir = zdir / dnorm;
1041 }
1042 // Next cluster
1043 iter++;
1044 }
1045 if (iter == m_maxclusters) {
1046 std::cerr << hdr << "Exceeded maximum number of clusters.\n";
1047 }
1048 return true;
1049 // finished generating
1050}
1051
1052bool TrackSrim::SmallestStep(const double ekin, double de, double step,
1053 double& stpmin) {
1054 // Determines the smallest step size for which there is little
1055 // or no risk of finding negative energy fluctuations.
1056 // SRMMST
1057
1058 const std::string hdr = m_className + "::SmallestStep: ";
1059 const double expmax = 30;
1060
1061 // By default, assume the step is right.
1062 stpmin = step;
1063 // Check correctness.
1064 if (ekin <= 0 || de <= 0 || step <= 0) {
1065 std::cerr << hdr << "\n Input parameters not valid.\n Ekin = "
1066 << ekin << " MeV, dE = " << de << " MeV, step length = "
1067 << step << " cm.\n";
1068 return false;
1069 } else if (m_mass <= 0 || fabs(m_q) <= 0) {
1070 std::cerr << hdr << "\n Track parameters not valid.\n Mass = "
1071 << m_mass << " eV, charge = " << m_q << ".\n";
1072 return false;
1073 } else if (m_a <= 0 || m_z <= 0 || m_density <= 0) {
1074 std::cerr << hdr << "\n Gas parameters not valid.\n A = " << m_a
1075 << ", Z = " << m_z << " density = " << m_density << " g/cm3.\n";
1076 return false;
1077 }
1078
1079 // Basic kinematic parameters
1080 const double rkin = 1.e6 * ekin / m_mass;
1081 const double gamma = 1. + rkin;
1082 const double beta2 = rkin > 1.e-5 ? 1. - 1. / (gamma * gamma) : 2. * rkin;
1083
1084 // Compute maximum energy transfer [MeV]
1085 const double rm = ElectronMass / m_mass;
1086 const double emax = 2 * ElectronMass * 1.e-6 * beta2 * gamma * gamma /
1087 (1. + 2 * gamma * rm + rm * rm);
1088 // Compute the Rutherford term
1089 const double fconst = 0.1534;
1090 double xi = fconst * m_q * m_q * m_z * m_density * step / (m_a * beta2);
1091 // Compute the scaling parameter
1092 double rkappa = xi / emax;
1093 // Step size and energy loss
1094 double denow = de;
1095 double stpnow = step;
1096 const unsigned int nMaxIter = 10;
1097 for (unsigned int iter = 0; iter < nMaxIter; ++iter) {
1098 bool retry = false;
1099 // Debugging output.
1100 if (m_debug) {
1101 PrintSettings(hdr, denow, stpnow, ekin, beta2, gamma, m_a, m_z, m_density,
1102 m_q, m_mass, emax, xi, rkappa);
1103 }
1104 double xinew = xi;
1105 double rknew = rkappa;
1106 if (m_model <= 0 || m_model > 4) {
1107 // No fluctuations: any step is permitted
1108 stpmin = stpnow;
1109 } else if (m_model == 1) {
1110 // Landau distribution
1111 const double xlmin = -3.;
1112 const double exponent = -xlmin - 1. + Gamma - beta2 - denow / xi;
1113 const double rklim = exponent < -expmax ? 0. : exp(exponent);
1114 stpmin = stpnow * (rklim / rkappa);
1115 if (m_debug) {
1116 std::cout << hdr << "Landau distribution is imposed.\n kappa_min = "
1117 << rklim << ", d_min = " << stpmin << " cm.\n";
1118 }
1119 } else if (m_model == 2) {
1120 // Vavilov distribution, ensure we're in range.
1121 const double xlmin = StepVavilov(rkappa);
1122 const double exponent = -xlmin - 1. + Gamma - beta2 - denow / xi;
1123 const double rklim = exponent < -expmax ? 0. : exp(exponent);
1124 stpmin = stpnow * (rklim / rkappa);
1125 xinew = fconst * m_q * m_q * m_z * m_density * stpmin / (m_a * beta2);
1126 rknew = xinew / emax;
1127 if (m_debug) {
1128 std::cout << hdr << "Vavilov distribution is imposed.\n kappa_min = "
1129 << rklim << ", d_min = " << stpmin << " cm\n kappa_new = "
1130 << rknew << ", xi_new = " << xinew << " MeV.\n";
1131 }
1132 if (stpmin > stpnow * 1.1) {
1133 if (m_debug) std::cout << hdr << "Step size increase. New pass.\n";
1134 retry = true;
1135 }
1136 } else if (m_model == 3) {
1137 // Gaussian model
1138 stpmin = stpnow * 16 * xi * emax * (1 - 0.5 * beta2) / (denow * denow);
1139 if (m_debug) {
1140 std::cout << hdr << "Gaussian distribution is imposed.\n";
1141 printf("\td_min = %g cm.\n\tsigma/mu_old = %g, sigma/mu_min = %g\n",
1142 stpmin, sqrt(xi * emax * (1 - 0.5 * beta2)) / de,
1143 sqrt((fconst * m_q * m_q * m_z * m_density * stpmin /
1144 (m_a * beta2)) *
1145 emax * (1 - 0.5 * beta2)) /
1146 (stpmin * denow / stpnow));
1147 }
1148 } else if (rkappa < 0.05) {
1149 // Combined model: for low kappa, use the Landau distribution.
1150 const double xlmin = -3.;
1151 const double exponent = -xlmin - 1. + Gamma - beta2 - denow / xi;
1152 const double rklim = exponent < -expmax ? 0. : exp(exponent);
1153 stpmin = stpnow * (rklim / rkappa);
1154 xinew = fconst * m_q * m_q * m_z * m_density * stpmin / (m_a * beta2);
1155 rknew = xinew / emax;
1156 if (m_debug) {
1157 std::cout << hdr << "Landau distribution automatic.\n kappa_min = "
1158 << rklim << ", d_min = " << stpmin << " cm.\n";
1159 }
1160 if (rknew > 0.05 || stpmin > stpnow * 1.1) {
1161 retry = true;
1162 if (m_debug) {
1163 std::cout << hdr << "Model change or step increase. New pass.\n";
1164 }
1165 }
1166 } else if (rkappa < 5) {
1167 // For medium kappa, use the Vavilov distribution
1168 const double xlmin = StepVavilov(rkappa);
1169 const double exponent = -xlmin - 1. + Gamma - beta2 - denow / xi;
1170 const double rklim = exponent < -expmax ? 0. : exp(exponent);
1171 stpmin = stpnow * (rklim / rkappa);
1172 xinew = fconst * m_q * m_q * m_z * m_density * stpmin / (m_a * beta2);
1173 rknew = xinew / emax;
1174 if (m_debug) {
1175 std::cout << hdr << "Vavilov distribution automatic.\n kappa_min = "
1176 << rklim << ", d_min = " << stpmin << " cm\n kappa_new = "
1177 << rknew << ", xi_new = " << xinew << " MeV.\n";
1178 }
1179 if (rknew > 5 || stpmin > stpnow * 1.1) {
1180 retry = true;
1181 if (m_debug) {
1182 std::cout << hdr << "Model change or step increase. New pass.\n";
1183 }
1184 }
1185 } else {
1186 // And for large kappa, use the Gaussian values.
1187 stpmin = stpnow * 16 * xi * emax * (1 - 0.5 * beta2) / (denow * denow);
1188 if (m_debug) {
1189 std::cout << hdr << "Gaussian distribution automatic.\n";
1190 printf("\td_min = %g cm.\n\tsigma/mu_old = %g, sigma/mu_min = %g\n",
1191 stpmin, sqrt(xi * emax * (1 - 0.5 * beta2)) / de,
1192 sqrt((fconst * m_q * m_q * m_z * m_density * stpmin /
1193 (m_a * beta2)) *
1194 emax * (1 - 0.5 * beta2)) /
1195 (stpmin * denow / stpnow));
1196 }
1197 }
1198 // See whether we should do another pass.
1199 if (stpnow > stpmin) {
1200 if (m_debug) {
1201 std::cout << hdr << "Step size ok, minimum: " << stpmin << " cm\n";
1202 }
1203 break;
1204 }
1205 if (!retry) {
1206 if (m_debug) {
1207 std::cerr << hdr << "\nStep size must be increased to "
1208 << stpmin << "cm.\n";
1209 }
1210 break;
1211 }
1212 // New iteration
1213 rkappa = rknew;
1214 xi = xinew;
1215 denow *= stpmin / stpnow;
1216 stpnow = stpmin;
1217 if (m_debug) std::cout << hdr << "Iteration " << iter << "\n";
1218 if (iter == nMaxIter - 1) {
1219 // Need interation, but ran out of tries
1220 std::cerr << hdr << "\n No convergence reached on step size.\n";
1221 }
1222 }
1223 return true;
1224}
1225
1226double TrackSrim::RndmEnergyLoss(const double ekin, const double de,
1227 const double step) const {
1228 // RNDDE - Generates a random energy loss.
1229 // VARIABLES : EKIN : Kinetic energy [MeV]
1230 // DE : Mean energy loss over the step [MeV]
1231 // STEP : Step length [cm]
1232 // BETA2 : Velocity-squared
1233 // GAMMA : Projectile gamma
1234 // EMAX : Maximum energy transfer per collision [MeV]
1235 // XI : Rutherford term [MeV]
1236 // FCONST : Proportionality constant
1237 // EMASS : Electron mass [MeV]
1238 // (Last changed on 26/10/07.)
1239
1240 const std::string hdr = "TrackSrim::RndmEnergyLoss: ";
1241 // Check correctness.
1242 if (ekin <= 0 || de <= 0 || step <= 0) {
1243 std::cerr << hdr << "\n Input parameters not valid.\n Ekin = "
1244 << ekin << " MeV, dE = " << de << " MeV, step length = "
1245 << step << " cm.\n";
1246 return 0.;
1247 } else if (m_mass <= 0 || fabs(m_q) <= 0) {
1248 std::cerr << hdr << "\n Track parameters not valid.\n Mass = "
1249 << m_mass << " MeV, charge = " << m_q << ".\n";
1250 return 0.;
1251 } else if (m_a <= 0 || m_z <= 0 || m_density <= 0) {
1252 std::cerr << hdr << "\n Material parameters not valid.\n A = " << m_a
1253 << ", Z = " << m_z << ", density = " << m_density << " g/cm3.\n";
1254 return 0.;
1255 }
1256 // Basic kinematic parameters
1257 const double rkin = 1.e6 * ekin / m_mass;
1258 const double gamma = 1. + rkin;
1259 const double beta2 = rkin > 1.e-5 ? 1. - 1. / (gamma * gamma) : 2. * rkin;
1260
1261 // Compute maximum energy transfer
1262 const double rm = ElectronMass / m_mass;
1263 const double emax = 2 * ElectronMass * 1.e-6 * beta2 * gamma * gamma /
1264 (1. + 2 * gamma * rm + rm * rm);
1265 // Compute the Rutherford term
1266 const double fconst = 0.1534;
1267 const double xi = fconst * m_q * m_q * m_z * m_density * step / (m_a * beta2);
1268 // Compute the scaling parameter
1269 const double rkappa = xi / emax;
1270 // Debugging output.
1271 if (m_debug) {
1272 PrintSettings(hdr, de, step, ekin, beta2, gamma, m_a, m_z, m_density, m_q,
1273 m_mass, emax, xi, rkappa);
1274 }
1275 double rndde = de;
1276 if (m_model <= 0 || m_model > 4) {
1277 // No fluctuations.
1278 if (m_debug) std::cout << hdr << "Fixed energy loss.\n";
1279 } else if (m_model == 1) {
1280 // Landau distribution
1281 if (m_debug) std::cout << hdr << "Landau imposed.\n";
1282 const double xlmean = -(log(rkappa) + beta2 + 1. - Gamma);
1283 rndde += xi * (RndmLandau() - xlmean);
1284 } else if (m_model == 2) {
1285 // Vavilov distribution, ensure we are in range.
1286 if (m_debug) std::cout << hdr << "Vavilov imposed.\n";
1287 if (rkappa > 0.01 && rkappa < 12) {
1288 const double xvav = RndmVavilov(rkappa, beta2);
1289 rndde += xi * (xvav + log(rkappa) + beta2 + (1 - Gamma));
1290 }
1291 } else if (m_model == 3) {
1292 // Gaussian model
1293 if (m_debug) std::cout << hdr << "Gaussian imposed.\n";
1294 rndde += RndmGaussian(0., sqrt(xi * emax * (1 - 0.5 * beta2)));
1295 } else if (rkappa < 0.05) {
1296 // Combined model: for low kappa, use the landau distribution.
1297 if (m_debug) std::cout << hdr << "Landau automatic.\n";
1298 const double xlmean = -(log(rkappa) + beta2 + (1 - Gamma));
1299 const double par[] = {0.50884, 1.26116, 0.0346688, 1.46314,
1300 0.15088e-2, 1.00324, -0.13049e-3};
1301 const double xlmax = par[0] + par[1] * xlmean + par[2] * xlmean * xlmean +
1302 par[6] * xlmean * xlmean * xlmean +
1303 (par[3] + xlmean * par[4]) * exp(par[5] * xlmean);
1304 double xlan = RndmLandau();
1305 for (unsigned int iter = 0; iter < 100; ++iter) {
1306 if (xlan < xlmax) break;
1307 xlan = RndmLandau();
1308 }
1309 rndde += xi * (xlan - xlmean);
1310 } else if (rkappa < 5) {
1311 // For medium kappa, use the Vavilov distribution, precise
1312 // } else if (m_precisevavilov && rkappa < 5) {
1313 // printf("Vavilov slow automatic\n");
1314 // rndde = de+xi*(rndvvl(rkappa,beta2) + log(xi/emax)+beta2+(1-Gamma));
1315 // // ... or fast.
1316 if (m_debug) std::cout << hdr << "Vavilov fast automatic.\n";
1317 const double xvav = RndmVavilov(rkappa, beta2);
1318 rndde += xi * (xvav + log(rkappa) + beta2 + (1 - Gamma));
1319 } else {
1320 // And for large kappa, use the Gaussian values.
1321 if (m_debug) std::cout << hdr << "Gaussian automatic.\n";
1322 rndde = RndmGaussian(de, sqrt(xi * emax * (1 - 0.5 * beta2)));
1323 }
1324 // Debugging output
1325 if (m_debug)
1326 std::cout << hdr << "Energy loss generated = " << rndde << " MeV.\n";
1327 return rndde;
1328}
1329
1330bool TrackSrim::GetCluster(double& xcls, double& ycls, double& zcls,
1331 double& tcls, int& n, double& e, double& extra) {
1332 if (m_debug) {
1333 printf("Current cluster: %d, array size: %ld",
1334 m_currcluster, m_clusters.size());
1335 }
1336 // Stop if we have exhausted the list of clusters.
1337 if (m_currcluster >= m_clusters.size()) return false;
1338
1339 xcls = m_clusters[m_currcluster].x;
1340 ycls = m_clusters[m_currcluster].y;
1341 zcls = m_clusters[m_currcluster].z;
1342 tcls = m_clusters[m_currcluster].t;
1343
1344 n = m_clusters[m_currcluster].electrons;
1345 e = m_clusters[m_currcluster].ec;
1346 extra = m_clusters[m_currcluster].kinetic;
1347 // Move to next cluster
1348 ++m_currcluster;
1349 return true;
1350}
1351
1352}
Abstract base class for media.
Definition: Medium.hh:11
bool IsIonisable() const
Definition: Medium.hh:59
bool IsInArea(const double x, const double y, const double z)
Check if a point is inside the user area.
Definition: Sensor.cc:264
bool GetMedium(const double x, const double y, const double z, Medium *&medium)
Get the medium at (x, y, z).
Definition: Sensor.cc:150
bool GetArea(double &xmin, double &ymin, double &zmin, double &xmax, double &ymax, double &zmax)
Return the current user area.
Definition: Sensor.cc:237
std::vector< double > m_longstraggle
Longitudinal straggling [cm].
Definition: TrackSrim.hh:106
double RndmEnergyLoss(const double ekin, const double de, const double step) const
Definition: TrackSrim.cc:1226
double m_q
Charge of ion.
Definition: TrackSrim.hh:86
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: TrackSrim.cc:698
unsigned int m_currcluster
Index of the next cluster to be returned.
Definition: TrackSrim.hh:109
std::vector< double > m_hdloss
Hadronic energy loss [MeV cm2/g].
Definition: TrackSrim.hh:100
void SetDensity(const double density)
Set/get the density [g/cm3] of the target medium.
Definition: TrackSrim.hh:26
std::vector< double > m_range
Projected range [cm].
Definition: TrackSrim.hh:102
double DedxHD(const double e) const
Definition: TrackSrim.cc:517
std::vector< cluster > m_clusters
Definition: TrackSrim.hh:121
std::vector< double > m_emloss
EM energy loss [MeV cm2/g].
Definition: TrackSrim.hh:98
virtual bool GetCluster(double &xcls, double &ycls, double &zcls, double &tcls, int &n, double &e, double &extra)
Definition: TrackSrim.cc:1330
double m_mass
Mass of ion [MeV].
Definition: TrackSrim.hh:88
bool m_chargeset
Charge gas been defined.
Definition: TrackSrim.hh:78
bool SmallestStep(double ekin, double de, double step, double &stpmin)
Definition: TrackSrim.cc:1052
double m_fano
Fano factor [-].
Definition: TrackSrim.hh:84
TrackSrim()
Constructor.
Definition: TrackSrim.cc:122
bool PreciseLoss(const double step, const double estart, double &deem, double &dehd) const
Definition: TrackSrim.cc:521
double m_work
Work function [eV].
Definition: TrackSrim.hh:82
unsigned int m_model
Definition: TrackSrim.hh:112
double m_density
Density [g/cm3].
Definition: TrackSrim.hh:80
bool m_useTransStraggle
Include transverse straggling.
Definition: TrackSrim.hh:73
int m_nsize
Targeted cluster size.
Definition: TrackSrim.hh:114
std::vector< double > m_ekin
Energy in energy loss table [MeV].
Definition: TrackSrim.hh:96
std::vector< double > m_transstraggle
Transverse straggling [cm].
Definition: TrackSrim.hh:104
bool EstimateRange(const double ekin, const double step, double &stpmax)
Definition: TrackSrim.cc:602
bool m_useLongStraggle
Include longitudinal straggling.
Definition: TrackSrim.hh:75
double m_a
A and Z of the gas.
Definition: TrackSrim.hh:90
int m_maxclusters
Maximum number of clusters allowed (infinite if 0)
Definition: TrackSrim.hh:94
double DedxEM(const double e) const
Definition: TrackSrim.cc:513
bool ReadFile(const std::string &file)
Definition: TrackSrim.cc:140
Abstract base class for track generation.
Definition: Track.hh:14
double GetKineticEnergy() const
Definition: Track.hh:43
Sensor * m_sensor
Definition: Track.hh:90
bool m_debug
Definition: Track.hh:97
std::string m_className
Definition: Track.hh:80
double m_energy
Definition: Track.hh:85
double Divdif(const std::vector< double > &f, const std::vector< double > &a, int nn, double x, int mm)
Definition: Numerics.cc:650
double RndmHeedWF(const double w, const double f)
Definition: Random.cc:659
double RndmVavilov(const double rkappa, const double beta2)
Draw a random number from a Vavilov distribution.
Definition: Random.cc:283
void RndmDirection(double &dx, double &dy, double &dz, const double length=1.)
Draw a random (isotropic) direction vector.
Definition: Random.hh:106
double RndmGaussian()
Draw a Gaussian random variate with mean zero and standard deviation one.
Definition: Random.hh:25
double RndmLandau()
Draw a random number from a Landau distribution.
Definition: Random.cc:87