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;
458 return 1. / emin - 1. / emax -
m_beta2 * log(emax / emin) / emax +
465 const double a = 1. / (3 * ec);
466 const double b = (emax - emin);
467 return 1. / emin - 1. / emax + a * b * (emin + e2 + emax) / e2 -
475 return 1. / emin - 1. / emax;
478double TrackPAI::ComputeDeDxTail(
const double emin,
const double emax) {
481 return -log(emin * (ek - emin) / (ek * ek)) +
482 (1. / (8 * (emin - ek) * ek * ek)) *
483 (-4 *
pow(emin, 3) + 4 * emin * emin * ek +
484 emin * ek * ek * (17. - 16. * CLog2) +
485 pow(ek, 3) * (-9. + 16. * CLog2));
486 }
else if (
m_mass == ElectronMass) {
489 return log(ek / emin) -
490 (ek - emin) * (ek - emin) *
491 (3. * emin * emin - 2. * emin * ek + 11. * ek * ek) /
497 return log(emax / emin) -
m_beta2 * (emax - emin) / emax;
501 return log(emax / emin) -
m_beta2 * (emax - emin) / emax +
508 return log(emax / emin) + (
pow(emax, 3) -
pow(emin, 3)) / (9. * e2 * ec) +
509 (emax * emax - emin * emin) / (6. * e2) +
511 (2. - (1. + emin / emax + 6 * ec / emax) *
m_beta2) /
520 return log(emax / emin);
523double TrackPAI::SampleAsymptoticCs(
double u)
const {
524 const double emin = m_energies.back();
526 u = (u - m_cdf.back()) / (1. - m_cdf.back());
529 return SampleAsymptoticCsElectron(emin, u);
530 }
else if (
m_mass == ElectronMass) {
531 return SampleAsymptoticCsPositron(emin, u);
537 return SampleAsymptoticCsSpinZero(emin, u);
541 return SampleAsymptoticCsSpinHalf(emin, u);
545 return SampleAsymptoticCsSpinOne(emin, u);
551 return emin * m_emax / (u * emin + (1. - u) * m_emax);
554double TrackPAI::SampleAsymptoticCsSpinZero(
const double emin,
double u)
const {
555 const double a = emin / m_emax;
557 u *= (1. - a + b * log(a));
558 double eLow = emin, eUp = m_emax;
559 while (eUp - eLow > 1.) {
560 const double eM = 0.5 * (eUp + eLow);
561 if (u >= 1. - emin / eM - b * log(eM / emin)) {
568 return 0.5 * (eLow + eUp);
571double TrackPAI::SampleAsymptoticCsSpinHalf(
const double emin,
double u)
const {
572 const double a = emin / m_emax;
575 u *= 1. - a + b * log(a) + (m_emax - emin) * c;
576 double eLow = emin, eUp = m_emax;
577 while (eUp - eLow > 1.) {
578 const double eM = 0.5 * (eUp + eLow);
579 if (u >= 1. - emin / eM - b * log(eM / emin) + (eM - emin) * c) {
586 return 0.5 * (eLow + eUp);
589double TrackPAI::SampleAsymptoticCsSpinOne(
const double emin,
double u)
const {
592 const double a = 2 * ec / e2 -
m_beta2 / m_emax;
593 const double b = 1.5 * ec / emin;
594 const double c = 1. - 1.5 * ec *
m_beta2 / m_emax;
595 u *= (m_emax - emin) * (0.5 * (emin + m_emax) / e2 + a + b / m_emax) +
596 c * log(m_emax / emin);
597 double eLow = emin, eUp = m_emax;
598 while (eUp - eLow > 1.) {
599 const double eM = 0.5 * (eUp + eLow);
601 (eM - emin) * ((emin + eM) / e2 + a + b / eM) + c * log(eM / emin)) {
608 return 0.5 * (eLow + eUp);
611double TrackPAI::SampleAsymptoticCsElectron(
const double emin,
double u)
const {
613 const double ek2 = ek * ek;
614 const double a = ek / (emin * (ek - emin));
615 const double norm = 1. / emin - 0.5 / ek - emin * emin / ((ek - emin) * ek2) -
618 double eLow = emin, eUp = m_emax;
619 while (eUp - eLow > 1.) {
620 const double eM = 0.5 * (eUp + eLow);
621 if (u >= a - 1. / eM + (eM - emin) / ek2 + 1. / (ek - eM)) {
627 return 0.5 * (eLow + eUp);
630double TrackPAI::SampleAsymptoticCsPositron(
const double emin,
double u)
const {
632 const double ek2 = ek * ek;
633 const double ek3 = ek2 * ek;
634 const double ek4 = 3 * ek3 * ek;
635 const double emin2 = emin * emin;
636 const double a = 1. / emin;
637 const double b = 3. / ek2;
638 const double c = 2. / ek;
639 u *= 1. / emin - 1. / m_emax + 3 * (m_emax - emin) / ek2 -
640 (m_emax - emin) * (m_emax + emin) / ek3 +
641 (m_emax - emin) * (emin * emin + emin * m_emax + m_emax * m_emax) / ek4 -
642 (2. / ek) * log(m_emax / emin);
643 double eLow = emin, eUp = m_emax;
644 while (eUp - eLow > 1.) {
645 const double eM = 0.5 * (eUp + eLow);
646 const double eM2 = eM * eM;
647 if (u >= a - 1. / eM + b * (eM - emin) - (eM2 - emin2) / ek3 +
648 (eM - emin) * (emin2 + emin * eM + eM2) / ek4 -
649 c * log(eM / emin)) {
656 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)