17 const double t0,
const double dx0,
const double dy0,
23 std::cerr <<
m_className <<
"::NewTrack: Sensor is not defined.\n";
30 std::cerr <<
m_className <<
"::NewTrack: No medium at initial position.\n";
35 <<
" Medium at initial position is not ionisable.\n";
39 if (medium->
GetName() != m_mediumName ||
42 if (!SetupMedium(medium)) {
43 std::cerr <<
m_className <<
"::NewTrack:\n Properties of medium "
44 << medium->
GetName() <<
" are not available.\n";
47 m_mediumName = medium->
GetName();
54 if (!SetupCrossSectionTable()) {
56 <<
" Calculation of ionisation cross-section failed.\n";
67 const double d = sqrt(dx0 * dx0 + dy0 * dy0 + dz0 * dz0);
71 <<
" Direction vector has zero norm.\n"
72 <<
" Initial direction is randomized.\n";
85 double& tcls,
int& ncls,
double& edep,
96 std::cerr <<
" Track not initialized. Call NewTrack first.\n";
101 if (SetupCrossSectionTable()) {
105 std::cerr <<
" Calculation of ionisation cross-section failed.\n";
123 if (medium->
GetName() != m_mediumName ||
151 std::cout <<
" Fraction of Rutherford scattering: " << f <<
"\n";
156double TrackPAI::SampleEnergyDeposit(
const double u,
double& f)
const {
157 if (u > m_cdf.back()) {
160 return SampleAsymptoticCs(u);
163 if (u <= m_cdf[0])
return m_energies[0];
164 if (u >= 1.)
return m_energies.back();
168 const auto begin = m_cdf.cbegin();
169 const auto it1 = std::upper_bound(begin, m_cdf.cend(), u);
170 if (it1 == m_cdf.cbegin())
return m_energies[0];
171 const auto it0 = std::prev(it1);
172 const double c0 = *it0;
173 const double c1 = *it1;
174 const double e0 = m_energies[it0 - begin];
175 const double e1 = m_energies[it1 - begin];
176 const double r0 = m_rutherford[it0 - begin];
177 const double r1 = m_rutherford[it1 - begin];
179 const double edep = e0 + (u - c0) * (e1 - e0) / (c1 - c0);
180 f = r0 + (edep - e0) * (r1 - r0) / (e1 - e0);
183 const double loge0 = log(e0);
184 const double loge1 = log(e1);
185 const double logc0 = log(c0);
186 const double logc1 = log(c1);
187 double edep = loge0 + (log(u) - logc0) * (loge1 - loge0) / (logc1 - logc0);
188 f = r0 + (log(edep) - loge0) * (r1 - r0) / (loge1 - loge0);
193bool TrackPAI::SetupMedium(Medium* medium) {
196 std::cerr <<
m_className <<
"::SetupMedium: Null pointer.\n";
201 m_electronDensity = medium->GetNumberDensity() * medium->GetAtomicNumber();
202 if (m_electronDensity <= 0.) {
204 <<
" Unphysical electron density (" << m_electronDensity
211 if (!medium->GetOpticalDataRange(emin, emax)) {
213 std::cerr <<
" Could not load optical data for medium " << m_mediumName
219 if (emin < Small) emin = Small;
223 m_opticalDataTable.clear();
224 opticalData newEpsilon;
227 const double r =
pow(emax / emin, 1. /
double(m_nSteps));
230 double eC = 0.5 * emin * (1. + r);
231 for (
int i = 0; i < m_nSteps; ++i) {
232 medium->GetDielectricFunction(eC, eps1, eps2);
233 newEpsilon.eps1 = eps1;
234 newEpsilon.eps2 = eps2;
235 m_opticalDataTable.push_back(newEpsilon);
236 m_energies.push_back(eC);
241 m_opticalDataTable[0].integral = 0.;
242 double integral = 0.;
243 double f1 = m_energies[0] * LossFunction(m_opticalDataTable[0].eps1,
244 m_opticalDataTable[0].eps2);
246 for (
int i = 1; i < m_nSteps; ++i) {
248 LossFunction(m_opticalDataTable[i].eps1, m_opticalDataTable[i].eps2);
249 const double eM = 0.5 * (m_energies[i - 1] + m_energies[i]);
250 medium->GetDielectricFunction(eM, eps1, eps2);
251 const double fM = eM * LossFunction(eps1, eps2);
253 integral += (f1 + 4 * fM + f2) * (m_energies[i] - m_energies[i - 1]) / 6.;
254 m_opticalDataTable[i].integral = integral;
259 const double trk = 2 * Pi2 * FineStructureConstant *
pow(HbarC, 3) *
260 m_electronDensity / ElectronMass;
261 if (
fabs(integral - trk) > 0.2 * trk) {
263 std::cerr <<
" Deviation from Thomas-Reiche-Kuhn sum rule by > 20%.\n";
264 std::cerr <<
" Optical data are probably incomplete or erroneous!\n";
272 std::cerr <<
m_className <<
"::GetClusterDensity:\n";
273 std::cerr <<
" Track has not been initialized.\n";
278 if (SetupCrossSectionTable()) {
281 std::cerr <<
m_className <<
"::GetClusterDensity:\n";
282 std::cerr <<
" Ionisation cross-section could not be calculated.\n";
292 std::cerr <<
m_className <<
"::GetStoppingPower:\n";
293 std::cerr <<
" Track has not been initialised.\n";
298 if (SetupCrossSectionTable()) {
301 std::cerr <<
m_className <<
"::GetStoppingPower:\n";
302 std::cerr <<
" Ionisation cross-section could not be calculated.\n";
310bool TrackPAI::SetupCrossSectionTable() {
312 std::cerr <<
m_className <<
"::SetupCrossSectionTable:\n"
313 <<
" Medium not set up.\n";
317 const double c1 = 2. * Pi2 * FineStructureConstant * pow(HbarC, 3) *
318 m_electronDensity / ElectronMass;
319 const double c2 =
m_q *
m_q * FineStructureConstant / (
m_beta2 * Pi * HbarC);
322 m_emax = ComputeMaxTransfer();
324 std::ofstream outfile;
325 if (
m_debug) outfile.open(
"dcs.txt", std::ios::out);
328 std::vector<double> dcs;
329 m_rutherford.clear();
331 for (
int i = 0; i < m_nSteps; ++i) {
333 const double egamma = m_energies[i];
334 const double eps1 = m_opticalDataTable[i].eps1;
335 const double eps2 = m_opticalDataTable[i].eps2;
336 const double integral = m_opticalDataTable[i].integral;
339 double dcsLog = 0., dcsDensity = 0., dcsCher = 0.;
342 const double lf = LossFunction(eps1, eps2);
344 dcsLog = lf * log(2 * ElectronMass *
m_beta2 / egamma);
346 const double u = 1. -
m_beta2 * eps1;
347 const double v =
m_beta2 * eps2;
348 dcsDensity = -0.5 * lf * log(u * u + v * v);
350 dcsCher = (
m_beta2 - eps1 / (eps1 * eps1 + eps2 * eps2)) *
351 (HalfPi - atan(u / v));
352 }
else if (eps1 > 1. /
m_beta2) {
354 dcsCher = Pi * (
m_beta2 - 1. / eps1);
360 if (egamma > 0. && integral > 0.) {
361 dcsRuth = integral / (egamma * egamma);
362 f = dcsRuth / (dcsLog + dcsDensity + dcsCher);
364 m_rutherford.push_back(f);
365 dcs.push_back(dcsLog + dcsDensity + dcsCher + dcsRuth);
368 outfile << egamma <<
" " << eps1 <<
" " << eps2 <<
" " << dcsLog * c2
369 <<
" " << dcsDensity * c2 <<
" " << dcsCher * c2 <<
" "
370 << dcsRuth * c2 <<
"\n";
381 for (
int i = 1; i < m_nSteps; ++i) {
382 const double e0 = m_energies[i - 1];
383 const double e1 = m_energies[i];
384 const double de = e1 - e0;
385 const double dcs0 = dcs[i - 1];
386 const double dcs1 = dcs[i];
387 cs += 0.5 * (dcs0 + dcs1) * de;
389 m_dedx += 0.5 * (dcs0 * e0 + dcs1 * e1) * de;
394 const double elim = m_energies.back();
396 cs += c1 * ComputeCsTail(elim, m_emax);
397 m_dedx += c1 * ComputeDeDxTail(elim, m_emax);
399 std::cerr <<
m_className <<
"::SetupCrossSectionTable:\n";
400 std::cerr <<
" Max. energy transfer lower than optical data range.\n";
404 std::cerr <<
m_className <<
"::SetupCrossSectionTable:\n";
405 std::cerr <<
" Total cross-section <= 0.\n";
410 for (
int i = m_nSteps; i--;) m_cdf[i] /= cs;
421double TrackPAI::ComputeMaxTransfer()
const {
431 return 2. * mass2 * ElectronMass * bg2 /
432 (mass2 + ElectronMass * ElectronMass + 2. *
m_energy * ElectronMass);
435double TrackPAI::ComputeCsTail(
const double emin,
const double emax) {
439 return 1. / emin - 1. / emax - 2 * emin / (ek * ek) -
440 emin * emin / ((ek - emin) * ek * ek);
441 }
else if (
m_mass == ElectronMass) {
444 return 1. / emin - 1. / emax + 3 * (emax - emin) / (ek * ek) -
445 (emax - emin) * (ek * (emax + emin) +
446 (emin * emin + emin * emax + emax * emax) / 3.) /
448 (2. / ek) * log(emax / emin);
454 return 1. / emin - 1. / emax -
m_beta2 * log(emax / emin) / emax;
457 return 1. / emin - 1. / emax -
m_beta2 * log(emax / emin) / emax +
463 const double a = 1. / (3 * ec);
464 const double b = (emax - emin);
465 return 1. / emin - 1. / emax + a * b * (emin + e2 + emax) / e2 -
472 return 1. / emin - 1. / emax;
475double TrackPAI::ComputeDeDxTail(
const double emin,
const double emax) {
478 return -log(emin * (ek - emin) / (ek * ek)) +
479 (1. / (8 * (emin - ek) * ek * ek)) *
480 (-4 *
pow(emin, 3) + 4 * emin * emin * ek +
481 emin * ek * ek * (17. - 16. * CLog2) +
482 pow(ek, 3) * (-9. + 16. * CLog2));
483 }
else if (
m_mass == ElectronMass) {
486 return log(ek / emin) -
487 (ek - emin) * (ek - emin) *
488 (3. * emin * emin - 2. * emin * ek + 11. * ek * ek) /
494 return log(emax / emin) -
m_beta2 * (emax - emin) / emax;
497 return log(emax / emin) -
m_beta2 * (emax - emin) / emax +
503 return log(emax / emin) + (
pow(emax, 3) -
pow(emin, 3)) / (9. * e2 * ec) +
504 (emax * emax - emin * emin) / (6. * e2) +
506 (2. - (1. + emin / emax + 6 * ec / emax) *
m_beta2) /
514 return log(emax / emin);
517double TrackPAI::SampleAsymptoticCs(
double u)
const {
518 const double emin = m_energies.back();
520 u = (u - m_cdf.back()) / (1. - m_cdf.back());
523 return SampleAsymptoticCsElectron(emin, u);
524 }
else if (
m_mass == ElectronMass) {
525 return SampleAsymptoticCsPositron(emin, u);
531 return SampleAsymptoticCsSpinZero(emin, u);
534 return SampleAsymptoticCsSpinHalf(emin, u);
537 return SampleAsymptoticCsSpinOne(emin, u);
542 return emin * m_emax / (u * emin + (1. - u) * m_emax);
545double TrackPAI::SampleAsymptoticCsSpinZero(
const double emin,
double u)
const {
546 const double a = emin / m_emax;
548 u *= (1. - a + b * log(a));
549 double eLow = emin, eUp = m_emax;
550 while (eUp - eLow > 1.) {
551 const double eM = 0.5 * (eUp + eLow);
552 if (u >= 1. - emin / eM - b * log(eM / emin)) {
559 return 0.5 * (eLow + eUp);
562double TrackPAI::SampleAsymptoticCsSpinHalf(
const double emin,
double u)
const {
563 const double a = emin / m_emax;
566 u *= 1. - a + b * log(a) + (m_emax - emin) * c;
567 double eLow = emin, eUp = m_emax;
568 while (eUp - eLow > 1.) {
569 const double eM = 0.5 * (eUp + eLow);
570 if (u >= 1. - emin / eM - b * log(eM / emin) + (eM - emin) * c) {
577 return 0.5 * (eLow + eUp);
580double TrackPAI::SampleAsymptoticCsSpinOne(
const double emin,
double u)
const {
583 const double a = 2 * ec / e2 -
m_beta2 / m_emax;
584 const double b = 1.5 * ec / emin;
585 const double c = 1. - 1.5 * ec *
m_beta2 / m_emax;
586 u *= (m_emax - emin) * (0.5 * (emin + m_emax) / e2 + a + b / m_emax) +
587 c * log(m_emax / emin);
588 double eLow = emin, eUp = m_emax;
589 while (eUp - eLow > 1.) {
590 const double eM = 0.5 * (eUp + eLow);
592 (eM - emin) * ((emin + eM) / e2 + a + b / eM) + c * log(eM / emin)) {
599 return 0.5 * (eLow + eUp);
602double TrackPAI::SampleAsymptoticCsElectron(
const double emin,
double u)
const {
604 const double ek2 = ek * ek;
605 const double a = ek / (emin * (ek - emin));
606 const double norm = 1. / emin - 0.5 / ek - emin * emin / ((ek - emin) * ek2) -
609 double eLow = emin, eUp = m_emax;
610 while (eUp - eLow > 1.) {
611 const double eM = 0.5 * (eUp + eLow);
612 if (u >= a - 1. / eM + (eM - emin) / ek2 + 1. / (ek - eM)) {
618 return 0.5 * (eLow + eUp);
621double TrackPAI::SampleAsymptoticCsPositron(
const double emin,
double u)
const {
623 const double ek2 = ek * ek;
624 const double ek3 = ek2 * ek;
625 const double ek4 = 3 * ek3 * ek;
626 const double emin2 = emin * emin;
627 const double a = 1. / emin;
628 const double b = 3. / ek2;
629 const double c = 2. / ek;
630 u *= 1. / emin - 1. / m_emax + 3 * (m_emax - emin) / ek2 -
631 (m_emax - emin) * (m_emax + emin) / ek3 +
632 (m_emax - emin) * (emin * emin + emin * m_emax + m_emax * m_emax) / ek4 -
633 (2. / ek) * log(m_emax / emin);
634 double eLow = emin, eUp = m_emax;
635 while (eUp - eLow > 1.) {
636 const double eM = 0.5 * (eUp + eLow);
637 const double eM2 = eM * eM;
638 if (u >= a - 1. / eM + b * (eM - emin) - (eM2 - emin2) / ek3 +
639 (eM - emin) * (emin2 + emin * eM + eM2) / ek4 -
640 c * log(eM / emin)) {
647 return 0.5 * (eLow + eUp);
Abstract base class for media.
virtual double GetNumberDensity() const
Get the number density [cm-3].
const std::string & GetName() const
Get the medium name/identifier.
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).
virtual bool GetCluster(double &xcls, double &ycls, double &zcls, double &tcls, int &ncls, double &ecls, double &extra)
virtual double GetStoppingPower()
Get the stopping power (mean energy loss [eV] per cm).
virtual bool NewTrack(const double x0, const double y0, const double z0, const double t0, const double dx0, const double dy0, const double dz0)
virtual double GetClusterDensity()
Abstract base class for track generation.
double RndmUniform()
Draw a random number uniformly distributed in the range [0, 1).
void RndmDirection(double &dx, double &dy, double &dz, const double length=1.)
Draw a random (isotropic) direction vector.
double RndmUniformPos()
Draw a random number uniformly distributed in the range (0, 1).
DoubleAc pow(const DoubleAc &f, double p)
DoubleAc exp(const DoubleAc &f)
DoubleAc fabs(const DoubleAc &f)