21double StepVavilov(
const double rkappa) {
25 }
else if (rkappa < 1) {
27 }
else if (rkappa < 2) {
29 }
else if (rkappa < 3) {
31 }
else if (rkappa < 4) {
33 }
else if (rkappa < 5) {
35 }
else if (rkappa < 6) {
37 }
else if (rkappa < 7) {
39 }
else if (rkappa < 8) {
45double Interpolate(
const double x,
const std::vector<double>& xtab,
46 const std::vector<double>& ytab) {
49 }
else if (x > xtab.back()) {
55void PrintSettings(
const std::string& hdr,
const double de,
const double step,
56 const double ekin,
const double beta2,
const double gamma,
57 const double agas,
const double zgas,
const double density,
58 const double qp,
const double mp,
const double emax,
59 const double xi,
const double kappa) {
60 std::cout << hdr <<
"Settings:\n"
61 <<
" dE = " << de <<
" MeV,\n"
62 <<
" step = " << step <<
" cm.\n"
63 <<
" Ekin = " << ekin <<
" MeV,\n"
64 <<
" beta2 = " << beta2 <<
",\n"
65 <<
" gamma = " << gamma <<
".\n"
66 <<
" Agas = " << agas <<
", Zgas = " << zgas <<
",\n"
67 <<
" density = " << density <<
" g/cm3.\n"
68 <<
" Qpart = " << qp <<
", mpart = " << 1.e-6 * mp <<
" MeV.\n"
69 <<
" Emax = " << emax <<
" MeV,\n"
70 <<
" xi = " << xi <<
" MeV,\n"
71 <<
" kappa = " << kappa <<
".\n";
82 const std::string hdr =
m_className +
"::ReadFile:\n ";
85 fsrim.open(file.c_str(), std::ios::in);
87 std::cerr << hdr <<
"Could not open SRIM file " << file
88 <<
" for reading.\n The file perhaps does not exist.\n";
91 unsigned int nread = 0;
95 std::cout << hdr <<
"SRIM header records from file " << file <<
"\n";
97 constexpr size_t size = 100;
99 while (fsrim.getline(line, size,
'\n')) {
101 if (strstr(line,
"SRIM version") != NULL) {
102 if (
m_debug) std::cout <<
"\t" << line <<
"\n";
103 }
else if (strstr(line,
"Calc. date") != NULL) {
104 if (
m_debug) std::cout <<
"\t" << line <<
"\n";
105 }
else if (strstr(line,
"Ion =") != NULL) {
112 token = strtok(line,
" []=");
113 token = strtok(NULL,
" []=");
114 token = strtok(NULL,
" []=");
116 m_qion = std::atof(token);
118 token = strtok(NULL,
" []=");
119 token = strtok(NULL,
" []=");
120 token = strtok(NULL,
" []=");
122 m_mion = std::atof(token) * AtomicMassUnitElectronVolt;
125 if (!fsrim.getline(line, size,
'\n')) {
126 std::cerr << hdr <<
"Premature EOF looking for target density (line "
131 if (!fsrim.getline(line, size,
'\n')) {
132 std::cerr << hdr <<
"Premature EOF looking for target density (line "
137 const bool pre2013 = (strstr(line,
"Target Density") != NULL);
138 token = strtok(line,
" ");
139 token = strtok(NULL,
" ");
140 token = strtok(NULL,
" ");
141 if (pre2013) token = strtok(NULL,
" ");
145 while (fsrim.getline(line, size,
'\n')) {
147 if (strstr(line,
"Stopping Units") == NULL)
continue;
148 if (strstr(line,
"Stopping Units = MeV / (mg/cm2)") != NULL ||
149 strstr(line,
"Stopping Units = MeV/(mg/cm2)") != NULL) {
151 std::cout << hdr <<
"Stopping units: MeV / (mg/cm2) as expected.\n";
155 std::cerr << hdr <<
"Unknown stopping units. Aborting (line " << nread
161 while (fsrim.getline(line, size,
'\n')) {
163 if (strstr(line,
"-----------") != NULL)
break;
173 unsigned int ntable = 0;
174 while (fsrim.getline(line, size,
'\n')) {
176 if (strstr(line,
"-----------") != NULL)
break;
178 token = strtok(line,
" ");
179 m_ekin.push_back(atof(token));
180 token = strtok(NULL,
" ");
181 if (strcmp(token,
"eV") == 0) {
183 }
else if (strcmp(token,
"keV") == 0) {
185 }
else if (strcmp(token,
"GeV") == 0) {
187 }
else if (strcmp(token,
"MeV") != 0) {
188 std::cerr << hdr <<
"Unknown energy unit " << token <<
"; aborting\n";
192 token = strtok(NULL,
" ");
195 token = strtok(NULL,
" ");
198 token = strtok(NULL,
" ");
199 m_range.push_back(atof(token));
200 token = strtok(NULL,
" ");
201 if (strcmp(token,
"A") == 0) {
203 }
else if (strcmp(token,
"um") == 0) {
205 }
else if (strcmp(token,
"mm") == 0) {
207 }
else if (strcmp(token,
"m") == 0) {
209 }
else if (strcmp(token,
"km") == 0) {
211 }
else if (strcmp(token,
"cm") != 0) {
212 std::cerr << hdr <<
"Unknown distance unit " << token <<
"; aborting\n";
216 token = strtok(NULL,
" ");
218 token = strtok(NULL,
" ");
219 if (strcmp(token,
"A") == 0) {
221 }
else if (strcmp(token,
"um") == 0) {
223 }
else if (strcmp(token,
"mm") == 0) {
225 }
else if (strcmp(token,
"m") == 0) {
227 }
else if (strcmp(token,
"km") == 0) {
229 }
else if (strcmp(token,
"cm") != 0) {
230 std::cerr << hdr <<
"Unknown distance unit " << token <<
"; aborting\n";
234 token = strtok(NULL,
" ");
236 token = strtok(NULL,
" ");
237 if (strcmp(token,
"A") == 0) {
239 }
else if (strcmp(token,
"um") == 0) {
241 }
else if (strcmp(token,
"mm") == 0) {
243 }
else if (strcmp(token,
"m") == 0) {
245 }
else if (strcmp(token,
"km") == 0) {
247 }
else if (strcmp(token,
"cm") != 0) {
248 std::cerr << hdr <<
"Unknown distance unit " << token <<
"; aborting\n";
258 while (fsrim.getline(line, size,
'\n')) {
260 if (strstr(line,
"=============") != NULL) {
262 }
else if (strstr(line,
"MeV / (mg/cm2)") != NULL ||
263 strstr(line,
"MeV/(mg/cm2)") != NULL) {
264 token = strtok(line,
" ");
265 scale = std::atof(token);
269 std::cerr << hdr <<
"Did not find stopping unit scaling; aborting.\n";
273 for (
unsigned int i = 0; i < ntable; ++i) {
280 std::cout << hdr <<
"Successfully read " << file <<
"(" << nread
287 std::cout <<
"TrackSrim::Print:\n SRIM energy loss table\n\n"
288 <<
" Energy EM Loss HD loss Range "
289 <<
"l straggle t straggle\n"
290 <<
" [MeV] [MeV/cm] [MeV/cm] [cm] "
292 const unsigned int nPoints =
m_emloss.size();
293 for (
unsigned int i = 0; i < nPoints; ++i) {
294 printf(
"%10g %10g %10g %10g %10g %10g\n",
m_ekin[i],
299 printf(
" Work function: %g eV\n",
m_work);
300 printf(
" Fano factor: %g\n",
m_fano);
301 printf(
" Ion charge: %g\n",
m_qion);
302 printf(
" Mass: %g MeV\n", 1.e-6 *
m_mion);
303 printf(
" Density: %g g/cm3\n",
m_density);
304 printf(
" A, Z: %g, %g\n",
m_a,
m_z);
309 const unsigned int nPoints =
m_ekin.size();
310 std::vector<double> yE;
311 std::vector<double> yH;
312 std::vector<double> yT;
313 for (
unsigned int i = 0; i < nPoints; ++i) {
318 yT.push_back(em + hd);
320 const double xmin = *std::min_element(std::begin(
m_ekin), std::end(
m_ekin));
321 const double xmax = *std::max_element(std::begin(
m_ekin), std::end(
m_ekin));
322 const double ymax = *std::max_element(std::begin(yT), std::end(yT));
325 TCanvas* celoss =
new TCanvas(name.c_str(),
"Energy loss");
329 celoss->DrawFrame(xmin, 0., xmax, 1.05 * ymax,
";Ion energy [MeV];Energy loss [MeV/cm]");
333 gr.SetLineStyle(kSolid);
335 gr.SetMarkerStyle(21);
336 gr.SetLineColor(kBlue + 1);
337 gr.SetMarkerColor(kBlue + 1);
338 gr.DrawGraph(nPoints,
m_ekin.data(), yE.data(),
"plsame");
340 gr.SetLineColor(kGreen + 2);
341 gr.SetMarkerColor(kGreen + 2);
342 gr.DrawGraph(nPoints,
m_ekin.data(), yH.data(),
"plsame");
344 gr.SetLineColor(kOrange - 3);
345 gr.SetMarkerColor(kOrange - 3);
346 gr.DrawGraph(nPoints,
m_ekin.data(), yT.data(),
"plsame");
349 double xLabel = 0.4 * xmax;
350 double yLabel = 0.9 * ymax;
351 label.SetTextColor(kBlue + 1);
352 label.SetText(xLabel, yLabel,
"EM energy loss");
353 label.DrawLatex(xLabel, yLabel,
"EM energy loss");
354 yLabel -= 1.5 * label.GetYsize();
355 label.SetTextColor(kGreen + 2);
356 label.DrawLatex(xLabel, yLabel,
"HD energy loss");
357 yLabel -= 1.5 * label.GetYsize();
358 label.SetTextColor(kOrange - 3);
359 label.DrawLatex(xLabel, yLabel,
"Total energy loss");
365 const double xmin = *std::min_element(std::begin(
m_ekin), std::end(
m_ekin));
366 const double xmax = *std::max_element(std::begin(
m_ekin), std::end(
m_ekin));
367 const double ymax = *std::max_element(std::begin(
m_range), std::end(
m_range));
371 TCanvas* crange =
new TCanvas(name.c_str(),
"Range");
375 crange->DrawFrame(xmin, 0., xmax, 1.05 * ymax,
";Ion energy [MeV];Projected range [cm]");
378 gr.SetLineColor(kOrange - 3);
379 gr.SetMarkerColor(kOrange - 3);
380 gr.SetLineStyle(kSolid);
382 gr.SetMarkerStyle(21);
389 const double xmin = *std::min_element(std::begin(
m_ekin), std::end(
m_ekin));
390 const double xmax = *std::max_element(std::begin(
m_ekin), std::end(
m_ekin));
391 const double ymax = std::max(*std::max_element(std::begin(
m_longstraggle),
397 TCanvas* cstraggle =
new TCanvas(name.c_str(),
"Straggling");
398 cstraggle->SetLogx();
399 cstraggle->SetGridx();
400 cstraggle->SetGridy();
401 cstraggle->DrawFrame(xmin, 0., xmax, 1.05 * ymax,
";Ion energy [MeV];Straggling [cm]");
404 const unsigned int nPoints =
m_ekin.size();
406 gr.SetLineStyle(kSolid);
408 gr.SetMarkerStyle(21);
410 gr.SetLineColor(kOrange - 3);
411 gr.SetMarkerColor(kOrange - 3);
414 gr.SetLineColor(kGreen + 2);
415 gr.SetMarkerColor(kGreen + 2);
419 double xLabel = 1.2 * xmin;
420 double yLabel = 0.9 * ymax;
421 label.SetTextColor(kOrange - 3);
422 label.SetText(xLabel, yLabel,
"Longitudinal");
423 label.DrawLatex(xLabel, yLabel,
"Longitudinal");
424 yLabel -= 1.5 * label.GetYsize();
425 label.SetTextColor(kGreen + 2);
426 label.DrawLatex(xLabel, yLabel,
"Transverse");
440 constexpr double fconst = 1.e-6 * TwoPi * (
441 FineStructureConstant * FineStructureConstant * HbarC * HbarC) /
442 (ElectronMass * AtomicMassUnit);
447 double& deem,
double& dehd)
const {
450 const std::string hdr =
m_className +
"::PreciseLoss: ";
453 std::cout << hdr <<
"\n"
454 <<
" Initial energy: " << estart <<
" MeV\n"
455 <<
" Step: " << step <<
" cm\n";
458 const double eps = 1.0e-2;
460 unsigned int ndiv = 1;
462 const unsigned int nMaxIter = 10;
463 bool converged =
false;
464 for (
unsigned int iter = 0; iter < nMaxIter; ++iter) {
470 const double s =
m_density * step / ndiv;
471 for (
unsigned int i = 0; i < ndiv; i++) {
475 const double em22 = s *
DedxEM(e2 - 0.5 * de21);
476 const double hd22 = s *
DedxHD(e2 - 0.5 * de21);
480 const double em41 = s *
DedxEM(e4);
481 const double hd41 = s *
DedxHD(e4);
482 const double de41 = em41 + hd41;
484 const double em42 = s *
DedxEM(e4 - 0.5 * de41);
485 const double hd42 = s *
DedxHD(e4 - 0.5 * de41);
486 const double de42 = em42 + hd42;
488 const double em43 = s *
DedxEM(e4 - 0.5 * de42);
489 const double hd43 = s *
DedxHD(e4 - 0.5 * de42);
490 const double de43 = em43 + hd43;
492 const double em44 = s *
DedxEM(e4 - de43);
493 const double hd44 = s *
DedxHD(e4 - de43);
494 const double de44 = em44 + hd44;
496 deem += (em41 + em44) / 6. + (em42 + em43) / 3.;
497 dehd += (hd41 + hd44) / 6. + (hd42 + hd43) / 3.;
499 e4 -= (de41 + de44) / 6. + (de42 + de43) / 3.;
502 std::cout << hdr <<
"\n Iteration " << iter <<
" has " << ndiv
503 <<
" division(s). Losses:\n";
504 printf(
"\tde4 = %12g, de2 = %12g MeV\n", estart - e2, estart - e4);
505 printf(
"\tem4 = %12g, hd4 = %12g MeV\n", deem, dehd);
508 if (fabs(e2 - e4) > eps * (fabs(e2) + fabs(e4) + fabs(estart))) {
518 std::cerr << hdr <<
"No convergence achieved integrating energy loss.\n";
520 std::cout << hdr <<
"Convergence at eps = " << eps <<
"\n";
533 const std::string hdr =
m_className +
"::EstimateRange: ";
539 double deem = 0., dehd = 0.;
541 double de1 = deem + dehd;
544 if (
m_debug) std::cout << hdr <<
"Initial step OK.\n";
548 double st2 = 0.5 * step;
550 const unsigned int nMaxIter = 20;
551 for (
unsigned int iter = 0; iter < nMaxIter; ++iter) {
556 if (de2 < ekin)
break;
563 std::cerr << hdr <<
"\n Did not find a smaller step in " << nMaxIter
564 <<
" iterations. Abandoned.\n";
565 stpmax = 0.5 * (st1 + st2);
569 printf(
"\tstep 1 = %g cm, de 1 = %g MeV\n\tstep 2 = %g cm, de 2 = %g MeV\n",
570 st1, de1 - ekin, st2, de2 - ekin);
573 for (
unsigned int iter = 0; iter < nMaxIter; ++iter) {
577 std::cerr << hdr <<
"Bisection failed due to equal energy loss for "
578 <<
"two step sizes. Abandoned.\n";
580 stpmax = 0.5 * (st1 + st2);
585 if ((fabs(de1 - ekin) < 0.01 * fabs(de2 - de1)) ||
586 (fabs(de1 - ekin) > 0.99 * fabs(de2 - de1))) {
587 st3 = 0.5 * (st1 + st2);
589 st3 = st1 - (st2 - st1) * (de1 - ekin) / (de2 - de1);
593 const double de3 = deem + dehd;
595 std::printf(
"\tStep 1 = %g cm, dE 1 = %g MeV\n", st1, de1 - ekin);
596 std::printf(
"\tStep 2 = %g cm, dE 2 = %g MeV\n", st2, de2 - ekin);
597 std::printf(
"\tStep 3 = %g cm, dE 3 = %g MeV\n", st3, de3 - ekin);
608 if (fabs(de3 - ekin) < 1e-3 * (fabs(de3) + fabs(ekin)) ||
609 fabs(st1 - st2) < 1e-3 * (fabs(st1) + fabs(st2))) {
610 stpmax = st1 - (st2 - st1) * (de1 - ekin) / (de2 - de1);
615 std::cout << hdr <<
"Bisection did not converge in " << nMaxIter
616 <<
" steps. Abandoned.\n";
618 stpmax = st1 - (st2 - st1) * (de1 - ekin) / (de2 - de1);
623 const double t0,
const double dx0,
const double dy0,
627 const std::string hdr =
m_className +
"::NewTrack: ";
631 std::cerr << hdr <<
"Sensor is not defined.\n";
636 double xmin = 0., ymin = 0., zmin = 0.;
637 double xmax = 0., ymax = 0., zmax = 0.;
639 std::cerr << hdr <<
"Drift area is not set.\n";
641 }
else if (x0 < xmin || x0 > xmax || y0 < ymin || y0 > ymax || z0 < zmin ||
643 std::cerr << hdr <<
"Initial position outside bounding box.\n";
650 std::cerr << hdr <<
"No medium at initial position.\n";
653 std::cerr << hdr <<
"Medium at initial position is not ionisable.\n";
658 const double normdir = sqrt(dx0 * dx0 + dy0 * dy0 + dz0 * dz0);
662 if (normdir < Small) {
664 std::cout << hdr <<
"Direction vector has zero norm.\n"
665 <<
" Initial direction is randomized.\n";
678 std::cerr << hdr <<
"Particle mass not set.\n";
681 std::cerr << hdr <<
"Particle charge not set.\n";
684 std::cerr << hdr <<
"Initial particle energy not set.\n";
686 }
else if (
m_work < Small) {
687 std::cerr << hdr <<
"Work function not set.\n";
689 }
else if (
m_a < Small ||
m_z < Small) {
690 std::cerr << hdr <<
"A and/or Z not set.\n";
695 if (ekin0 < 1.e-14 *
m_mion || ekin0 < 1.e-6 *
m_work) {
696 std::cerr << hdr <<
"Initial kinetic energy E = " << ekin0
697 <<
" MeV such that beta2 = 0 or E << W; particle stopped.\n";
702 const double tracklength = 10 * Interpolate(ekin0,
m_ekin,
m_range);
706 std::cout << hdr <<
"Track generation with the following parameters:\n";
707 const unsigned int nTable =
m_ekin.size();
708 printf(
" Table size %u\n", nTable);
709 printf(
" Particle kin. energy %g MeV\n", ekin0);
710 printf(
" Particle mass %g MeV\n", 1.e-6 *
m_mion);
711 printf(
" Particle charge %g\n",
m_qion);
712 printf(
" Work function %g eV\n",
m_work);
714 printf(
" Fano factor %g\n",
m_fano);
716 std::cout <<
" Fano factor Not set\n";
720 printf(
" Cluster size %d\n",
m_nsize);
757 std::cout << hdr <<
"\n Energy = " << e
758 <<
" MeV,\n dEdx em, hd = " << dedxem <<
", " << dedxhd
759 <<
" MeV/cm,\n e-/cm = " << 1.e6 * dedxem /
m_work
760 <<
".\n Straggling long/lat: " << strlon <<
", " << strlat
769 step = ekin0 / (ncls * (dedxem + dedxhd));
774 double deem = 0., dehd = 0.;
778 if (deem + dehd > e) {
782 deem = e * deem / (dehd + deem);
785 if (
m_debug) std::cout << hdr <<
"Finish raised. Track length reached.\n";
787 stpmax = tracklength - dsum;
790 std::cout << hdr <<
"Maximum step size set to " << stpmax <<
" cm.\n";
795 std::cerr << hdr <<
"Failure computing the minimum step size."
796 <<
"\n Clustering abandoned.\n";
801 if (stpmin > stpmax) {
803 if (
m_debug) std::cout << hdr <<
"stpmin > stpmax. Deposit all energy.\n";
805 if (e - eloss - dehd < 0) eloss = e - dehd;
807 if (
m_debug) std::cout << hdr <<
"Finish raised. Single deposit.\n";
808 }
else if (step < stpmin) {
810 if (
m_debug) std::cout << hdr <<
"Enlarging step size.\n";
813 if (deem + dehd > e) {
814 if (
m_debug) std::cout << hdr <<
"Excess loss. Recomputing stpmax.\n";
818 deem = e * deem / (dehd + deem);
826 if (
m_debug) std::cout << hdr <<
"Using existing step size.\n";
831 if (
m_debug) std::cout << hdr <<
"Truncating negative energy loss.\n";
833 }
else if (eloss > e - dehd) {
834 if (
m_debug) std::cout << hdr <<
"Excess energy loss, using mean.\n";
836 if (e - eloss - dehd < 0) {
839 if (
m_debug) std::cout << hdr <<
"Finish raised. Using mean energy.\n";
843 std::cout << hdr <<
"Step length = " << step <<
" cm.\n "
844 <<
"Mean loss = " << deem <<
" MeV.\n "
845 <<
"Actual loss = " << eloss <<
" MeV.\n";
851 std::cout << hdr <<
"No medium at position (" << x <<
"," << y <<
","
857 std::cout << hdr <<
"Medium at (" << x <<
"," << y <<
"," << z
858 <<
") is not ionisable.\n";
863 std::cout << hdr <<
"Cluster at (" << x <<
"," << y <<
"," << z
864 <<
") outside bounding box.\n";
879 double ecl = 1.e6 * (eloss + epool);
885 if (ernd1 > ecl)
break;
891 std::cout << hdr <<
"EM + pool: " << 1.e6 * (eloss + epool)
893 <<
" eV, E/w: " << (eloss + epool) / (1.0e-6 *
m_work)
894 <<
", n: " << cluster.
electrons <<
".\n";
898 epool += eloss - 1.e-6 * cluster.
ec;
900 std::cout << hdr <<
"Cluster " <<
m_clusters.size() <<
"\n at ("
901 << cluster.
x <<
", " << cluster.
y <<
", " << cluster.
z
902 <<
"),\n e = " << cluster.
ec
904 <<
",\n pool = " << epool <<
" MeV.\n";
914 if (
m_debug) std::cout << hdr <<
"Finishing flag raised.\n";
916 }
else if (e < ekin0 * 1.e-9) {
918 if (
m_debug) std::cout << hdr <<
"Energy exhausted.\n";
922 const double scale = sqrt(step / prange);
927 std::cout << hdr <<
"sigma l, t1, t2: " << sigl <<
", " << sigt1 <<
", "
931 if (xdir * xdir + zdir * zdir <= 0) {
934 }
else if (ydir > 0) {
937 std::cerr << hdr <<
"Zero step length; clustering abandoned.\n";
942 phi = atan2(xdir, zdir);
943 theta = atan2(ydir, sqrt(xdir * xdir + zdir * zdir));
947 const double cp = cos(phi);
948 const double ct = cos(theta);
949 const double sp = sin(phi);
950 const double st = sin(theta);
951 x += step * xdir + cp * sigt1 - sp * st * sigt2 + sp * ct * sigl;
952 y += step * ydir + ct * sigt2 + st * sigl;
953 z += step * zdir - sp * sigt1 - cp * st * sigt2 + cp * ct * sigl;
955 const double rk = 1.e6 * e /
m_mion;
956 const double gamma = 1. + rk;
957 const double beta2 = rk > 1.e-5 ? 1. - 1. / (gamma * gamma) : 2. * rk;
958 const double vmag = sqrt(beta2) * SpeedOfLight;
959 if (vmag > 0.) t += step / vmag;
962 xdir = step * xdir + cp * sigt1 - sp * st * sigt2 + sp * ct * sigl;
963 ydir = step * ydir + ct * sigt2 + st * sigl;
964 zdir = step * zdir - sp * sigt1 - cp * st * sigt2 + cp * ct * sigl;
965 double dnorm = sqrt(xdir * xdir + ydir * ydir + zdir * zdir);
967 std::cerr << hdr <<
"Zero step length; clustering abandoned.\n";
978 std::cerr << hdr <<
"Exceeded maximum number of clusters.\n";
990 const std::string hdr =
m_className +
"::SmallestStep: ";
991 constexpr double expmax = 30;
996 if (ekin <= 0 || de <= 0 || step <= 0) {
997 std::cerr << hdr <<
"Input parameters not valid.\n Ekin = " << ekin
998 <<
" MeV, dE = " << de <<
" MeV, step length = " << step
1003 <<
"Track parameters not valid.\n Mass = " << 1.e-6 *
m_mion
1004 <<
" MeV, charge = " <<
m_qion <<
".\n";
1007 std::cerr << hdr <<
"Gas parameters not valid.\n A = " <<
m_a
1008 <<
", Z = " <<
m_z <<
" density = " <<
m_density <<
" g/cm3.\n";
1013 const double rkin = 1.e6 * ekin /
m_mion;
1014 const double gamma = 1. + rkin;
1015 const double beta2 = rkin > 1.e-5 ? 1. - 1. / (gamma * gamma) : 2. * rkin;
1018 const double rm = ElectronMass /
m_mion;
1019 const double emax = 2 * ElectronMass * 1.e-6 * beta2 * gamma * gamma /
1020 (1. + 2 * gamma * rm + rm * rm);
1022 double xi =
Xi(step, beta2);
1024 double rkappa = xi / emax;
1027 double stpnow = step;
1028 constexpr unsigned int nMaxIter = 10;
1029 for (
unsigned int iter = 0; iter < nMaxIter; ++iter) {
1033 PrintSettings(hdr, denow, stpnow, ekin, beta2, gamma,
m_a,
m_z,
m_density,
1037 double rknew = rkappa;
1038 if (m_model <= 0 || m_model > 4) {
1043 constexpr double xlmin = -3.;
1044 const double exponent = -xlmin - 1. + Gamma - beta2 - denow / xi;
1045 const double rklim = exponent < -expmax ? 0. : exp(exponent);
1046 stpmin = stpnow * (rklim / rkappa);
1048 std::cout << hdr <<
"Landau distribution is imposed.\n kappa_min = "
1049 << rklim <<
", d_min = " << stpmin <<
" cm.\n";
1053 const double xlmin = StepVavilov(rkappa);
1054 const double exponent = -xlmin - 1. + Gamma - beta2 - denow / xi;
1055 const double rklim = exponent < -expmax ? 0. : exp(exponent);
1056 stpmin = stpnow * (rklim / rkappa);
1057 xinew =
Xi(stpmin, beta2);
1058 rknew = xinew / emax;
1060 std::cout << hdr <<
"Vavilov distribution is imposed.\n kappa_min = "
1061 << rklim <<
", d_min = " << stpmin
1062 <<
" cm\n kappa_new = " << rknew <<
", xi_new = " << xinew
1065 if (stpmin > stpnow * 1.1) {
1066 if (
m_debug) std::cout << hdr <<
"Step size increase. New pass.\n";
1071 const double sigma2 = xi * emax * (1 - 0.5 * beta2);
1072 stpmin = stpnow * 16 * sigma2 / (denow * denow);
1074 const double sigmaMin2 =
Xi(stpmin, beta2) * emax * (1 - 0.5 * beta2);
1075 std::cout << hdr <<
"Gaussian distribution is imposed.\n "
1076 <<
"d_min = " << stpmin <<
" cm.\n sigma/mu (old) = "
1077 << sqrt(sigma2) / de <<
",\n sigma/mu (min) = "
1078 << sqrt(sigmaMin2) / (stpmin * denow / stpnow) <<
"\n";
1080 }
else if (rkappa < 0.05) {
1082 constexpr double xlmin = -3.;
1083 const double exponent = -xlmin - 1. + Gamma - beta2 - denow / xi;
1084 const double rklim = exponent < -expmax ? 0. : exp(exponent);
1085 stpmin = stpnow * (rklim / rkappa);
1086 xinew =
Xi(stpmin, beta2);
1087 rknew = xinew / emax;
1089 std::cout << hdr <<
"Landau distribution automatic.\n kappa_min = "
1090 << rklim <<
", d_min = " << stpmin <<
" cm.\n";
1092 if (rknew > 0.05 || stpmin > stpnow * 1.1) {
1095 std::cout << hdr <<
"Model change or step increase. New pass.\n";
1098 }
else if (rkappa < 5) {
1100 const double xlmin = StepVavilov(rkappa);
1101 const double exponent = -xlmin - 1. + Gamma - beta2 - denow / xi;
1102 const double rklim = exponent < -expmax ? 0. : exp(exponent);
1103 stpmin = stpnow * (rklim / rkappa);
1104 xinew =
Xi(stpmin, beta2);
1105 rknew = xinew / emax;
1107 std::cout << hdr <<
"Vavilov distribution automatic.\n kappa_min = "
1108 << rklim <<
", d_min = " << stpmin <<
" cm\n kappa_new = "
1109 << rknew <<
", xi_new = " << xinew <<
" MeV.\n";
1111 if (rknew > 5 || stpmin > stpnow * 1.1) {
1114 std::cout << hdr <<
"Model change or step increase. New pass.\n";
1119 const double sigma2 = xi * emax * (1 - 0.5 * beta2);
1120 stpmin = stpnow * 16 * sigma2 / (denow * denow);
1122 const double sigmaMin2 =
Xi(stpmin, beta2) * emax * (1 - 0.5 * beta2);
1123 std::cout << hdr <<
"Gaussian distribution automatic.\n "
1124 <<
"d_min = " << stpmin <<
" cm.\n sigma/mu (old) = "
1125 << sqrt(sigma2) / de <<
",\n sigma/mu (min) = "
1126 << sqrt(sigmaMin2) / (stpmin * denow / stpnow) <<
"\n";
1130 if (stpnow > stpmin) {
1132 std::cout << hdr <<
"Step size ok, minimum: " << stpmin <<
" cm\n";
1138 std::cerr << hdr <<
"Step size must be increased to " << stpmin
1146 denow *= stpmin / stpnow;
1148 if (
m_debug) std::cout << hdr <<
"Iteration " << iter <<
"\n";
1149 if (iter == nMaxIter - 1) {
1151 std::cerr << hdr <<
"No convergence reached on step size.\n";
1158 const double step)
const {
1170 const std::string hdr =
"TrackSrim::RndmEnergyLoss: ";
1172 if (ekin <= 0 || de <= 0 || step <= 0) {
1173 std::cerr << hdr <<
"Input parameters not valid.\n Ekin = " << ekin
1174 <<
" MeV, dE = " << de <<
" MeV, step length = " << step
1178 std::cerr << hdr <<
"Track parameters not valid.\n Mass = "
1182 std::cerr << hdr <<
"Material parameters not valid.\n A = " <<
m_a
1183 <<
", Z = " <<
m_z <<
", density = " <<
m_density <<
" g/cm3.\n";
1187 const double rkin = 1.e6 * ekin /
m_mion;
1188 const double gamma = 1. + rkin;
1189 const double beta2 = rkin > 1.e-5 ? 1. - 1. / (gamma * gamma) : 2. * rkin;
1192 const double rm = ElectronMass /
m_mion;
1193 const double emax = 2 * ElectronMass * 1.e-6 * beta2 * gamma * gamma /
1194 (1. + 2 * gamma * rm + rm * rm);
1196 const double xi =
Xi(step, beta2);
1198 const double rkappa = xi / emax;
1205 if (m_model <= 0 || m_model > 4) {
1207 if (
m_debug) std::cout << hdr <<
"Fixed energy loss.\n";
1210 if (
m_debug) std::cout << hdr <<
"Landau imposed.\n";
1211 const double xlmean = -(log(rkappa) + beta2 + 1. - Gamma);
1215 if (
m_debug) std::cout << hdr <<
"Vavilov imposed.\n";
1216 if (rkappa > 0.01 && rkappa < 12) {
1218 rndde += xi * (xvav + log(rkappa) + beta2 + (1 - Gamma));
1222 if (
m_debug) std::cout << hdr <<
"Gaussian imposed.\n";
1223 rndde +=
RndmGaussian(0., sqrt(xi * emax * (1 - 0.5 * beta2)));
1224 }
else if (rkappa < 0.05) {
1226 if (
m_debug) std::cout << hdr <<
"Landau automatic.\n";
1227 const double xlmean = -(log(rkappa) + beta2 + (1 - Gamma));
1228 const double par[] = {0.50884, 1.26116, 0.0346688, 1.46314,
1229 0.15088e-2, 1.00324, -0.13049e-3};
1230 const double xlmax = par[0] + par[1] * xlmean + par[2] * xlmean * xlmean +
1231 par[6] * xlmean * xlmean * xlmean +
1232 (par[3] + xlmean * par[4]) * exp(par[5] * xlmean);
1234 for (
unsigned int iter = 0; iter < 100; ++iter) {
1235 if (xlan < xlmax)
break;
1238 rndde += xi * (xlan - xlmean);
1239 }
else if (rkappa < 5) {
1241 if (
m_debug) std::cout << hdr <<
"Vavilov fast automatic.\n";
1243 rndde += xi * (xvav + log(rkappa) + beta2 + (1 - Gamma));
1246 if (
m_debug) std::cout << hdr <<
"Gaussian automatic.\n";
1247 rndde =
RndmGaussian(de, sqrt(xi * emax * (1 - 0.5 * beta2)));
1251 std::cout << hdr <<
"Energy loss generated = " << rndde <<
" MeV.\n";
1257 double& tcls,
int& n,
double& e,
double& extra) {
1271 n = cluster.electrons;
1273 extra = cluster.kinetic;
Abstract base class for media.
bool IsIonisable() const
Is charge deposition by charged particles/photon enabled in this medium?
bool IsInArea(const double x, const double y, const double z)
Check if a point is inside the user area.
bool GetMedium(const double x, const double y, const double z, Medium *&medium)
Get the medium at (x, y, z).
bool GetArea(double &xmin, double &ymin, double &zmin, double &xmax, double &ymax, double &zmax)
Return the current user area.
std::vector< double > m_longstraggle
Longitudinal straggling [cm].
double RndmEnergyLoss(const double ekin, const double de, const double step) const
void Print()
Print the energy loss table.
double m_qion
Charge of ion.
double m_z
Effective Z of the gas.
std::vector< double > m_hdloss
Hadronic energy loss [MeV cm2/g].
void SetDensity(const double density)
Set the density [g/cm3] of the target medium.
bool GetCluster(double &xcls, double &ycls, double &zcls, double &tcls, int &n, double &e, double &extra) override
std::vector< double > m_range
Projected range [cm].
bool NewTrack(const double x0, const double y0, const double z0, const double t0, const double dx0, const double dy0, const double dz0) override
double DedxHD(const double e) const
std::vector< Cluster > m_clusters
size_t m_currcluster
Index of the next cluster to be returned.
std::vector< double > m_emloss
EM energy loss [MeV cm2/g].
bool m_chargeset
Charge gas been defined.
bool SmallestStep(double ekin, double de, double step, double &stpmin)
double m_fano
Fano factor [-].
double m_mion
Mass of ion [MeV].
bool PreciseLoss(const double step, const double estart, double &deem, double &dehd) const
double m_work
Work function [eV].
double m_density
Density [g/cm3].
bool m_useTransStraggle
Include transverse straggling.
int m_nsize
Targeted cluster size.
std::vector< double > m_ekin
Energy in energy loss table [MeV].
std::vector< double > m_transstraggle
Transverse straggling [cm].
bool EstimateRange(const double ekin, const double step, double &stpmax)
bool m_useLongStraggle
Include longitudinal straggling.
double m_a
Effective A of the gas.
void PlotRange()
Make a plot of the projected range as function of the projectile energy.
int m_maxclusters
Maximum number of clusters allowed (infinite if 0)
double DedxEM(const double e) const
bool ReadFile(const std::string &file)
Load data from a SRIM file.
double Xi(const double x, const double beta2) const
Abstract base class for track generation.
double GetKineticEnergy() const
Return the kinetic energy of the projectile.
void PlotCluster(const double x0, const double y0, const double z0)
void PlotNewTrack(const double x0, const double y0, const double z0)
static std::string FindUnusedCanvasName(const std::string &s)
Find an unused canvas name.
double Divdif(const std::vector< double > &f, const std::vector< double > &a, int nn, double x, int mm)
double RndmHeedWF(const double w, const double f)
double RndmVavilov(const double rkappa, const double beta2)
Draw a random number from a Vavilov distribution.
void RndmDirection(double &dx, double &dy, double &dz, const double length=1.)
Draw a random (isotropic) direction vector.
double RndmGaussian()
Draw a Gaussian random variate with mean zero and standard deviation one.
double RndmLandau()
Draw a random number from a Landau distribution.
int electrons
Number of electrons in this cluster.
double kinetic
Ion energy when cluster was created.
double ec
Energy spent to make the cluster.
double t
Cluster location and time.