39 const double t0,
const double dx0,
const double dy0,
46 std::cerr <<
className <<
"::NewTrack:\n";
47 std::cerr <<
" Sensor is not defined.\n";
54 std::cerr <<
className <<
"::NewTrack:\n";
55 std::cerr <<
" No medium at initial position.\n";
59 std::cerr <<
className <<
"::NewTrack:\n";
60 std::cerr <<
" Medium at initial position is not ionisable.\n";
64 if (medium->
GetName() != mediumName ||
67 if (!SetupMedium(medium)) {
68 std::cerr <<
className <<
"::NewTrack:\n";
69 std::cerr <<
" Properties of medium " << medium->
GetName()
70 <<
" are not available.\n";
80 if (!SetupCrossSectionTable()) {
81 std::cerr <<
className <<
"::NewTrack:\n";
82 std::cerr <<
" Calculation of ionisation cross-section failed.\n";
93 const double d =
sqrt(dx0 * dx0 + dy0 * dy0 + dz0 * dz0);
96 std::cout <<
className <<
"::NewTrack:\n";
97 std::cout <<
" Direction vector has zero norm.\n";
98 std::cout <<
" Initial direction is randomized.\n";
101 const double stheta =
sqrt(1. - ctheta * ctheta);
103 dx =
cos(phi) * stheta;
104 dy =
sin(phi) * stheta;
116 double& tcls,
int& ncls,
double& edep,
127 std::cerr <<
className <<
"::GetCluster:\n";
128 std::cerr <<
" Track not initialized.\n";
129 std::cerr <<
" Call NewTrack first.\n";
134 if (SetupCrossSectionTable()) {
137 std::cerr <<
className <<
"::GetCluster:\n";
138 std::cerr <<
" Calculation of ionisation cross-section failed.\n";
156 if (medium->
GetName() != mediumName ||
176 if (u < cdf.back()) {
179 }
else if (u >= 1.) {
180 edep = energies.back();
184 int iLow = 0, iUp = cdf.size(), iMid;
185 while (iUp - iLow > 1) {
186 iMid = (iUp + iLow) >> 1;
187 if (u >= cdf[iMid]) {
194 edep = energies[iLow] + (u - cdf[iLow]) *
195 (energies[iUp] - energies[iLow]) /
196 (cdf[iUp] - cdf[iLow]);
197 f = rutherford[iLow] + (edep - energies[iLow]) *
198 (rutherford[iUp] - rutherford[iLow]) /
199 (energies[iUp] - energies[iLow]);
201 edep = log(energies[iLow]) +
202 (log(u) - log(cdf[iLow])) *
203 (log(energies[iUp]) - log(energies[iLow])) /
204 (log(cdf[iUp]) - log(cdf[iLow]));
206 f = rutherford[iLow] + (log(edep) - log(energies[iLow])) *
207 (rutherford[iUp] - rutherford[iLow]) /
208 (log(energies[iUp]) - log(energies[iLow]));
214 edep = SampleAsymptoticCs(u);
223 std::cout <<
className <<
"::GetCluster:\n";
224 std::cout <<
" Fraction of Rutherford scattering: " << f <<
"\n";
229bool TrackPAI::SetupMedium(
Medium* medium) {
233 std::cerr <<
className <<
"::SetupMedium:\n";
234 std::cerr <<
" Medium pointer is null.\n";
240 if (electronDensity <= 0.) {
241 std::cerr <<
className <<
"::SetupMedium:\n";
242 std::cerr <<
" Medium has an unphysical electron density ("
243 << electronDensity <<
")\n";
250 std::cerr <<
className <<
"::SetupMedium:\n";
251 std::cerr <<
" Could not load optical data for medium " << mediumName
257 if (emin < Small) emin = Small;
261 opticalDataTable.clear();
262 opticalData newEpsilon;
265 const double r =
pow(emax / emin, 1. /
double(nSteps));
268 double eC = 0.5 * emin * (1. + r);
269 for (
int i = 0; i < nSteps; ++i) {
271 newEpsilon.eps1 = eps1;
272 newEpsilon.eps2 = eps2;
273 opticalDataTable.push_back(newEpsilon);
274 energies.push_back(eC);
279 opticalDataTable[0].integral = 0.;
280 double integral = 0.;
281 double f1 = energies[0] *
282 LossFunction(opticalDataTable[0].eps1, opticalDataTable[0].eps2);
285 for (
int i = 1; i < nSteps; ++i) {
287 LossFunction(opticalDataTable[i].eps1, opticalDataTable[i].eps2);
288 eM = 0.5 * (energies[i - 1] + energies[i]);
290 fM = eM * LossFunction(eps1, eps2);
292 integral += (f1 + 4 * fM + f2) * (energies[i] - energies[i - 1]) / 6.;
293 opticalDataTable[i].integral = integral;
298 const double trk = 2 * Pi2 * FineStructureConstant *
pow(HbarC, 3) *
299 electronDensity / ElectronMass;
300 if (
fabs(integral - trk) > 0.2 * trk) {
301 std::cerr <<
className <<
"::SetupMedium:\n";
302 std::cerr <<
" Deviation from Thomas-Reiche-Kuhn sum rule by > 20%.\n";
303 std::cerr <<
" Optical data are probably incomplete or erroneous!\n";
312 std::cerr <<
className <<
"::GetClusterDensity:\n";
313 std::cerr <<
" Track has not been initialized.\n";
318 if (SetupCrossSectionTable()) {
321 std::cerr <<
className <<
"::GetClusterDensity:\n";
322 std::cerr <<
" Ionisation cross-section could not be calculated.\n";
333 std::cerr <<
className <<
"::GetStoppingPower:\n";
334 std::cerr <<
" Track has not been initialised.\n";
339 if (SetupCrossSectionTable()) {
342 std::cerr <<
className <<
"::GetStoppingPower:\n";
343 std::cerr <<
" Ionisation cross-section could not be calculated.\n";
351bool TrackPAI::SetupCrossSectionTable() {
354 std::cerr <<
className <<
"::SetupCrossSectionTable:\n";
355 std::cerr <<
" Medium not set up.\n";
359 const double c1 = 2. * Pi2 * FineStructureConstant *
pow(HbarC, 3) *
360 electronDensity / ElectronMass;
361 const double c2 =
q *
q * FineStructureConstant / (
beta2 * Pi * HbarC);
364 emax = ComputeMaxTransfer();
366 std::ofstream outfile;
367 if (
debug) outfile.open(
"dcs.txt", std::ios::out);
370 std::vector<double> dcs;
374 for (
int i = 0; i < nSteps; ++i) {
376 const double egamma = energies[i];
377 const double eps1 = opticalDataTable[i].eps1;
378 const double eps2 = opticalDataTable[i].eps2;
379 const double integral = opticalDataTable[i].integral;
382 double dcsLog = 0., dcsDensity = 0., dcsCerenkov = 0.;
387 LossFunction(eps1, eps2) * log(2 * ElectronMass *
beta2 / egamma);
389 const double u = 1. -
beta2 * eps1;
390 const double v =
beta2 * eps2;
391 dcsDensity = -0.5 * LossFunction(eps1, eps2) * log(u * u + v * v);
394 (
beta2 - eps1 / (eps1 * eps1 + eps2 * eps2)) * (HalfPi - atan(u / v));
395 }
else if (eps1 > 1. /
beta2) {
397 dcsCerenkov = Pi * (
beta2 - 1. / eps1);
403 if (egamma > 0. && integral > 0.) {
404 dcsRuth = integral / (egamma * egamma);
405 f = dcsRuth / (dcsLog + dcsDensity + dcsCerenkov);
407 rutherford.push_back(f);
408 dcs.push_back(dcsLog + dcsDensity + dcsCerenkov + dcsRuth);
411 outfile << egamma <<
" " << eps1 <<
" " << eps2 <<
" " << dcsLog* c2
412 <<
" " << dcsDensity* c2 <<
" " << dcsCerenkov* c2 <<
" "
413 << dcsRuth* c2 <<
"\n";
416 if (
debug) outfile.close();
424 for (
int i = 1; i < nSteps; ++i) {
425 cs += 0.5 * (dcs[i - 1] + dcs[i]) * (energies[i] - energies[i - 1]);
427 dedx += 0.5 * (dcs[i - 1] * energies[i - 1] + dcs[i] * energies[i]) *
428 (energies[i] - energies[i - 1]);
433 const double elim = energies.back();
435 cs += c1 * ComputeCsTail(elim, emax);
436 dedx += c1 * ComputeDeDxTail(elim, emax);
438 std::cerr <<
className <<
"::SetupCrossSectionTable:\n";
439 std::cerr <<
" Max. energy transfer lower than optical data range.\n";
443 std::cerr <<
"TrackPAI:SetupCrossSectionTable:\n";
444 std::cerr <<
" Total cross-section <= 0.\n";
449 for (
int i = nSteps; i--;) cdf[i] /= cs;
460double TrackPAI::ComputeMaxTransfer()
const {
471 return 2. * mass2 * ElectronMass * bg2 /
472 (mass2 + ElectronMass * ElectronMass + 2. *
energy * ElectronMass);
475double TrackPAI::ComputeCsTail(
const double emin,
const double emax) {
480 return 1. / emin - 1. / emax - 2 * emin / (ek * ek) -
481 emin * emin / ((ek - emin) * ek * ek);
482 }
else if (
mass == ElectronMass) {
485 return 1. / emin - 1. / emax + 3 * (emax - emin) / (ek * ek) -
486 (emax - emin) * (ek * (emax + emin) +
487 (emin * emin + emin * emax + emax * emax) / 3.) /
489 (2. / ek) * log(emax / emin);
495 return 1. / emin - 1. / emax -
beta2 * log(emax / emin) / emax;
499 return 1. / emin - 1. / emax -
beta2 * log(emax / emin) / emax +
505 const double ec =
mass *
mass / ElectronMass;
506 const double a = 1. / (3 * ec);
507 const double b = (emax - emin);
508 return 1. / emin - 1. / emax + a * b * (emin + e2 + emax) / e2 -
509 beta2 * a * b / emax + (a -
beta2 / emax) * log(emax / emin);
516 return 1. / emin - 1. / emax;
519double TrackPAI::ComputeDeDxTail(
const double emin,
const double emax) {
523 return -log(emin * (ek - emin) / (ek * ek)) +
524 (1. / (8 * (emin - ek) * ek * ek)) *
525 (-4 *
pow(emin, 3) + 4 * emin * emin * ek +
526 emin * ek * ek * (17. - 16. * CLog2) +
527 pow(ek, 3) * (-9. + 16. * CLog2));
528 }
else if (
mass == ElectronMass) {
531 return log(ek / emin) -
532 (ek - emin) * (ek - emin) *
533 (3. * emin * emin - 2. * emin * ek + 11. * ek * ek) /
539 return log(emax / emin) -
beta2 * (emax - emin) / emax;
543 return log(emax / emin) -
beta2 * (emax - emin) / emax +
549 double ec =
mass *
mass / ElectronMass;
550 return log(emax / emin) + (
pow(emax, 3) -
pow(emin, 3)) / (9. * e2 * ec) +
551 (emax * emax - emin * emin) / (6. * e2) +
552 (emax - emin) * (2. - (1. + emin / emax + 6 * ec / emax) *
beta2) /
561 return log(emax / emin);
564double TrackPAI::SampleAsymptoticCs(
double u) {
566 const double emin = energies.back();
568 u = (u - cdf.back()) / (1. - cdf.back());
571 return SampleAsymptoticCsElectron(emin, u);
572 }
else if (
mass == ElectronMass) {
573 return SampleAsymptoticCsPositron(emin, u);
579 return SampleAsymptoticCsSpinZero(emin, u);
583 return SampleAsymptoticCsSpinHalf(emin, u);
587 return SampleAsymptoticCsSpinOne(emin, u);
593 return emin * emax / (u * emin + (1. - u) * emax);
596double TrackPAI::SampleAsymptoticCsSpinZero(
const double emin,
double u) {
598 const double a = emin / emax;
599 const double b =
beta2 * a;
600 u *= (1. - a + b * log(a));
601 double eLow = emin, eUp = emax;
603 while (eUp - eLow > 1.) {
604 eM = 0.5 * (eUp + eLow);
605 if (u >= 1. - emin / eM - b * log(eM / emin)) {
612 return 0.5 * (eLow + eUp);
615double TrackPAI::SampleAsymptoticCsSpinHalf(
const double emin,
double u) {
617 const double a = emin / emax;
618 const double b =
beta2 * a;
620 u *= 1. - a + b * log(a) + (emax - emin) * c;
621 double eLow = emin, eUp = emax;
623 while (eUp - eLow > 1.) {
624 eM = 0.5 * (eUp + eLow);
625 if (u >= 1. - emin / eM - b * log(eM / emin) + (eM - emin) * c) {
632 return 0.5 * (eLow + eUp);
635double TrackPAI::SampleAsymptoticCsSpinOne(
const double emin,
double u) {
638 const double ec =
mass *
mass / ElectronMass;
639 const double a = 2 * ec / e2 -
beta2 / emax;
640 const double b = 1.5 * ec / emin;
641 const double c = 1. - 1.5 * ec *
beta2 / emax;
642 u *= (emax - emin) * (0.5 * (emin + emax) / e2 + a + b / emax) +
643 c * log(emax / emin);
644 double eLow = emin, eUp = emax;
646 while (eUp - eLow > 1.) {
647 eM = 0.5 * (eUp + eLow);
649 (eM - emin) * ((emin + eM) / e2 + a + b / eM) + c * log(eM / emin)) {
656 return 0.5 * (eLow + eUp);
659double TrackPAI::SampleAsymptoticCsElectron(
const double emin,
double u) {
662 const double ek2 = ek * ek;
663 const double a = ek / (emin * (ek - emin));
664 const double norm = 1. / emin - 0.5 / ek - emin * emin / ((ek - emin) * ek2) -
667 double eLow = emin, eUp = emax, eM;
668 while (eUp - eLow > 1.) {
669 eM = 0.5 * (eUp + eLow);
670 if (u >= a - 1. / eM + (eM - emin) / ek2 + 1. / (ek - eM)) {
676 return 0.5 * (eLow + eUp);
679double TrackPAI::SampleAsymptoticCsPositron(
const double emin,
double u) {
682 const double ek2 = ek * ek;
683 const double ek3 = ek2 * ek;
684 const double ek4 = 3 * ek3 * ek;
685 const double emin2 = emin * emin;
686 const double a = 1. / emin;
687 const double b = 3. / ek2;
688 const double c = 2. / ek;
689 u *= 1. / emin - 1. / emax + 3 * (emax - emin) / ek2 -
690 (emax - emin) * (emax + emin) / ek3 +
691 (emax - emin) * (emin * emin + emin * emax + emax * emax) / ek4 -
692 (2. / ek) * log(emax / emin);
693 double eLow = emin, eUp = emax;
695 while (eUp - eLow > 1.) {
696 eM = 0.5 * (eUp + eLow);
698 if (u >= a - 1. / eM + b * (eM - emin) - (eM2 - emin2) / ek3 +
699 (eM - emin) * (emin2 + emin * eM + eM2) / ek4 -
700 c * log(eM / emin)) {
707 return 0.5 * (eLow + eUp);
710double TrackPAI::LossFunction(
const double eps1,
const double eps2) {
712 const double eps = eps1 * eps1 + eps2 * eps2;
714 std::cerr <<
className <<
"::LossFunction:\n";
715 std::cerr <<
" Dielectric function is zero.\n";
718 return eps2 / (eps1 * eps1 + eps2 * eps2);
DoubleAc cos(const DoubleAc &f)
DoubleAc pow(const DoubleAc &f, double p)
DoubleAc sqrt(const DoubleAc &f)
DoubleAc sin(const DoubleAc &f)
DoubleAc exp(const DoubleAc &f)
DoubleAc fabs(const DoubleAc &f)
virtual double GetNumberDensity() const
virtual bool GetOpticalDataRange(double &emin, double &emax, const unsigned int &i=0)
virtual double GetAtomicNumber() const
virtual bool GetDielectricFunction(const double &e, double &eps1, double &eps2, const unsigned int &i=0)
std::string GetName() const
bool IsInArea(const double x, const double y, const double z)
bool GetMedium(const double x, const double y, const double z, Medium *&medium)
bool GetCluster(double &xcls, double &ycls, double &zcls, double &tcls, int &ncls, double &ecls, double &extra)
double GetStoppingPower()
bool NewTrack(const double x0, const double y0, const double z0, const double t0, const double dx0, const double dy0, const double dz0)
double GetClusterDensity()