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 edensity,
const double qp,
const double mp,
58 const double emax,
const double xi,
const double kappa) {
59 std::cout << hdr <<
"Settings:\n"
60 <<
" dE = " << de <<
" MeV,\n"
61 <<
" step = " << step <<
" cm.\n"
62 <<
" Ekin = " << ekin <<
" MeV,\n"
63 <<
" beta2 = " << beta2 <<
",\n"
64 <<
" gamma = " << gamma <<
".\n"
65 <<
" electron density = " << edensity <<
" / cm3.\n"
66 <<
" Qpart = " << qp <<
", mpart = " << 1.e-6 * mp <<
" MeV.\n"
67 <<
" Emax = " << emax <<
" MeV,\n"
68 <<
" xi = " << xi <<
" MeV,\n"
69 <<
" kappa = " << kappa <<
".\n";
82 const std::string hdr =
m_className +
"::ReadFile:\n ";
84 std::ifstream fsrim(file);
86 std::cerr << hdr <<
"Could not open SRIM file " << file
87 <<
" for reading.\n The file perhaps does not exist.\n";
90 unsigned int nread = 0;
94 std::cout << hdr <<
"SRIM header records from file " << file <<
"\n";
96 constexpr size_t size = 100;
98 while (fsrim.getline(line, size,
'\n')) {
100 if (strstr(line,
"SRIM version") != NULL) {
101 if (
m_debug) std::cout <<
"\t" << line <<
"\n";
102 }
else if (strstr(line,
"Calc. date") != NULL) {
103 if (
m_debug) std::cout <<
"\t" << line <<
"\n";
104 }
else if (strstr(line,
"Ion =") != NULL) {
110 char* token =
nullptr;
111 token = strtok(line,
" []=");
112 token = strtok(
nullptr,
" []=");
113 token = strtok(
nullptr,
" []=");
116 m_qion = std::atof(token);
119 token = strtok(
nullptr,
" []=");
120 token = strtok(
nullptr,
" []=");
121 token = strtok(
nullptr,
" []=");
123 m_mion = std::atof(token) * AtomicMassUnitElectronVolt;
126 if (!fsrim.getline(line, size,
'\n')) {
127 std::cerr << hdr <<
"Premature EOF looking for target density (line "
132 if (!fsrim.getline(line, size,
'\n')) {
133 std::cerr << hdr <<
"Premature EOF looking for target density (line "
138 const bool pre2013 = (strstr(line,
"Target Density") != NULL);
139 token = strtok(line,
" ");
140 token = strtok(NULL,
" ");
141 token = strtok(NULL,
" ");
142 if (pre2013) token = strtok(NULL,
" ");
146 while (fsrim.getline(line, size,
'\n')) {
148 if (strstr(line,
"Stopping Units") == NULL)
continue;
149 if (strstr(line,
"Stopping Units = MeV / (mg/cm2)") != NULL ||
150 strstr(line,
"Stopping Units = MeV/(mg/cm2)") != NULL) {
152 std::cout << hdr <<
"Stopping units: MeV / (mg/cm2) as expected.\n";
156 std::cerr << hdr <<
"Unknown stopping units. Aborting (line " << nread
162 while (fsrim.getline(line, size,
'\n')) {
164 if (strstr(line,
"-----------") != NULL)
break;
174 unsigned int ntable = 0;
175 while (fsrim.getline(line, size,
'\n')) {
177 if (strstr(line,
"-----------") != NULL)
break;
179 token = strtok(line,
" ");
180 m_ekin.push_back(atof(token));
181 token = strtok(NULL,
" ");
182 if (strcmp(token,
"eV") == 0) {
184 }
else if (strcmp(token,
"keV") == 0) {
186 }
else if (strcmp(token,
"GeV") == 0) {
188 }
else if (strcmp(token,
"MeV") != 0) {
189 std::cerr << hdr <<
"Unknown energy unit " << token <<
"; aborting\n";
193 token = strtok(NULL,
" ");
196 token = strtok(NULL,
" ");
199 token = strtok(NULL,
" ");
200 m_range.push_back(atof(token));
201 token = strtok(NULL,
" ");
202 if (strcmp(token,
"A") == 0) {
204 }
else if (strcmp(token,
"um") == 0) {
206 }
else if (strcmp(token,
"mm") == 0) {
208 }
else if (strcmp(token,
"m") == 0) {
210 }
else if (strcmp(token,
"km") == 0) {
212 }
else if (strcmp(token,
"cm") != 0) {
213 std::cerr << hdr <<
"Unknown distance unit " << token <<
"; aborting\n";
217 token = strtok(NULL,
" ");
219 token = strtok(NULL,
" ");
220 if (strcmp(token,
"A") == 0) {
222 }
else if (strcmp(token,
"um") == 0) {
224 }
else if (strcmp(token,
"mm") == 0) {
226 }
else if (strcmp(token,
"m") == 0) {
228 }
else if (strcmp(token,
"km") == 0) {
230 }
else if (strcmp(token,
"cm") != 0) {
231 std::cerr << hdr <<
"Unknown distance unit " << token <<
"; aborting\n";
235 token = strtok(NULL,
" ");
237 token = strtok(NULL,
" ");
238 if (strcmp(token,
"A") == 0) {
240 }
else if (strcmp(token,
"um") == 0) {
242 }
else if (strcmp(token,
"mm") == 0) {
244 }
else if (strcmp(token,
"m") == 0) {
246 }
else if (strcmp(token,
"km") == 0) {
248 }
else if (strcmp(token,
"cm") != 0) {
249 std::cerr << hdr <<
"Unknown distance unit " << token <<
"; aborting\n";
259 while (fsrim.getline(line, size,
'\n')) {
261 if (strstr(line,
"=============") != NULL) {
263 }
else if (strstr(line,
"MeV / (mg/cm2)") != NULL ||
264 strstr(line,
"MeV/(mg/cm2)") != NULL) {
265 token = strtok(line,
" ");
266 scale = std::atof(token);
270 std::cerr << hdr <<
"Did not find stopping unit scaling; aborting.\n";
274 for (
unsigned int i = 0; i < ntable; ++i) {
281 std::cout << hdr <<
"Successfully read " << file <<
"(" << nread
288 std::cout <<
"TrackSrim::Print:\n SRIM energy loss table\n\n"
289 <<
" Energy EM Loss HD loss Range "
290 <<
"l straggle t straggle\n"
291 <<
" [MeV] [MeV/cm] [MeV/cm] [cm] "
293 const unsigned int nPoints =
m_emloss.size();
294 for (
unsigned int i = 0; i < nPoints; ++i) {
295 printf(
"%10g %10g %10g %10g %10g %10g\n",
m_ekin[i],
301 std::printf(
" Work function: %g eV\n",
m_work);
303 std::cout <<
" Work function: automatic\n";
306 std::printf(
" Fano factor: %g\n",
m_fano);
308 std::cout <<
" Fano factor: automatic\n";
310 printf(
" Ion charge: %g\n",
m_qion);
311 printf(
" Mass: %g MeV\n", 1.e-6 *
m_mion);
312 printf(
" Density: %g g/cm3\n",
m_rho);
313 if (
m_a > 0. &&
m_z > 0.) {
314 std::printf(
" A, Z: %g, %g\n",
m_a,
m_z);
316 std::cout <<
" A, Z: automatic\n";
322 const unsigned int nPoints =
m_ekin.size();
323 std::vector<double> yE;
324 std::vector<double> yH;
325 std::vector<double> yT;
326 for (
unsigned int i = 0; i < nPoints; ++i) {
331 yT.push_back(em + hd);
333 const double xmin = *std::min_element(std::begin(
m_ekin), std::end(
m_ekin));
334 const double xmax = *std::max_element(std::begin(
m_ekin), std::end(
m_ekin));
335 const double ymax = *std::max_element(std::begin(yT), std::end(yT));
338 TCanvas* celoss =
new TCanvas(name.c_str(),
"Energy loss");
342 celoss->DrawFrame(xmin, 0., xmax, 1.05 * ymax,
";Ion energy [MeV];Energy loss [MeV/cm]");
346 gr.SetLineStyle(kSolid);
348 gr.SetMarkerStyle(21);
349 gr.SetLineColor(kBlue + 1);
350 gr.SetMarkerColor(kBlue + 1);
351 gr.DrawGraph(nPoints,
m_ekin.data(), yE.data(),
"plsame");
353 gr.SetLineColor(kGreen + 2);
354 gr.SetMarkerColor(kGreen + 2);
355 gr.DrawGraph(nPoints,
m_ekin.data(), yH.data(),
"plsame");
357 gr.SetLineColor(kOrange - 3);
358 gr.SetMarkerColor(kOrange - 3);
359 gr.DrawGraph(nPoints,
m_ekin.data(), yT.data(),
"plsame");
362 double xLabel = 0.4 * xmax;
363 double yLabel = 0.9 * ymax;
364 label.SetTextColor(kBlue + 1);
365 label.SetText(xLabel, yLabel,
"EM energy loss");
366 label.DrawLatex(xLabel, yLabel,
"EM energy loss");
367 yLabel -= 1.5 * label.GetYsize();
368 label.SetTextColor(kGreen + 2);
369 label.DrawLatex(xLabel, yLabel,
"HD energy loss");
370 yLabel -= 1.5 * label.GetYsize();
371 label.SetTextColor(kOrange - 3);
372 label.DrawLatex(xLabel, yLabel,
"Total energy loss");
378 const double xmin = *std::min_element(std::begin(
m_ekin), std::end(
m_ekin));
379 const double xmax = *std::max_element(std::begin(
m_ekin), std::end(
m_ekin));
380 const double ymax = *std::max_element(std::begin(
m_range), std::end(
m_range));
384 TCanvas* crange =
new TCanvas(name.c_str(),
"Range");
388 crange->DrawFrame(xmin, 0., xmax, 1.05 * ymax,
";Ion energy [MeV];Projected range [cm]");
391 gr.SetLineColor(kOrange - 3);
392 gr.SetMarkerColor(kOrange - 3);
393 gr.SetLineStyle(kSolid);
395 gr.SetMarkerStyle(21);
402 const double xmin = *std::min_element(std::begin(
m_ekin), std::end(
m_ekin));
403 const double xmax = *std::max_element(std::begin(
m_ekin), std::end(
m_ekin));
404 const double ymax = std::max(*std::max_element(std::begin(
m_longstraggle),
410 TCanvas* cstraggle =
new TCanvas(name.c_str(),
"Straggling");
411 cstraggle->SetLogx();
412 cstraggle->SetGridx();
413 cstraggle->SetGridy();
414 cstraggle->DrawFrame(xmin, 0., xmax, 1.05 * ymax,
";Ion energy [MeV];Straggling [cm]");
417 const unsigned int nPoints =
m_ekin.size();
419 gr.SetLineStyle(kSolid);
421 gr.SetMarkerStyle(21);
423 gr.SetLineColor(kOrange - 3);
424 gr.SetMarkerColor(kOrange - 3);
427 gr.SetLineColor(kGreen + 2);
428 gr.SetMarkerColor(kGreen + 2);
432 double xLabel = 1.2 * xmin;
433 double yLabel = 0.9 * ymax;
434 label.SetTextColor(kOrange - 3);
435 label.SetText(xLabel, yLabel,
"Longitudinal");
436 label.DrawLatex(xLabel, yLabel,
"Longitudinal");
437 yLabel -= 1.5 * label.GetYsize();
438 label.SetTextColor(kGreen + 2);
439 label.DrawLatex(xLabel, yLabel,
"Transverse");
452 const double edens)
const {
454 constexpr double fconst = 1.e-6 * TwoPi * (
455 FineStructureConstant * FineStructureConstant * HbarC * HbarC) /
461 double& deem,
double& dehd)
const {
465 if (
m_debug) printf(
" Integrating energy losses over %g cm.\n", step);
467 const double eps = 1.0e-2;
469 unsigned int ndiv = 1;
471 const unsigned int nMaxIter = 10;
472 bool converged =
false;
473 for (
unsigned int iter = 0; iter < nMaxIter; ++iter) {
479 const double s =
m_rho * step / ndiv;
480 for (
unsigned int i = 0; i < ndiv; i++) {
484 const double em22 = s *
DedxEM(e2 - 0.5 * de21);
485 const double hd22 = s *
DedxHD(e2 - 0.5 * de21);
489 const double em41 = s *
DedxEM(e4);
490 const double hd41 = s *
DedxHD(e4);
491 const double de41 = em41 + hd41;
493 const double em42 = s *
DedxEM(e4 - 0.5 * de41);
494 const double hd42 = s *
DedxHD(e4 - 0.5 * de41);
495 const double de42 = em42 + hd42;
497 const double em43 = s *
DedxEM(e4 - 0.5 * de42);
498 const double hd43 = s *
DedxHD(e4 - 0.5 * de42);
499 const double de43 = em43 + hd43;
501 const double em44 = s *
DedxEM(e4 - de43);
502 const double hd44 = s *
DedxHD(e4 - de43);
503 const double de44 = em44 + hd44;
505 deem += (em41 + em44) / 6. + (em42 + em43) / 3.;
506 dehd += (hd41 + hd44) / 6. + (hd42 + hd43) / 3.;
508 e4 -= (de41 + de44) / 6. + (de42 + de43) / 3.;
511 printf(
" Iteration %u has %u division(s). Losses:\n", iter, ndiv);
512 printf(
" de4 = %12g, de2 = %12g MeV\n", estart - e2, estart - e4);
513 printf(
" em4 = %12g, hd4 = %12g MeV\n", deem, dehd);
516 if (fabs(e2 - e4) > eps * (fabs(e2) + fabs(e4) + fabs(estart))) {
527 <<
"No convergence achieved integrating energy loss.\n";
529 std::cout <<
" Convergence at eps = " << eps <<
".\n";
535 double& stpmax)
const {
542 const std::string hdr =
m_className +
"::EstimateRange: ";
548 double deem = 0., dehd = 0.;
550 double de1 = deem + dehd;
553 if (
m_debug) std::cout <<
" Initial step OK.\n";
557 double st2 = 0.5 * step;
559 const unsigned int nMaxIter = 20;
560 for (
unsigned int iter = 0; iter < nMaxIter; ++iter) {
565 if (de2 < ekin)
break;
572 std::cerr << hdr <<
"\n Did not find a smaller step in " << nMaxIter
573 <<
" iterations. Abandoned.\n";
574 stpmax = 0.5 * (st1 + st2);
578 printf(
" Step 1 = %g cm, dE 1 = %g MeV\n", st1, de1 - ekin);
579 printf(
" Step 2 = %g cm, dE 2 = %g MeV\n", st2, de2 - ekin);
582 for (
unsigned int iter = 0; iter < nMaxIter; ++iter) {
586 std::cerr <<
" Bisection failed due to equal energy loss for "
587 <<
"two step sizes. Abandoned.\n";
589 stpmax = 0.5 * (st1 + st2);
594 if ((fabs(de1 - ekin) < 0.01 * fabs(de2 - de1)) ||
595 (fabs(de1 - ekin) > 0.99 * fabs(de2 - de1))) {
596 st3 = 0.5 * (st1 + st2);
598 st3 = st1 - (st2 - st1) * (de1 - ekin) / (de2 - de1);
602 const double de3 = deem + dehd;
604 std::printf(
" Step 1 = %g cm, dE 1 = %g MeV\n", st1, de1 - ekin);
605 std::printf(
" Step 2 = %g cm, dE 2 = %g MeV\n", st2, de2 - ekin);
606 std::printf(
" Step 3 = %g cm, dE 3 = %g MeV\n", st3, de3 - ekin);
617 if (fabs(de3 - ekin) < 1e-3 * (fabs(de3) + fabs(ekin)) ||
618 fabs(st1 - st2) < 1e-3 * (fabs(st1) + fabs(st2))) {
619 stpmax = st1 - (st2 - st1) * (de1 - ekin) / (de2 - de1);
624 std::cout <<
" Bisection did not converge in " << nMaxIter
625 <<
" steps. Abandoned.\n";
627 stpmax = st1 - (st2 - st1) * (de1 - ekin) / (de2 - de1);
640 const double t0,
const double dx0,
const double dy0,
644 const std::string hdr =
m_className +
"::NewTrack: ";
648 std::cerr << hdr <<
"Sensor is not defined.\n";
655 std::cerr << hdr <<
"No valid medium at initial position.\n";
662 std::cerr << hdr <<
"Work function not defined.\n";
667 fano = std::max(fano, 0.);
680 std::cerr << hdr <<
"Invalid target density.\n";
685 const double normdir = sqrt(dx0 * dx0 + dy0 * dy0 + dz0 * dz0);
686 std::array<double, 3> v = {dx0, dy0, dz0};
687 if (normdir < Small) {
689 std::cout << hdr <<
"Direction vector has zero norm.\n"
690 <<
" Initial direction is randomized.\n";
703 std::cerr << hdr <<
"Particle mass not set.\n";
706 std::cerr << hdr <<
"Particle charge not set.\n";
709 std::cerr << hdr <<
"Initial particle energy not set.\n";
715 if (ekin0 < 1.e-14 *
m_mion || ekin0 < 1.e-6 * w) {
716 std::cerr << hdr <<
"Initial kinetic energy E = " << ekin0
717 <<
" MeV such that beta2 = 0 or E << W; particle stopped.\n";
722 const double tracklength = 10 * Interpolate(ekin0,
m_ekin,
m_range);
726 std::cout << hdr <<
"Track generation with the following parameters:\n";
727 const unsigned int nTable =
m_ekin.size();
728 printf(
" Table size %u\n", nTable);
729 printf(
" Particle kin. energy %g MeV\n", ekin0);
730 printf(
" Particle mass %g MeV\n", 1.e-6 *
m_mion);
731 printf(
" Particle charge %g\n",
m_qion);
732 printf(
" Work function %g eV\n", w);
733 printf(
" Fano factor %g\n", fano);
736 printf(
" Cluster size %d\n",
m_nsize);
747 std::array<double, 3> x = {x0, y0, z0};
759 if (
m_debug) printf(
" Iteration %d. Ekin = %g MeV.\n", iter, ekin);
768 step =
m_nsize * 1.e-6 * w / dedxem;
771 step = ekin0 / (ncls * (dedxem + dedxhd));
773 if (
m_debug) printf(
" Estimated step size: %g cm\n", step);
776 if (dsum + step > tracklength) {
777 step = tracklength - dsum;
778 if (
m_debug) printf(
" Truncating step to %g cm.\n", step);
782 double deem = 0., dehd = 0.;
784 double stpmax = tracklength - dsum;
786 if (deem + dehd > ekin) {
790 deem = ekin * deem / (dehd + deem);
793 if (
m_debug) std::cout <<
" Track length reached.\n";
796 std::cout <<
" Maximum step size set to " << stpmax <<
" cm.\n";
801 std::cerr << hdr <<
"Failure computing the minimum step size.\n"
802 <<
" Clustering abandoned.\n";
805 if (stpmin > stpmax) {
808 std::cout <<
" Min. step > max. step; depositing all energy.\n";
810 if (deem + dehd > ekin) {
816 }
else if (step < stpmin) {
818 if (
m_debug) std::cout <<
" Enlarging step size.\n";
821 if (deem + dehd > ekin) {
822 if (
m_debug) std::cout <<
" Excess loss. Recomputing max. step.\n";
826 deem = ekin * deem / (dehd + deem);
834 if (
m_debug) std::cout <<
" Step size ok.\n";
839 if (
m_debug) std::cout <<
" Truncating negative energy loss.\n";
841 }
else if (eloss > ekin - dehd) {
842 if (
m_debug) std::cout <<
" Excess energy loss, using mean.\n";
843 if (deem + dehd > ekin) {
851 std::cout <<
" Step length = " << step <<
" cm.\n"
852 <<
" Mean loss = " << deem <<
" MeV.\n"
853 <<
" Actual loss = " << eloss <<
" MeV.\n";
856 const double rk = 1.e6 * ekin /
m_mion;
857 const double gamma = 1. + rk;
858 const double beta2 = rk > 1.e-5 ? 1. - 1. / (gamma * gamma) : 2. * rk;
859 const double vmag = sqrt(beta2) * SpeedOfLight;
861 const double dt = vmag > 0. ? step / vmag : 0.;
863 std::array<double, 3> x1 = x;
864 std::array<double, 3> v1 = v;
866 double bx = 0., by = 0., bz = 0.;
868 m_sensor->MagneticField(x[0], x[1], x[2], bx, by, bz, status);
870 std::array<double, 3> d =
StepBfield(dt, qoverm, vmag, bx, by, bz, v1);
875 x1[0] = x[0] + step * v[0];
876 x1[1] = x[1] + step * v[1];
877 x1[2] = x[2] + step * v[2];
881 if (!
m_sensor->HasMagneticField()) {
887 if (deem + dehd > ekin) {
888 if (
m_debug) std::cout <<
" Excess loss.\n";
889 deem = ekin * deem / (dehd + deem);
893 if (eloss > ekin - dehd) eloss = deem;
904 cluster.
n = int((eloss + epool) / (1.e-6 * w));
905 cluster.
energy = w * cluster.
n;
907 double ecl = 1.e6 * (eloss + epool);
913 if (ernd1 > ecl)
break;
919 std::cout <<
" EM + pool: " << 1.e6 * (eloss + epool)
921 <<
" eV, E/w: " << (eloss + epool) / (1.e-6 * w)
922 <<
", n: " << cluster.
n <<
".\n";
926 epool += eloss - 1.e-6 * cluster.
energy;
928 std::cout <<
" Adding cluster " <<
m_clusters.size() <<
" at ("
929 << cluster.
x <<
", " << cluster.
y <<
", " << cluster.
z
930 <<
"), e = " << cluster.
energy <<
", n = " << cluster.
n
932 <<
" Pool = " << epool <<
" MeV.\n";
939 ekin -= eloss + dehd;
942 if (
m_debug) std::cout <<
" Finishing flag raised.\n";
944 }
else if (ekin < ekin0 * 1.e-9) {
946 if (
m_debug) std::cout <<
" Energy exhausted.\n";
960 const double scale = sqrt(step / prange);
965 printf(
" Sigma longitudinal: %g cm\n", sigl);
966 printf(
" Sigma transverse: %g, %g cm\n", sigt1, sigt2);
970 if (v[0] * v[0] + v[2] * v[2] <= 0) {
973 }
else if (v[1] > 0) {
976 std::cerr << hdr <<
"Zero step length; clustering abandoned.\n";
981 phi = atan2(v[0], v[2]);
982 theta = atan2(v[1], sqrt(v[0] * v[0] + v[2] * v[2]));
985 const double cp = cos(phi);
986 const double ct = cos(theta);
987 const double sp = sin(phi);
988 const double st = sin(theta);
989 x[0] += +cp * sigt1 - sp * st * sigt2 + sp * ct * sigl;
990 x[1] += +ct * sigt2 + st * sigl;
991 x[2] += -sp * sigt1 - cp * st * sigt2 + cp * ct * sigl;
997 std::cerr << hdr <<
"W value in medium " << medium->
GetName()
998 <<
" is not defined.\n";
1005 std::cerr << hdr <<
"Exceeded maximum number of clusters.\n";
1012 const std::array<double, 3>& v0,
1013 const double step0)
const {
1015 std::array<double, 3> x1 = x0;
1017 const double tol = std::max(1.e-6 * step0, 1.e-6);
1020 const std::array<double, 3> x2 = {x1[0] + s * v0[0], x1[1] + s * v0[1],
1031 const std::array<double, 3>& v0,
1032 const double dt0,
const double vmag)
const {
1036 std::array<double, 3> x1 = x0;
1037 std::array<double, 3> v1 = v0;
1039 const double tol = std::max(1.e-6 * dt0, 1.e-6);
1042 double bx = 0., by = 0., bz = 0.;
1044 m_sensor->MagneticField(x1[0], x1[1], x1[2], bx, by, bz, status);
1045 std::array<double, 3> v2 = v1;
1046 std::array<double, 3> d =
StepBfield(s, qoverm, vmag, bx, by, bz, v2);
1047 std::array<double, 3> x2 = {x1[0] + d[0], x1[1] + d[1], x1[2] + d[2]};
1058 double de,
double step,
double& stpmin) {
1063 const std::string hdr =
m_className +
"::SmallestStep: ";
1064 constexpr double expmax = 30;
1069 if (ekin <= 0 || de <= 0 || step <= 0) {
1070 std::cerr << hdr <<
"Input parameters not valid.\n Ekin = " << ekin
1071 <<
" MeV, dE = " << de <<
" MeV, step length = " << step
1076 <<
"Track parameters not valid.\n Mass = " << 1.e-6 *
m_mion
1077 <<
" MeV, charge = " <<
m_qion <<
".\n";
1079 }
else if (edens <= 0) {
1080 std::cerr << hdr <<
"Target parameters not valid.\n"
1081 <<
" electron density = " << edens <<
" / cm3.\n";
1086 const double rkin = 1.e6 * ekin /
m_mion;
1087 const double gamma = 1. + rkin;
1088 const double beta2 = rkin > 1.e-5 ? 1. - 1. / (gamma * gamma) : 2. * rkin;
1091 const double rm = ElectronMass /
m_mion;
1092 const double emax = 2 * ElectronMass * 1.e-6 * beta2 * gamma * gamma /
1093 (1. + 2 * gamma * rm + rm * rm);
1095 double xi =
Xi(step, beta2, edens);
1097 double rkappa = xi / emax;
1100 double stpnow = step;
1101 constexpr unsigned int nMaxIter = 10;
1102 for (
unsigned int iter = 0; iter < nMaxIter; ++iter) {
1106 PrintSettings(hdr, denow, stpnow, ekin, beta2, gamma, edens,
1110 double rknew = rkappa;
1116 constexpr double xlmin = -3.;
1117 const double exponent = -xlmin - 1. + Gamma - beta2 - denow / xi;
1118 const double rklim = exponent < -expmax ? 0. : exp(exponent);
1119 stpmin = stpnow * (rklim / rkappa);
1121 std::cout <<
" Landau distribution is imposed (kappa_min = "
1122 << rklim <<
", d_min = " << stpmin <<
" cm).\n";
1126 const double xlmin = StepVavilov(rkappa);
1127 const double exponent = -xlmin - 1. + Gamma - beta2 - denow / xi;
1128 const double rklim = exponent < -expmax ? 0. : exp(exponent);
1129 stpmin = stpnow * (rklim / rkappa);
1130 xinew =
Xi(stpmin, beta2, edens);
1131 rknew = xinew / emax;
1133 std::cout <<
" Vavilov distribution is imposed (kappa_min = "
1134 << rklim <<
", d_min = " << stpmin <<
" cm, kappa_new = "
1135 << rknew <<
", xi_new = " << xinew <<
" MeV).\n";
1137 if (stpmin > stpnow * 1.1) {
1138 if (
m_debug) std::cout <<
" Step size increase. New pass.\n";
1143 const double sigma2 = xi * emax * (1 - 0.5 * beta2);
1144 stpmin = stpnow * 16 * sigma2 / (denow * denow);
1146 const double sigmaMin2 =
Xi(stpmin, beta2, edens) * emax * (1 - 0.5 * beta2);
1147 std::cout <<
" Gaussian distribution is imposed, d_min = "
1148 << stpmin <<
" cm, sigma/mu (old) = "
1149 << sqrt(sigma2) / de <<
", sigma/mu (min) = "
1150 << sqrt(sigmaMin2) / (stpmin * denow / stpnow) <<
".\n";
1152 }
else if (rkappa < 0.05) {
1154 constexpr double xlmin = -3.;
1155 const double exponent = -xlmin - 1. + Gamma - beta2 - denow / xi;
1156 const double rklim = exponent < -expmax ? 0. : exp(exponent);
1157 stpmin = stpnow * (rklim / rkappa);
1158 xinew =
Xi(stpmin, beta2, edens);
1159 rknew = xinew / emax;
1161 std::cout <<
" Landau distribution automatic (kappa_min = "
1162 << rklim <<
", d_min = " << stpmin <<
" cm).\n";
1164 if (rknew > 0.05 || stpmin > stpnow * 1.1) {
1167 std::cout <<
" Model change or step increase. New pass.\n";
1170 }
else if (rkappa < 5) {
1172 const double xlmin = StepVavilov(rkappa);
1173 const double exponent = -xlmin - 1. + Gamma - beta2 - denow / xi;
1174 const double rklim = exponent < -expmax ? 0. : exp(exponent);
1175 stpmin = stpnow * (rklim / rkappa);
1176 xinew =
Xi(stpmin, beta2, edens);
1177 rknew = xinew / emax;
1179 std::cout <<
" Vavilov distribution automatic (kappa_min = "
1180 << rklim <<
", d_min = " << stpmin <<
" cm, kappa_new = "
1181 << rknew <<
", xi_new = " << xinew <<
" MeV).\n";
1183 if (rknew > 5 || stpmin > stpnow * 1.1) {
1186 std::cout <<
" Model change or step increase. New pass.\n";
1191 const double sigma2 = xi * emax * (1 - 0.5 * beta2);
1192 stpmin = stpnow * 16 * sigma2 / (denow * denow);
1194 const double sigmaMin2 =
Xi(stpmin, beta2, edens) * emax * (1 - 0.5 * beta2);
1195 std::cout <<
" Gaussian distribution automatic, d_min = "
1196 << stpmin <<
" cm, sigma/mu (old) = "
1197 << sqrt(sigma2) / de <<
", sigma/mu (min) = "
1198 << sqrt(sigmaMin2) / (stpmin * denow / stpnow) <<
".\n";
1202 if (stpnow > stpmin) {
1204 std::cout <<
" Step size ok, minimum: " << stpmin <<
" cm\n";
1210 std::cerr <<
" Step size must be increased to " << stpmin
1218 denow *= stpmin / stpnow;
1220 if (
m_debug) std::cout <<
" Iteration " << iter <<
"\n";
1221 if (iter == nMaxIter - 1) {
1223 std::cerr << hdr <<
"No convergence reached on step size.\n";
1230 const double step,
const double edens)
const {
1242 const std::string hdr =
"TrackSrim::RndmEnergyLoss: ";
1244 if (ekin <= 0 || de <= 0 || step <= 0) {
1245 std::cerr << hdr <<
"Input parameters not valid.\n Ekin = " << ekin
1246 <<
" MeV, dE = " << de <<
" MeV, step length = " << step
1250 std::cerr << hdr <<
"Track parameters not valid.\n Mass = "
1253 }
else if (edens <= 0.) {
1254 std::cerr << hdr <<
"Target parameters not valid.\n"
1255 <<
" electron density = " << edens <<
" / cm3.\n";
1259 const double rkin = 1.e6 * ekin /
m_mion;
1260 const double gamma = 1. + rkin;
1261 const double beta2 = rkin > 1.e-5 ? 1. - 1. / (gamma * gamma) : 2. * rkin;
1264 const double rm = ElectronMass /
m_mion;
1265 const double emax = 2 * ElectronMass * 1.e-6 * beta2 * gamma * gamma /
1266 (1. + 2 * gamma * rm + rm * rm);
1268 const double xi =
Xi(step, beta2, edens);
1270 const double rkappa = xi / emax;
1273 PrintSettings(hdr, de, step, ekin, beta2, gamma, edens,
1279 if (
m_debug) std::cout <<
" Fixed energy loss.\n";
1282 if (
m_debug) std::cout <<
" Landau imposed.\n";
1283 const double xlmean = -(log(rkappa) + beta2 + 1. - Gamma);
1287 if (
m_debug) std::cout <<
" Vavilov imposed.\n";
1288 if (rkappa > 0.01 && rkappa < 12) {
1290 rndde += xi * (xvav + log(rkappa) + beta2 + (1 - Gamma));
1294 if (
m_debug) std::cout <<
" Gaussian imposed.\n";
1295 rndde +=
RndmGaussian(0., sqrt(xi * emax * (1 - 0.5 * beta2)));
1296 }
else if (rkappa < 0.05) {
1298 if (
m_debug) std::cout <<
" Landau automatic.\n";
1299 const double xlmean = -(log(rkappa) + beta2 + (1 - Gamma));
1300 const double par[] = {0.50884, 1.26116, 0.0346688, 1.46314,
1301 0.15088e-2, 1.00324, -0.13049e-3};
1302 const double xlmax = par[0] + par[1] * xlmean + par[2] * xlmean * xlmean +
1303 par[6] * xlmean * xlmean * xlmean +
1304 (par[3] + xlmean * par[4]) * exp(par[5] * xlmean);
1306 for (
unsigned int iter = 0; iter < 100; ++iter) {
1307 if (xlan < xlmax)
break;
1310 rndde += xi * (xlan - xlmean);
1311 }
else if (rkappa < 5) {
1313 if (
m_debug) std::cout <<
" Vavilov fast automatic.\n";
1315 rndde += xi * (xvav + log(rkappa) + beta2 + (1 - Gamma));
1318 if (
m_debug) std::cout <<
" Gaussian automatic.\n";
1319 rndde =
RndmGaussian(de, sqrt(xi * emax * (1 - 0.5 * beta2)));
1323 std::cout <<
" Energy loss generated = " << rndde <<
" MeV.\n";
1329 double& tcls,
int& n,
double& e,
double& extra) {
1345 extra = cluster.kinetic;
Abstract base class for media.
double GetW() const
Get the W value.
virtual double GetMassDensity() const
Get the mass density [g/cm3].
virtual double GetNumberDensity() const
Get the number density [cm-3].
virtual double GetAtomicNumber() const
Get the effective atomic number.
const std::string & GetName() const
Get the medium name/identifier.
bool IsIonisable() const
Is charge deposition by charged particles/photon enabled in this medium?
double GetFanoFactor() const
Get the Fano factor.
std::vector< double > m_longstraggle
Longitudinal straggling [cm].
void Print()
Print the energy loss table.
double Xi(const double x, const double beta2, const double edens) const
double m_qion
Charge of the projectile.
double m_z
Effective Z of the target.
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.
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
bool EstimateRange(const double ekin, const double step, double &stpmax) 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
Has the charge been defined?
double m_fano
Fano factor [-] of the target.
TrackSrim()
Default constructor.
Medium * GetMedium(const std::array< double, 3 > &x) const
double RndmEnergyLoss(const double ekin, const double de, const double step, const double edens) const
double m_mion
Mass [MeV] of the projectile.
bool PreciseLoss(const double step, const double estart, double &deem, double &dehd) const
double m_work
Work function [eV] of the target.
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].
double Terminate(const std::array< double, 3 > &x0, const std::array< double, 3 > &v0, const double step0) const
double m_rho
Mass density [g/cm3] of the target.
bool m_useLongStraggle
Include longitudinal straggling.
double m_a
Effective A of the target.
bool GetCluster(double &xc, double &yc, double &zc, double &tc, int &nc, double &ec, double &extra) override
void PlotRange()
Plot the projected range as function of the projectile energy.
bool m_fset
Has the Fano factor been set?
int m_maxclusters
Maximum number of clusters allowed (infinite if 0)
double DedxEM(const double e) const
bool SmallestStep(const double ekin, const double edens, double de, double step, double &stpmin)
double TerminateBfield(const std::array< double, 3 > &x0, const std::array< double, 3 > &v0, const double dt0, const double vmag) const
bool ReadFile(const std::string &file)
Load data from a SRIM file.
double GetKineticEnergy() const
Return the kinetic energy of the projectile.
static std::array< double, 3 > StepBfield(const double dt, const double qoverm, const double vmag, double bx, double by, double bz, std::array< double, 3 > &dir)
void PlotCluster(const double x0, const double y0, const double z0)
Track()=delete
Default constructor.
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 n
Number of electrons in this cluster.
double kinetic
Ion energy when cluster was created.
double t
Cluster location and time.
double energy
Energy spent to make the cluster.