30 m_electronDensity(0.) {
37 const double t0,
const double dx0,
const double dy0,
44 std::cerr <<
m_className <<
"::NewTrack: Sensor is not defined.\n";
51 std::cerr <<
m_className <<
"::NewTrack: No medium at initial position.\n";
56 <<
" Medium at initial position is not ionisable.\n";
60 if (medium->
GetName() != m_mediumName ||
63 if (!SetupMedium(medium)) {
64 std::cerr <<
m_className <<
"::NewTrack:\n Properties of medium "
65 << medium->
GetName() <<
" are not available.\n";
68 m_mediumName = medium->
GetName();
75 if (!SetupCrossSectionTable()) {
77 <<
" Calculation of ionisation cross-section failed.\n";
88 const double d = sqrt(dx0 * dx0 + dy0 * dy0 + dz0 * dz0);
92 <<
" Direction vector has zero norm.\n"
93 <<
" Initial direction is randomized.\n";
106 double& tcls,
int& ncls,
double& edep,
118 std::cerr <<
" Track not initialized. Call NewTrack first.\n";
123 if (SetupCrossSectionTable()) {
127 std::cerr <<
" Calculation of ionisation cross-section failed.\n";
145 if (medium->
GetName() != m_mediumName ||
173 std::cout <<
" Fraction of Rutherford scattering: " << f <<
"\n";
178double TrackPAI::SampleEnergyDeposit(
const double u,
double& f)
const {
180 if (u > m_cdf.back()) {
183 return SampleAsymptoticCs(u);
186 if (u <= m_cdf[0])
return m_energies[0];
187 if (u >= 1.)
return m_energies.back();
192 int iUp = m_cdf.size();
193 while (iUp - iLow > 1) {
194 const int iMid = (iUp + iLow) >> 1;
195 if (u >= m_cdf[iMid]) {
201 const double e0 = m_energies[iLow];
202 const double e1 = m_energies[iUp];
203 const double c0 = m_cdf[iLow];
204 const double c1 = m_cdf[iUp];
205 const double r0 = m_rutherford[iLow];
206 const double r1 = m_rutherford[iUp];
208 const double edep = e0 + (u - c0) * (e1 - e0) / (c1 - c0);
209 f = r0 + (edep - e0) * (r1 - r0) / (e1 - e0);
212 const double loge0 = log(e0);
213 const double loge1 = log(e1);
214 const double logc0 = log(c0);
215 const double logc1 = log(c1);
216 double edep = loge0 + (log(u) - logc0) * (loge1 - loge0) / (logc1 - logc0);
217 f = r0 + (log(edep) - loge0) * (r1 - r0) / (loge1 - loge0);
222bool TrackPAI::SetupMedium(Medium* medium) {
226 std::cerr <<
m_className <<
"::SetupMedium: Null pointer.\n";
231 m_electronDensity = medium->GetNumberDensity() * medium->GetAtomicNumber();
232 if (m_electronDensity <= 0.) {
234 <<
" Unphysical electron density ("
235 << m_electronDensity <<
")\n";
241 if (!medium->GetOpticalDataRange(emin, emax)) {
243 std::cerr <<
" Could not load optical data for medium " << m_mediumName
249 if (emin < Small) emin = Small;
253 m_opticalDataTable.clear();
254 opticalData newEpsilon;
257 const double r =
pow(emax / emin, 1. /
double(m_nSteps));
260 double eC = 0.5 * emin * (1. + r);
261 for (
int i = 0; i < m_nSteps; ++i) {
262 medium->GetDielectricFunction(eC, eps1, eps2);
263 newEpsilon.eps1 = eps1;
264 newEpsilon.eps2 = eps2;
265 m_opticalDataTable.push_back(newEpsilon);
266 m_energies.push_back(eC);
271 m_opticalDataTable[0].integral = 0.;
272 double integral = 0.;
273 double f1 = m_energies[0] * LossFunction(m_opticalDataTable[0].eps1,
274 m_opticalDataTable[0].eps2);
276 for (
int i = 1; i < m_nSteps; ++i) {
278 LossFunction(m_opticalDataTable[i].eps1, m_opticalDataTable[i].eps2);
279 const double eM = 0.5 * (m_energies[i - 1] + m_energies[i]);
280 medium->GetDielectricFunction(eM, eps1, eps2);
281 const double fM = eM * LossFunction(eps1, eps2);
283 integral += (f1 + 4 * fM + f2) * (m_energies[i] - m_energies[i - 1]) / 6.;
284 m_opticalDataTable[i].integral = integral;
289 const double trk = 2 * Pi2 * FineStructureConstant *
pow(HbarC, 3) *
290 m_electronDensity / ElectronMass;
291 if (
fabs(integral - trk) > 0.2 * trk) {
293 std::cerr <<
" Deviation from Thomas-Reiche-Kuhn sum rule by > 20%.\n";
294 std::cerr <<
" Optical data are probably incomplete or erroneous!\n";
303 std::cerr <<
m_className <<
"::GetClusterDensity:\n";
304 std::cerr <<
" Track has not been initialized.\n";
309 if (SetupCrossSectionTable()) {
312 std::cerr <<
m_className <<
"::GetClusterDensity:\n";
313 std::cerr <<
" Ionisation cross-section could not be calculated.\n";
324 std::cerr <<
m_className <<
"::GetStoppingPower:\n";
325 std::cerr <<
" Track has not been initialised.\n";
330 if (SetupCrossSectionTable()) {
333 std::cerr <<
m_className <<
"::GetStoppingPower:\n";
334 std::cerr <<
" Ionisation cross-section could not be calculated.\n";
342bool TrackPAI::SetupCrossSectionTable() {
345 std::cerr <<
m_className <<
"::SetupCrossSectionTable:\n"
346 <<
" Medium not set up.\n";
350 const double c1 = 2. * Pi2 * FineStructureConstant * pow(HbarC, 3) *
351 m_electronDensity / ElectronMass;
352 const double c2 =
m_q *
m_q * FineStructureConstant / (
m_beta2 * Pi * HbarC);
355 m_emax = ComputeMaxTransfer();
357 std::ofstream outfile;
358 if (
m_debug) outfile.open(
"dcs.txt", std::ios::out);
361 std::vector<double> dcs;
362 m_rutherford.clear();
364 for (
int i = 0; i < m_nSteps; ++i) {
366 const double egamma = m_energies[i];
367 const double eps1 = m_opticalDataTable[i].eps1;
368 const double eps2 = m_opticalDataTable[i].eps2;
369 const double integral = m_opticalDataTable[i].integral;
372 double dcsLog = 0., dcsDensity = 0., dcsCher = 0.;
375 const double lf = LossFunction(eps1, eps2);
377 dcsLog = lf * log(2 * ElectronMass *
m_beta2 / egamma);
379 const double u = 1. -
m_beta2 * eps1;
380 const double v =
m_beta2 * eps2;
381 dcsDensity = -0.5 * lf * log(u * u + v * v);
384 (
m_beta2 - eps1 / (eps1 * eps1 + eps2 * eps2)) * (HalfPi - atan(u / v));
385 }
else if (eps1 > 1. /
m_beta2) {
387 dcsCher = Pi * (
m_beta2 - 1. / eps1);
393 if (egamma > 0. && integral > 0.) {
394 dcsRuth = integral / (egamma * egamma);
395 f = dcsRuth / (dcsLog + dcsDensity + dcsCher);
397 m_rutherford.push_back(f);
398 dcs.push_back(dcsLog + dcsDensity + dcsCher + dcsRuth);
401 outfile << egamma <<
" " << eps1 <<
" " << eps2 <<
" " << dcsLog* c2
402 <<
" " << dcsDensity* c2 <<
" " << dcsCher* c2 <<
" "
403 << dcsRuth* c2 <<
"\n";
414 for (
int i = 1; i < m_nSteps; ++i) {
415 const double e0 = m_energies[i - 1];
416 const double e1 = m_energies[i];
417 const double de = e1 - e0;
418 const double dcs0 = dcs[i - 1];
419 const double dcs1 = dcs[i];
420 cs += 0.5 * (dcs0 + dcs1) * de;
422 m_dedx += 0.5 * (dcs0 * e0 + dcs1 * e1) * de;
427 const double elim = m_energies.back();
429 cs += c1 * ComputeCsTail(elim, m_emax);
430 m_dedx += c1 * ComputeDeDxTail(elim, m_emax);
432 std::cerr <<
m_className <<
"::SetupCrossSectionTable:\n";
433 std::cerr <<
" Max. energy transfer lower than optical data range.\n";
437 std::cerr <<
m_className <<
"::SetupCrossSectionTable:\n";
438 std::cerr <<
" Total cross-section <= 0.\n";
443 for (
int i = m_nSteps; i--;) m_cdf[i] /= cs;
454double TrackPAI::ComputeMaxTransfer()
const {
465 return 2. * mass2 * ElectronMass * bg2 /
466 (mass2 + ElectronMass * ElectronMass + 2. *
m_energy * ElectronMass);
469double TrackPAI::ComputeCsTail(
const double emin,
const double emax) {
474 return 1. / emin - 1. / emax - 2 * emin / (ek * ek) -
475 emin * emin / ((ek - emin) * ek * ek);
476 }
else if (
m_mass == ElectronMass) {
479 return 1. / emin - 1. / emax + 3 * (emax - emin) / (ek * ek) -
480 (emax - emin) * (ek * (emax + emin) +
481 (emin * emin + emin * emax + emax * emax) / 3.) /
483 (2. / ek) * log(emax / emin);
489 return 1. / emin - 1. / emax -
m_beta2 * log(emax / emin) / emax;
493 return 1. / emin - 1. / emax -
m_beta2 * log(emax / emin) / emax +
500 const double a = 1. / (3 * ec);
501 const double b = (emax - emin);
502 return 1. / emin - 1. / emax + a * b * (emin + e2 + emax) / e2 -
510 return 1. / emin - 1. / emax;
513double TrackPAI::ComputeDeDxTail(
const double emin,
const double emax) {
517 return -log(emin * (ek - emin) / (ek * ek)) +
518 (1. / (8 * (emin - ek) * ek * ek)) *
519 (-4 *
pow(emin, 3) + 4 * emin * emin * ek +
520 emin * ek * ek * (17. - 16. * CLog2) +
521 pow(ek, 3) * (-9. + 16. * CLog2));
522 }
else if (
m_mass == ElectronMass) {
525 return log(ek / emin) -
526 (ek - emin) * (ek - emin) *
527 (3. * emin * emin - 2. * emin * ek + 11. * ek * ek) /
533 return log(emax / emin) -
m_beta2 * (emax - emin) / emax;
537 return log(emax / emin) -
m_beta2 * (emax - emin) / emax +
544 return log(emax / emin) + (
pow(emax, 3) -
pow(emin, 3)) / (9. * e2 * ec) +
545 (emax * emax - emin * emin) / (6. * e2) + (emax - emin) *
546 (2. - (1. + emin / emax + 6 * ec / emax) *
m_beta2) / (6. * ec);
554 return log(emax / emin);
557double TrackPAI::SampleAsymptoticCs(
double u)
const {
559 const double emin = m_energies.back();
561 u = (u - m_cdf.back()) / (1. - m_cdf.back());
564 return SampleAsymptoticCsElectron(emin, u);
565 }
else if (
m_mass == ElectronMass) {
566 return SampleAsymptoticCsPositron(emin, u);
572 return SampleAsymptoticCsSpinZero(emin, u);
576 return SampleAsymptoticCsSpinHalf(emin, u);
580 return SampleAsymptoticCsSpinOne(emin, u);
586 return emin * m_emax / (u * emin + (1. - u) * m_emax);
589double TrackPAI::SampleAsymptoticCsSpinZero(
const double emin,
double u)
const {
591 const double a = emin / m_emax;
593 u *= (1. - a + b * log(a));
594 double eLow = emin, eUp = m_emax;
595 while (eUp - eLow > 1.) {
596 const double eM = 0.5 * (eUp + eLow);
597 if (u >= 1. - emin / eM - b * log(eM / emin)) {
604 return 0.5 * (eLow + eUp);
607double TrackPAI::SampleAsymptoticCsSpinHalf(
const double emin,
double u)
const {
609 const double a = emin / m_emax;
612 u *= 1. - a + b * log(a) + (m_emax - emin) * c;
613 double eLow = emin, eUp = m_emax;
614 while (eUp - eLow > 1.) {
615 const double eM = 0.5 * (eUp + eLow);
616 if (u >= 1. - emin / eM - b * log(eM / emin) + (eM - emin) * c) {
623 return 0.5 * (eLow + eUp);
626double TrackPAI::SampleAsymptoticCsSpinOne(
const double emin,
double u)
const {
630 const double a = 2 * ec / e2 -
m_beta2 / m_emax;
631 const double b = 1.5 * ec / emin;
632 const double c = 1. - 1.5 * ec *
m_beta2 / m_emax;
633 u *= (m_emax - emin) * (0.5 * (emin + m_emax) / e2 + a + b / m_emax) +
634 c * log(m_emax / emin);
635 double eLow = emin, eUp = m_emax;
636 while (eUp - eLow > 1.) {
637 const double eM = 0.5 * (eUp + eLow);
639 (eM - emin) * ((emin + eM) / e2 + a + b / eM) + c * log(eM / emin)) {
646 return 0.5 * (eLow + eUp);
649double TrackPAI::SampleAsymptoticCsElectron(
const double emin,
double u)
const {
652 const double ek2 = ek * ek;
653 const double a = ek / (emin * (ek - emin));
654 const double norm = 1. / emin - 0.5 / ek - emin * emin / ((ek - emin) * ek2) -
657 double eLow = emin, eUp = m_emax;
658 while (eUp - eLow > 1.) {
659 const double eM = 0.5 * (eUp + eLow);
660 if (u >= a - 1. / eM + (eM - emin) / ek2 + 1. / (ek - eM)) {
666 return 0.5 * (eLow + eUp);
669double TrackPAI::SampleAsymptoticCsPositron(
const double emin,
double u)
const {
672 const double ek2 = ek * ek;
673 const double ek3 = ek2 * ek;
674 const double ek4 = 3 * ek3 * ek;
675 const double emin2 = emin * emin;
676 const double a = 1. / emin;
677 const double b = 3. / ek2;
678 const double c = 2. / ek;
679 u *= 1. / emin - 1. / m_emax + 3 * (m_emax - emin) / ek2 -
680 (m_emax - emin) * (m_emax + emin) / ek3 +
681 (m_emax - emin) * (emin * emin + emin * m_emax + m_emax * m_emax) / ek4 -
682 (2. / ek) * log(m_emax / emin);
683 double eLow = emin, eUp = m_emax;
684 while (eUp - eLow > 1.) {
685 const double eM = 0.5 * (eUp + eLow);
686 const double eM2 = eM * eM;
687 if (u >= a - 1. / eM + b * (eM - emin) - (eM2 - emin2) / ek3 +
688 (eM - emin) * (emin2 + emin * eM + eM2) / ek4 -
689 c * log(eM / emin)) {
696 return 0.5 * (eLow + eUp);
Abstract base class for media.
virtual double GetNumberDensity() const
const std::string & GetName() const
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()
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)