16 using namespace Acts::UnitLiterals;
22 constexpr
float Me = 0.5109989461_MeV;
24 constexpr
float K = 0.307075_MeV * 1_cm * 1_cm;
26 constexpr
float PlasmaEnergyScale = 28.816_eV;
29 struct RelativisticQuantities {
30 float q2OverBeta2 = 0.0f;
32 float betaGamma = 0.0f;
35 RelativisticQuantities(
float mass,
float qOverP,
float q) {
38 q2OverBeta2 = q * q + (mass * qOverP) * (mass * qOverP);
40 const auto mOverP = mass *
std::abs(qOverP / q);
41 const auto pOverM = 1.0f / mOverP;
43 beta2 = 1.0f / (1.0f + mOverP * mOverP);
47 gamma = std::sqrt(1.0f + pOverM * pOverM);
52 inline float deriveBeta2(
float qOverP,
const RelativisticQuantities& rq) {
53 return -2 / (qOverP * rq.gamma * rq.gamma);
57 inline float computeMassTerm(
float mass,
const RelativisticQuantities& rq) {
58 return 2 * mass * rq.betaGamma * rq.betaGamma;
62 inline float logDeriveMassTerm(
float qOverP) {
70 inline float computeWMax(
float mass,
const RelativisticQuantities& rq) {
71 const auto mfrac = Me /
mass;
72 const auto nominator = 2 * Me * rq.betaGamma * rq.betaGamma;
73 const auto denonimator = 1.0f + 2 * rq.gamma * mfrac + mfrac * mfrac;
74 return nominator / denonimator;
78 inline float logDeriveWMax(
float mass,
float qOverP,
79 const RelativisticQuantities& rq) {
83 const auto a =
std::abs(qOverP / std::sqrt(rq.q2OverBeta2));
85 const auto b = Me * (1.0f + (mass / Me) * (mass / Me));
86 return -2 * (a * b - 2 + rq.beta2) / (qOverP * (a * b + 2));
97 inline float computeEpsilon(
float molarElectronDensity,
float thickness,
98 const RelativisticQuantities& rq) {
99 return 0.5f * K * molarElectronDensity * thickness * rq.q2OverBeta2;
103 inline float logDeriveEpsilon(
float qOverP,
const RelativisticQuantities& rq) {
105 return 2 / (qOverP * rq.gamma * rq.gamma);
113 inline float computeDeltaHalf(
float meanExitationPotential,
114 float molarElectronDensity,
115 const RelativisticQuantities& rq) {
117 if (rq.betaGamma < 10.0f) {
121 const auto plasmaEnergy = PlasmaEnergyScale * std::sqrt(molarElectronDensity);
122 return std::log(rq.betaGamma) +
123 std::log(plasmaEnergy / meanExitationPotential) - 0.5f;
127 inline float deriveDeltaHalf(
float qOverP,
const RelativisticQuantities& rq) {
132 return (rq.betaGamma < 10.0f) ? 0.0f : (-1.0f / qOverP);
137 #define ASSERT_INPUTS(mass, qOverP, q) \
138 assert((0 < mass) and "Mass must be positive"); \
139 assert((qOverP != 0) and "q/p must be non-zero"); \
140 assert((q != 0) and "Charge must be non-zero");
143 float m,
float qOverP,
float q) {
151 const auto I = slab.material().meanExcitationEnergy();
152 const auto Ne = slab.material().molarElectronDensity();
154 const auto rq = RelativisticQuantities(m, qOverP, q);
156 const auto dhalf = computeDeltaHalf(
I, Ne, rq);
157 const auto u = computeMassTerm(Me, rq);
158 const auto wmax = computeWMax(m, rq);
165 0.5f * std::log(
u /
I) + 0.5f * std::log(wmax /
I) - rq.beta2 - dhalf;
166 return eps * running;
170 int ,
float m,
float qOverP,
179 const auto I = slab.material().meanExcitationEnergy();
180 const auto Ne = slab.material().molarElectronDensity();
182 const auto rq = RelativisticQuantities(m, qOverP, q);
184 const auto dhalf = computeDeltaHalf(
I, Ne, rq);
185 const auto u = computeMassTerm(Me, rq);
186 const auto wmax = computeWMax(m, rq);
198 const auto logDerEps = logDeriveEpsilon(qOverP, rq);
199 const auto derDHalf = deriveDeltaHalf(qOverP, rq);
200 const auto logDerU = logDeriveMassTerm(qOverP);
201 const auto logDerWmax = logDeriveWMax(m, qOverP, rq);
202 const auto derBeta2 = deriveBeta2(qOverP, rq);
203 const auto rel = logDerEps * (0.5f * std::log(
u /
I) +
204 0.5f * std::log(wmax /
I) - rq.beta2 - dhalf) +
205 0.5f * logDerU + 0.5f * logDerWmax - derBeta2 - derDHalf;
210 float m,
float qOverP,
float q) {
218 const auto I = slab.material().meanExcitationEnergy();
219 const auto Ne = slab.material().molarElectronDensity();
221 const auto rq = RelativisticQuantities(m, qOverP, q);
223 const auto dhalf = computeDeltaHalf(
I, Ne, rq);
224 const auto t = computeMassTerm(m, rq);
227 std::log(
t /
I) + std::log(
eps /
I) + 0.2f - rq.beta2 - 2 * dhalf;
228 return eps * running;
233 float qOverP,
float q) {
241 const auto I = slab.material().meanExcitationEnergy();
242 const auto Ne = slab.material().molarElectronDensity();
244 const auto rq = RelativisticQuantities(m, qOverP, q);
246 const auto dhalf = computeDeltaHalf(
I, Ne, rq);
247 const auto t = computeMassTerm(m, rq);
259 const auto logDerEps = logDeriveEpsilon(qOverP, rq);
260 const auto derDHalf = deriveDeltaHalf(qOverP, rq);
261 const auto logDerT = logDeriveMassTerm(qOverP);
262 const auto derBeta2 = deriveBeta2(qOverP, rq);
263 const auto rel = logDerEps * (std::log(
t /
I) + std::log(
eps /
I) - 0.2f -
264 rq.beta2 - 2 * dhalf) +
265 logDerT + logDerEps - derBeta2 - 2 * derDHalf;
278 inline float convertLandauFwhmToGaussianSigma(
float fwhm) {
279 return fwhm / (2 * std::sqrt(2 * std::log(2.0f)));
286 float qOverP,
float q) {
294 const auto Ne = slab.material().molarElectronDensity();
296 const auto rq = RelativisticQuantities(m, qOverP, q);
298 const auto fwhm = 4 * computeEpsilon(Ne,
thickness, rq);
299 return convertLandauFwhmToGaussianSigma(fwhm);
304 float qOverP,
float q) {
312 const auto Ne = slab.material().molarElectronDensity();
314 const auto rq = RelativisticQuantities(m, qOverP, q);
316 const auto fwhm = 4 * computeEpsilon(Ne,
thickness, rq);
317 const auto sigmaE = convertLandauFwhmToGaussianSigma(fwhm);
325 const auto pInv = qOverP / q;
326 return std::sqrt(rq.q2OverBeta2) * pInv * pInv * sigmaE;
332 inline float computeBremsstrahlungLossMean(
float mass,
float energy) {
333 return energy * (Me /
mass) * (Me / mass);
337 inline float deriveBremsstrahlungLossMeanE(
float mass) {
338 return (Me / mass) * (Me /
mass);
349 constexpr
float MuonHighLowThreshold = 1_TeV;
351 constexpr
double MuonLow0 = -0.5345_MeV;
353 constexpr
double MuonLow1 = 6.803e-5;
355 constexpr
double MuonLow2 = 2.278e-11 / 1_MeV;
357 constexpr
double MuonLow3 = -9.899e-18 / (1_MeV * 1_MeV);
359 constexpr
double MuonHigh0 = -2.986_MeV;
361 constexpr
double MuonHigh1 = 9.253e-5;
364 inline float computeMuonDirectPairPhotoNuclearLossMean(
double energy) {
365 if (energy < MuonHighLowThreshold) {
367 (MuonLow1 + (MuonLow2 + MuonLow3 * energy) * energy) * energy;
369 return MuonHigh0 + MuonHigh1 * energy;
374 inline float deriveMuonDirectPairPhotoNuclearLossMeanE(
double energy) {
375 if (energy < MuonHighLowThreshold) {
376 return MuonLow1 + (2 * MuonLow2 + 3 * MuonLow3 * energy) * energy;
385 float m,
float qOverP,
float q) {
394 const auto x = slab.thicknessInX0();
400 auto dEdx = computeBremsstrahlungLossMean(m, energy);
403 dEdx += computeMuonDirectPairPhotoNuclearLossMean(energy);
410 float m,
float qOverP,
float q) {
419 const auto x = slab.thicknessInX0();
426 auto derE = deriveBremsstrahlungLossMeanE(m);
429 derE += deriveMuonDirectPairPhotoNuclearLossMeanE(energy);
437 const auto derQOverP = -(q * q) / (qOverP * qOverP * qOverP * energy);
438 return derE * derQOverP *
x;
442 float qOverP,
float q) {
448 float m,
float qOverP,
float q) {
454 float qOverP,
float q) {
462 float m,
float qOverP,
float q) {
472 inline float theta0Highland(
float xOverX0,
float momentumInv,
475 const auto t = std::sqrt(xOverX0 * q2OverBeta2);
478 return 13.6_MeV * momentumInv *
t * (1.0f + 0.038f * 2 * std::log(
t));
482 inline float theta0RossiGreisen(
float xOverX0,
float momentumInv,
485 const auto t = std::sqrt(xOverX0 * q2OverBeta2);
486 return 17.5_MeV * momentumInv *
t *
487 (1.0f + 0.125f * std::log10(10.0f * xOverX0));
493 float m,
float qOverP,
float q) {
502 const auto xOverX0 = slab.thicknessInX0();
504 const auto momentumInv =
std::abs(qOverP / q);
506 const auto q2OverBeta2 = RelativisticQuantities(m, qOverP, q).q2OverBeta2;
509 return theta0RossiGreisen(xOverX0, momentumInv, q2OverBeta2);
511 return theta0Highland(xOverX0, momentumInv, q2OverBeta2);