16 using namespace Acts::UnitLiterals;
21 constexpr
float Me = 0.5109989461_MeV;
23 constexpr
float K = 0.307075_MeV * 1
_cm * 1
_cm;
25 constexpr
float PlasmaEnergyScale = 28.816_eV;
28 struct RelativisticQuantities {
29 float q2OverBeta2 = 0.0f;
31 float betaGamma = 0.0f;
34 RelativisticQuantities(
float mass,
float qOverP,
float q) {
37 q2OverBeta2 = q * q + (mass * qOverP) * (mass * qOverP);
39 const auto mOverP = mass *
std::abs(qOverP / q);
40 const auto pOverM = 1.0f / mOverP;
42 beta2 = 1.0f / (1.0f + mOverP * mOverP);
46 gamma = std::sqrt(1.0
f + pOverM * pOverM);
51 inline float deriveBeta2(
float qOverP,
const RelativisticQuantities& rq) {
52 return -2 / (qOverP * rq.gamma * rq.gamma);
56 inline float computeMassTerm(
float mass,
const RelativisticQuantities& rq) {
57 return 2 * mass * rq.betaGamma * rq.betaGamma;
60 inline float logDeriveMassTerm(
float qOverP) {
68 inline float computeWMax(
float mass,
const RelativisticQuantities& rq) {
69 const auto mfrac = Me /
mass;
70 const auto nominator = 2 * Me * rq.betaGamma * rq.betaGamma;
71 const auto denonimator = 1.0f + 2 * rq.gamma * mfrac + mfrac * mfrac;
72 return nominator / denonimator;
75 inline float logDeriveWMax(
float mass,
float qOverP,
76 const RelativisticQuantities& rq) {
80 const auto a =
std::abs(qOverP / std::sqrt(rq.q2OverBeta2));
82 const auto b = Me * (1.0f + (mass / Me) * (mass / Me));
83 return -2 * (
a *
b - 2 + rq.beta2) / (qOverP * (
a *
b + 2));
94 inline float computeEpsilon(
float molarElectronDensity,
float thickness,
95 const RelativisticQuantities& rq) {
96 return 0.5f * K * molarElectronDensity * thickness * rq.q2OverBeta2;
99 inline float logDeriveEpsilon(
float qOverP,
const RelativisticQuantities& rq) {
101 return 2 / (qOverP * rq.gamma * rq.gamma);
109 inline float computeDeltaHalf(
float meanExitationPotential,
110 float molarElectronDensity,
111 const RelativisticQuantities& rq) {
113 if (rq.betaGamma < 10.0f) {
117 const auto plasmaEnergy = PlasmaEnergyScale * std::sqrt(molarElectronDensity);
118 return std::log(rq.betaGamma) +
119 std::log(plasmaEnergy / meanExitationPotential) - 0.5f;
122 inline float deriveDeltaHalf(
float qOverP,
const RelativisticQuantities& rq) {
127 return (rq.betaGamma < 10.0f) ? 0.0f : (-1.0f / qOverP);
131 #define ASSERT_INPUTS(mass, qOverP, q) \
132 assert((0 < mass) and "Mass must be positive"); \
133 assert((0 < (qOverP * q)) and "Inconsistent q/p and q signs");
136 int ,
float m,
float qOverP,
145 const auto I = slab.material().meanExcitationEnergy();
146 const auto Ne = slab.material().molarElectronDensity();
148 const auto rq = RelativisticQuantities(m, qOverP, q);
150 const auto dhalf = computeDeltaHalf(
I, Ne, rq);
151 const auto u = computeMassTerm(Me, rq);
152 const auto wmax = computeWMax(m, rq);
159 0.5f * std::log(
u /
I) + 0.5f * std::log(wmax /
I) - rq.beta2 - dhalf;
160 return eps * running;
164 int ,
float m,
float qOverP,
173 const auto I = slab.material().meanExcitationEnergy();
174 const auto Ne = slab.material().molarElectronDensity();
176 const auto rq = RelativisticQuantities(m, qOverP, q);
178 const auto dhalf = computeDeltaHalf(
I, Ne, rq);
179 const auto u = computeMassTerm(Me, rq);
180 const auto wmax = computeWMax(m, rq);
192 const auto logDerEps = logDeriveEpsilon(qOverP, rq);
193 const auto derDHalf = deriveDeltaHalf(qOverP, rq);
194 const auto logDerU = logDeriveMassTerm(qOverP);
195 const auto logDerWmax = logDeriveWMax(m, qOverP, rq);
196 const auto derBeta2 = deriveBeta2(qOverP, rq);
197 const auto rel = logDerEps * (0.5f * std::log(
u /
I) +
198 0.5f * std::log(wmax /
I) - rq.beta2 - dhalf) +
199 0.5
f * logDerU + 0.5
f * logDerWmax - derBeta2 - derDHalf;
204 int ,
float m,
float qOverP,
213 const auto I = slab.material().meanExcitationEnergy();
214 const auto Ne = slab.material().molarElectronDensity();
216 const auto rq = RelativisticQuantities(m, qOverP, q);
218 const auto dhalf = computeDeltaHalf(
I, Ne, rq);
219 const auto t = computeMassTerm(m, rq);
222 std::log(
t /
I) + std::log(
eps /
I) + 0.2f - rq.beta2 - 2 * dhalf;
223 return eps * running;
228 float qOverP,
float q) {
236 const auto I = slab.material().meanExcitationEnergy();
237 const auto Ne = slab.material().molarElectronDensity();
239 const auto rq = RelativisticQuantities(m, qOverP, q);
241 const auto dhalf = computeDeltaHalf(
I, Ne, rq);
242 const auto t = computeMassTerm(m, rq);
254 const auto logDerEps = logDeriveEpsilon(qOverP, rq);
255 const auto derDHalf = deriveDeltaHalf(qOverP, rq);
256 const auto logDerT = logDeriveMassTerm(qOverP);
257 const auto derBeta2 = deriveBeta2(qOverP, rq);
258 const auto rel = logDerEps * (std::log(
t /
I) + std::log(
eps /
I) - 0.2f -
259 rq.beta2 - 2 * dhalf) +
260 logDerT + logDerEps - derBeta2 - 2 * derDHalf;
272 inline float convertLandauFwhmToGaussianSigma(
float fwhm) {
273 return fwhm / (2 * std::sqrt(2 * std::log(2.0
f)));
279 float qOverP,
float q) {
287 const auto Ne = slab.material().molarElectronDensity();
289 const auto rq = RelativisticQuantities(m, qOverP, q);
291 const auto fwhm = 4 * computeEpsilon(Ne,
thickness, rq);
292 return convertLandauFwhmToGaussianSigma(fwhm);
297 float qOverP,
float q) {
305 const auto Ne = slab.material().molarElectronDensity();
307 const auto rq = RelativisticQuantities(m, qOverP, q);
309 const auto fwhm = 4 * computeEpsilon(Ne,
thickness, rq);
310 const auto sigmaE = convertLandauFwhmToGaussianSigma(fwhm);
317 const auto pInv = qOverP / q;
318 return std::sqrt(rq.q2OverBeta2) * pInv * pInv * sigmaE;
323 inline float computeBremsstrahlungLossMean(
float mass,
float energy) {
324 return energy * (Me /
mass) * (Me / mass);
327 inline float deriveBremsstrahlungLossMeanE(
float mass) {
328 return (Me / mass) * (Me /
mass);
339 constexpr
float MuonHighLowThreshold = 1
_TeV;
341 constexpr
double MuonLow0 = -0.5345_MeV;
343 constexpr
double MuonLow1 = 6.803e-5;
345 constexpr
double MuonLow2 = 2.278e-11 / 1
_MeV;
347 constexpr
double MuonLow3 = -9.899e-18 / (1
_MeV * 1
_MeV);
349 constexpr
double MuonHigh0 = -2.986_MeV;
351 constexpr
double MuonHigh1 = 9.253e-5;
354 inline float computeMuonDirectPairPhotoNuclearLossMean(
double energy) {
355 if (energy < MuonHighLowThreshold) {
357 (MuonLow1 + (MuonLow2 + MuonLow3 *
energy) * energy) *
energy;
359 return MuonHigh0 + MuonHigh1 *
energy;
363 inline float deriveMuonDirectPairPhotoNuclearLossMeanE(
double energy) {
364 if (energy < MuonHighLowThreshold) {
365 return MuonLow1 + (2 * MuonLow2 + 3 * MuonLow3 *
energy) * energy;
373 float m,
float qOverP,
float q) {
382 const auto x = slab.thicknessInX0();
387 auto dEdx = computeBremsstrahlungLossMean(m,
energy);
390 dEdx += computeMuonDirectPairPhotoNuclearLossMean(
energy);
397 int pdg,
float m,
float qOverP,
407 const auto x = slab.thicknessInX0();
413 auto derE = deriveBremsstrahlungLossMeanE(m);
416 derE += deriveMuonDirectPairPhotoNuclearLossMeanE(
energy);
424 const auto derQOverP = -(q * q) / (qOverP * qOverP * qOverP *
energy);
425 return derE * derQOverP *
x;
429 float m,
float qOverP,
float q) {
435 float m,
float qOverP,
float q) {
441 float m,
float qOverP,
float q) {
449 float m,
float qOverP,
float q) {
458 inline float theta0Highland(
float xOverX0,
float momentumInv,
461 const auto t = std::sqrt(xOverX0 * q2OverBeta2);
464 return 13.6_MeV * momentumInv *
t * (1.0f + 0.038f * 2 * std::log(
t));
467 inline float theta0RossiGreisen(
float xOverX0,
float momentumInv,
470 const auto t = std::sqrt(xOverX0 * q2OverBeta2);
471 return 17.5_MeV * momentumInv *
t *
472 (1.0f + 0.125f * std::log10(10.0
f * xOverX0));
477 int pdg,
float m,
float qOverP,
487 const auto xOverX0 = slab.thicknessInX0();
489 const auto momentumInv =
std::abs(qOverP / q);
491 const auto q2OverBeta2 = RelativisticQuantities(m, qOverP, q).q2OverBeta2;
494 return theta0RossiGreisen(xOverX0, momentumInv, q2OverBeta2);
496 return theta0Highland(xOverX0, momentumInv, q2OverBeta2);