17 const double t0,
const double dx0,
const double dy0,
23 std::cerr <<
m_className <<
"::NewTrack: Sensor is not defined.\n";
31 <<
" No ionisable medium at initial position.\n";
35 if (medium->
GetName() != m_mediumName ||
38 if (!SetupMedium(medium)) {
39 std::cerr <<
m_className <<
"::NewTrack:\n Properties of medium "
40 << medium->
GetName() <<
" are not available.\n";
43 m_mediumName = medium->
GetName();
48 if (!SetupCrossSectionTable()) {
50 <<
" Calculation of ionisation cross-section failed.\n";
63 const double d = sqrt(dx * dx + dy * dy + dz * dz);
69 const double scale = 1. / d;
83 medium =
m_sensor->GetMedium(x, y, z);
85 medium->
GetName() != m_mediumName ||
91 std::pair<double, double> edep = SampleEnergyDeposit(
RndmUniform());
100 cluster.
energy = edep.first;
101 m_clusters.push_back(std::move(cluster));
107 double& tc,
int& nc,
double& ec,
double& extra) {
109 xc = yc = zc = tc = ec = extra = 0.;
110 if (m_clusters.empty() || m_cluster >= m_clusters.size())
return false;
111 const auto& cluster = m_clusters[m_cluster];
123std::pair<double, double> TrackPAI::SampleEnergyDeposit(
const double u)
const {
124 if (u > m_cdf.back()) {
126 return std::make_pair(SampleAsymptoticCs(u), 1.);
129 if (u <= m_cdf[0])
return std::make_pair(m_energies[0], 0.);
130 if (u >= 1.)
return std::make_pair(m_energies.back(), 0.);
134 const auto begin = m_cdf.cbegin();
135 const auto it1 = std::upper_bound(begin, m_cdf.cend(), u);
136 if (it1 == m_cdf.cbegin())
return std::make_pair(m_energies[0], 0.);
137 const auto it0 = std::prev(it1);
138 const double c0 = *it0;
139 const double c1 = *it1;
140 const double e0 = m_energies[it0 - begin];
141 const double e1 = m_energies[it1 - begin];
142 const double r0 = m_rutherford[it0 - begin];
143 const double r1 = m_rutherford[it1 - begin];
145 const double f1 = (u - c0) / (c1 - c0);
146 const double f0 = 1. - f1;
147 return std::make_pair(f0 * e0 + f1 * e1, f0 * r0 + f1 * r1);
149 const double loge0 = log(e0);
150 const double loge1 = log(e1);
151 const double logc0 = log(c0);
152 const double logc1 = log(c1);
153 const double f1 = (log(u) - logc0) / (logc1 - logc0);
154 const double f0 = 1. - f1;
155 const double edep =
exp(f0 * loge0 + f1 * loge1);
156 return std::make_pair(edep, f0 * r0 + f1 * r1);
159bool TrackPAI::SetupMedium(
Medium* medium) {
162 std::cerr <<
m_className <<
"::SetupMedium: Null pointer.\n";
167 m_electronDensity = medium->GetNumberDensity() * medium->GetAtomicNumber();
168 if (m_electronDensity <= 0.) {
170 <<
" Unphysical electron density (" << m_electronDensity
177 if (!medium->GetOpticalDataRange(emin, emax)) {
179 std::cerr <<
" Could not load optical data for medium " << m_mediumName
185 if (emin < Small) emin = Small;
194 const double r =
pow(emax / emin, 1. /
double(m_nSteps));
195 double eC = 0.5 * emin * (1. + r);
196 for (
size_t i = 0; i < m_nSteps; ++i) {
197 double eps1 = 0., eps2 = 0.;
198 medium->GetDielectricFunction(eC, eps1, eps2);
207 double integral = 0.;
208 double f1 = m_energies[0] * LossFunction(m_eps1[0], m_eps2[0]);
210 for (
size_t i = 1; i < m_nSteps; ++i) {
211 f2 = m_energies[i] * LossFunction(m_eps1[i], m_eps2[i]);
212 const double eM = 0.5 * (m_energies[i - 1] + m_energies[i]);
213 double eps1 = 0., eps2 = 0.;
214 medium->GetDielectricFunction(eM, eps1, eps2);
215 const double fM = eM * LossFunction(eps1, eps2);
217 integral += (f1 + 4 * fM + f2) * (m_energies[i] - m_energies[i - 1]) / 6.;
218 m_epsInt[i] = integral;
223 const double trk = 2 * Pi2 * FineStructureConstant *
pow(HbarC, 3) *
224 m_electronDensity / ElectronMass;
225 if (
fabs(integral - trk) > 0.2 * trk) {
227 <<
" Deviation from Thomas-Reiche-Kuhn sum rule by > 20%.\n"
228 <<
" Optical data are probably incomplete or erroneous!\n";
235 if (SetupCrossSectionTable()) {
238 std::cerr <<
m_className <<
"::GetClusterDensity:\n";
239 std::cerr <<
" Ionisation cross-section could not be calculated.\n";
249 if (SetupCrossSectionTable()) {
252 std::cerr <<
m_className <<
"::GetStoppingPower:\n";
253 std::cerr <<
" Ionisation cross-section could not be calculated.\n";
260bool TrackPAI::SetupCrossSectionTable() {
269 const double c1 = 2. * Pi2 * FineStructureConstant * pow(HbarC, 3) *
270 m_electronDensity / ElectronMass;
271 const double c2 =
m_q *
m_q * FineStructureConstant / (
m_beta2 * Pi * HbarC);
274 m_emax = ComputeMaxTransfer();
276 std::ofstream outfile;
277 if (
m_debug) outfile.open(
"dcs.txt", std::ios::out);
280 std::vector<double> dcs;
281 m_rutherford.fill(0.);
282 for (
size_t i = 0; i < m_nSteps; ++i) {
284 const double egamma = m_energies[i];
285 const double eps1 = m_eps1[i];
286 const double eps2 = m_eps2[i];
287 const double integral = m_epsInt[i];
290 double dcsLog = 0., dcsDensity = 0., dcsCher = 0.;
293 const double lf = LossFunction(eps1, eps2);
295 dcsLog = lf * log(2 * ElectronMass *
m_beta2 / egamma);
297 const double u = 1. -
m_beta2 * eps1;
298 const double v =
m_beta2 * eps2;
299 dcsDensity = -0.5 * lf * log(u * u + v * v);
301 dcsCher = (
m_beta2 - eps1 / (eps1 * eps1 + eps2 * eps2)) *
302 (HalfPi - atan(u / v));
303 }
else if (eps1 > 1. /
m_beta2) {
305 dcsCher = Pi * (
m_beta2 - 1. / eps1);
311 if (egamma > 0. && integral > 0.) {
312 dcsRuth = integral / (egamma * egamma);
313 f = dcsRuth / (dcsLog + dcsDensity + dcsCher);
316 dcs.push_back(dcsLog + dcsDensity + dcsCher + dcsRuth);
319 outfile << egamma <<
" " << eps1 <<
" " << eps2 <<
" " << dcsLog * c2
320 <<
" " << dcsDensity * c2 <<
" " << dcsCher * c2 <<
" "
321 << dcsRuth * c2 <<
"\n";
331 for (
size_t i = 1; i < m_nSteps; ++i) {
332 const double e0 = m_energies[i - 1];
333 const double e1 = m_energies[i];
334 const double de = e1 - e0;
335 const double dcs0 = dcs[i - 1];
336 const double dcs1 = dcs[i];
337 cs += 0.5 * (dcs0 + dcs1) * de;
339 m_dedx += 0.5 * (dcs0 * e0 + dcs1 * e1) * de;
344 const double elim = m_energies.back();
346 cs += c1 * ComputeCsTail(elim, m_emax);
347 m_dedx += c1 * ComputeDeDxTail(elim, m_emax);
349 std::cerr <<
m_className <<
"::SetupCrossSectionTable:\n";
350 std::cerr <<
" Max. energy transfer lower than optical data range.\n";
354 std::cerr <<
m_className <<
"::SetupCrossSectionTable:\n";
355 std::cerr <<
" Total cross-section <= 0.\n";
360 for (
size_t i = 0; i < m_nSteps; ++i) m_cdf[i] /= cs;
371double TrackPAI::ComputeMaxTransfer()
const {
381 return 2. * mass2 * ElectronMass * bg2 /
382 (mass2 + ElectronMass * ElectronMass + 2. *
m_energy * ElectronMass);
385double TrackPAI::ComputeCsTail(
const double emin,
const double emax) {
389 return 1. / emin - 1. / emax - 2 * emin / (ek * ek) -
390 emin * emin / ((ek - emin) * ek * ek);
391 }
else if (
fabs(
m_mass - ElectronMass) < 0.1) {
394 return 1. / emin - 1. / emax + 3 * (emax - emin) / (ek * ek) -
395 (emax - emin) * (ek * (emax + emin) +
396 (emin * emin + emin * emax + emax * emax) / 3.) /
398 (2. / ek) * log(emax / emin);
404 return 1. / emin - 1. / emax -
m_beta2 * log(emax / emin) / emax;
407 return 1. / emin - 1. / emax -
m_beta2 * log(emax / emin) / emax +
413 const double a = 1. / (3 * ec);
414 const double b = (emax - emin);
415 return 1. / emin - 1. / emax + a * b * (emin + e2 + emax) / e2 -
422 return 1. / emin - 1. / emax;
425double TrackPAI::ComputeDeDxTail(
const double emin,
const double emax) {
428 return -log(emin * (ek - emin) / (ek * ek)) +
429 (1. / (8 * (emin - ek) * ek * ek)) *
430 (-4 *
pow(emin, 3) + 4 * emin * emin * ek +
431 emin * ek * ek * (17. - 16. * CLog2) +
432 pow(ek, 3) * (-9. + 16. * CLog2));
433 }
else if (
fabs(
m_mass - ElectronMass) < 0.1) {
436 return log(ek / emin) -
437 (ek - emin) * (ek - emin) *
438 (3. * emin * emin - 2. * emin * ek + 11. * ek * ek) /
444 return log(emax / emin) -
m_beta2 * (emax - emin) / emax;
447 return log(emax / emin) -
m_beta2 * (emax - emin) / emax +
453 return log(emax / emin) + (
pow(emax, 3) -
pow(emin, 3)) / (9. * e2 * ec) +
454 (emax * emax - emin * emin) / (6. * e2) +
456 (2. - (1. + emin / emax + 6 * ec / emax) *
m_beta2) /
464 return log(emax / emin);
467double TrackPAI::SampleAsymptoticCs(
double u)
const {
468 const double emin = m_energies.back();
470 u = (u - m_cdf.back()) / (1. - m_cdf.back());
473 return SampleAsymptoticCsElectron(emin, u);
474 }
else if (
fabs(
m_mass - ElectronMass) < 0.1) {
475 return SampleAsymptoticCsPositron(emin, u);
481 return SampleAsymptoticCsSpinZero(emin, u);
484 return SampleAsymptoticCsSpinHalf(emin, u);
487 return SampleAsymptoticCsSpinOne(emin, u);
492 return emin * m_emax / (u * emin + (1. - u) * m_emax);
495double TrackPAI::SampleAsymptoticCsSpinZero(
const double emin,
double u)
const {
496 const double a = emin / m_emax;
498 u *= (1. - a + b * log(a));
499 double eLow = emin, eUp = m_emax;
500 while (eUp - eLow > 1.) {
501 const double eM = 0.5 * (eUp + eLow);
502 if (u >= 1. - emin / eM - b * log(eM / emin)) {
509 return 0.5 * (eLow + eUp);
512double TrackPAI::SampleAsymptoticCsSpinHalf(
const double emin,
double u)
const {
513 const double a = emin / m_emax;
516 u *= 1. - a + b * log(a) + (m_emax - emin) * c;
517 double eLow = emin, eUp = m_emax;
518 while (eUp - eLow > 1.) {
519 const double eM = 0.5 * (eUp + eLow);
520 if (u >= 1. - emin / eM - b * log(eM / emin) + (eM - emin) * c) {
527 return 0.5 * (eLow + eUp);
530double TrackPAI::SampleAsymptoticCsSpinOne(
const double emin,
double u)
const {
533 const double a = 2 * ec / e2 -
m_beta2 / m_emax;
534 const double b = 1.5 * ec / emin;
535 const double c = 1. - 1.5 * ec *
m_beta2 / m_emax;
536 u *= (m_emax - emin) * (0.5 * (emin + m_emax) / e2 + a + b / m_emax) +
537 c * log(m_emax / emin);
538 double eLow = emin, eUp = m_emax;
539 while (eUp - eLow > 1.) {
540 const double eM = 0.5 * (eUp + eLow);
542 (eM - emin) * ((emin + eM) / e2 + a + b / eM) + c * log(eM / emin)) {
549 return 0.5 * (eLow + eUp);
552double TrackPAI::SampleAsymptoticCsElectron(
const double emin,
double u)
const {
554 const double ek2 = ek * ek;
555 const double a = ek / (emin * (ek - emin));
556 const double norm = 1. / emin - 0.5 / ek - emin * emin / ((ek - emin) * ek2) -
559 double eLow = emin, eUp = m_emax;
560 while (eUp - eLow > 1.) {
561 const double eM = 0.5 * (eUp + eLow);
562 if (u >= a - 1. / eM + (eM - emin) / ek2 + 1. / (ek - eM)) {
568 return 0.5 * (eLow + eUp);
571double TrackPAI::SampleAsymptoticCsPositron(
const double emin,
double u)
const {
573 const double ek2 = ek * ek;
574 const double ek3 = ek2 * ek;
575 const double ek4 = 3 * ek3 * ek;
576 const double emin2 = emin * emin;
577 const double a = 1. / emin;
578 const double b = 3. / ek2;
579 const double c = 2. / ek;
580 u *= 1. / emin - 1. / m_emax + 3 * (m_emax - emin) / ek2 -
581 (m_emax - emin) * (m_emax + emin) / ek3 +
582 (m_emax - emin) * (emin * emin + emin * m_emax + m_emax * m_emax) / ek4 -
583 (2. / ek) * log(m_emax / emin);
584 double eLow = emin, eUp = m_emax;
585 while (eUp - eLow > 1.) {
586 const double eM = 0.5 * (eUp + eLow);
587 const double eM2 = eM * eM;
588 if (u >= a - 1. / eM + b * (eM - emin) - (eM2 - emin2) / ek3 +
589 (eM - emin) * (emin2 + emin * eM + eM2) / ek4 -
590 c * log(eM / emin)) {
597 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 NewTrack(const double x0, const double y0, const double z0, const double t0, const double dx0, const double dy0, const double dz0) override
bool GetCluster(double &xc, double &yc, double &zc, double &tc, int &nc, double &ec, double &extra) override
double GetClusterDensity() override
double GetStoppingPower() override
Get the stopping power (mean energy loss [eV] per cm).
double GetKineticEnergy() const
Return the kinetic energy of the projectile.
Track()=delete
Default constructor.
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)