17 using namespace Acts::UnitLiterals;
23 constexpr
float Me = 0.5109989461_MeV;
25 constexpr
float K = 0.307075_MeV * 1_cm * 1_cm;
27 constexpr
float PlasmaEnergyScale = 28.816_eV;
30 struct RelativisticQuantities {
31 float q2OverBeta2 = 0.0f;
33 float betaGamma = 0.0f;
36 RelativisticQuantities(
float mass,
float qOverP,
float absQ) {
37 assert((0 < mass) and
"Mass must be positive");
38 assert((qOverP != 0) and
"q/p must be non-zero");
39 assert((absQ > 0) and
"Absolute charge must be non-zero and positive");
42 q2OverBeta2 = absQ * absQ + (mass * qOverP) * (mass * qOverP);
43 assert((q2OverBeta2 >= 0) &&
"Negative q2OverBeta2");
45 const float mOverP = mass * std::abs(qOverP / absQ);
46 const float pOverM = 1.0f / mOverP;
48 beta2 = 1.0f / (1.0f + mOverP * mOverP);
49 assert((beta2 >= 0) &&
"Negative beta2");
52 assert((betaGamma >= 0) &&
"Negative betaGamma");
54 gamma = std::sqrt(1.0
f + pOverM * pOverM);
59 inline float deriveBeta2(
float qOverP,
const RelativisticQuantities& rq) {
60 return -2 / (qOverP * rq.gamma * rq.gamma);
64 inline float computeMassTerm(
float mass,
const RelativisticQuantities& rq) {
65 return 2 * mass * rq.betaGamma * rq.betaGamma;
69 inline float logDeriveMassTerm(
float qOverP) {
77 inline float computeWMax(
float mass,
const RelativisticQuantities& rq) {
78 const float mfrac = Me /
mass;
79 const float nominator = 2 * Me * rq.betaGamma * rq.betaGamma;
80 const float denonimator = 1.0f + 2 * rq.gamma * mfrac + mfrac * mfrac;
81 return nominator / denonimator;
85 inline float logDeriveWMax(
float mass,
float qOverP,
86 const RelativisticQuantities& rq) {
90 const float a = std::abs(qOverP / std::sqrt(rq.q2OverBeta2));
92 const float b = Me * (1.0f + (mass / Me) * (mass / Me));
93 return -2 * (a * b - 2 + rq.beta2) / (qOverP * (a * b + 2));
104 inline float computeEpsilon(
float molarElectronDensity,
float thickness,
105 const RelativisticQuantities& rq) {
106 return 0.5f * K * molarElectronDensity * thickness * rq.q2OverBeta2;
110 inline float logDeriveEpsilon(
float qOverP,
const RelativisticQuantities& rq) {
112 return 2 / (qOverP * rq.gamma * rq.gamma);
116 inline float computeDeltaHalf(
float meanExitationPotential,
117 float molarElectronDensity,
118 const RelativisticQuantities& rq) {
121 if (rq.betaGamma < 10.0f) {
125 const float plasmaEnergy =
126 PlasmaEnergyScale * std::sqrt(1000.
f * molarElectronDensity);
127 return std::log(rq.betaGamma) +
128 std::log(plasmaEnergy / meanExitationPotential) - 0.5f;
132 inline float deriveDeltaHalf(
float qOverP,
const RelativisticQuantities& rq) {
137 return (rq.betaGamma < 10.0f) ? 0.0f : (-1.0f / qOverP);
147 inline float convertLandauFwhmToGaussianSigma(
float fwhm) {
149 return fwhm * 0.42466090014400953f;
155 const RelativisticQuantities& rq) {
161 const float Ne = slab.material().molarElectronDensity();
162 const float thickness = slab.thickness();
164 return 4 * computeEpsilon(Ne, thickness, rq);
172 float qOverP,
float absQ) {
178 const RelativisticQuantities rq{
m, qOverP, absQ};
179 const float I = slab.material().meanExcitationEnergy();
180 const float Ne = slab.material().molarElectronDensity();
181 const float thickness = slab.thickness();
182 const float eps = computeEpsilon(Ne, thickness, rq);
183 const float dhalf = computeDeltaHalf(I, Ne, rq);
184 const float u = computeMassTerm(Me, rq);
185 const float wmax = computeWMax(m, rq);
191 const float running =
192 std::log(u / I) + std::log(wmax / I) - 2.0f * rq.beta2 - 2.0f * dhalf;
193 return eps * running;
197 float qOverP,
float absQ) {
203 const RelativisticQuantities rq{
m, qOverP, absQ};
204 const float I = slab.material().meanExcitationEnergy();
205 const float Ne = slab.material().molarElectronDensity();
206 const float thickness = slab.thickness();
207 const float eps = computeEpsilon(Ne, thickness, rq);
208 const float dhalf = computeDeltaHalf(I, Ne, rq);
209 const float u = computeMassTerm(Me, rq);
210 const float wmax = computeWMax(m, rq);
222 const float logDerEps = logDeriveEpsilon(qOverP, rq);
223 const float derDHalf = deriveDeltaHalf(qOverP, rq);
224 const float logDerU = logDeriveMassTerm(qOverP);
225 const float logDerWmax = logDeriveWMax(m, qOverP, rq);
226 const float derBeta2 = deriveBeta2(qOverP, rq);
227 const float rel = logDerEps * (std::log(u / I) + std::log(wmax / I) -
228 2.0f * rq.beta2 - 2.0f * dhalf) +
229 logDerU + logDerWmax - 2.0
f * derBeta2 - 2.0
f * derDHalf;
234 float qOverP,
float absQ) {
240 const RelativisticQuantities rq{
m, qOverP, absQ};
241 const float I = slab.material().meanExcitationEnergy();
242 const float Ne = slab.material().molarElectronDensity();
243 const float thickness = slab.thickness();
244 const float eps = computeEpsilon(Ne, thickness, rq);
245 const float dhalf = computeDeltaHalf(I, Ne, rq);
246 const float u = computeMassTerm(Me, rq);
248 const float running =
249 std::log(u / I) + std::log(eps / I) + 0.2f - rq.beta2 - 2 * dhalf;
250 return eps * running;
254 float qOverP,
float absQ) {
260 const RelativisticQuantities rq{
m, qOverP, absQ};
261 const float I = slab.material().meanExcitationEnergy();
262 const float Ne = slab.material().molarElectronDensity();
263 const float thickness = slab.thickness();
264 const float eps = computeEpsilon(Ne, thickness, rq);
265 const float dhalf = computeDeltaHalf(I, Ne, rq);
266 const float t = computeMassTerm(Me, rq);
278 const float logDerEps = logDeriveEpsilon(qOverP, rq);
279 const float derDHalf = deriveDeltaHalf(qOverP, rq);
280 const float logDerT = logDeriveMassTerm(qOverP);
281 const float derBeta2 = deriveBeta2(qOverP, rq);
282 const float rel = logDerEps * (std::log(t / I) + std::log(eps / I) - 0.2f -
283 rq.beta2 - 2 * dhalf) +
284 logDerT + logDerEps - derBeta2 - 2 * derDHalf;
289 float qOverP,
float absQ) {
295 const RelativisticQuantities rq{
m, qOverP, absQ};
296 const float Ne = slab.material().molarElectronDensity();
297 const float thickness = slab.thickness();
299 const float fwhm = 4 * computeEpsilon(Ne, thickness, rq);
300 return convertLandauFwhmToGaussianSigma(fwhm);
304 float qOverP,
float absQ) {
305 const RelativisticQuantities rq{
m, qOverP, absQ};
310 float m,
float qOverP,
312 const RelativisticQuantities rq{
m, qOverP, absQ};
314 const float sigmaE = convertLandauFwhmToGaussianSigma(fwhm);
322 const float pInv = qOverP / absQ;
323 const float qOverBeta = std::sqrt(rq.q2OverBeta2);
324 return qOverBeta * pInv * pInv * sigmaE;
330 inline float computeBremsstrahlungLossMean(
float mass,
float energy) {
331 return energy * (Me /
mass) * (Me / mass);
335 inline float deriveBremsstrahlungLossMeanE(
float mass) {
336 return (Me / mass) * (Me /
mass);
347 constexpr
float MuonHighLowThreshold = 1_TeV;
349 constexpr
double MuonLow0 = -0.5345_MeV;
351 constexpr
double MuonLow1 = 6.803e-5;
353 constexpr
double MuonLow2 = 2.278e-11 / 1_MeV;
355 constexpr
double MuonLow3 = -9.899e-18 / (1_MeV * 1_MeV);
357 constexpr
double MuonHigh0 = -2.986_MeV;
359 constexpr
double MuonHigh1 = 9.253e-5;
362 inline float computeMuonDirectPairPhotoNuclearLossMean(
double energy) {
363 if (energy < MuonHighLowThreshold) {
365 (MuonLow1 + (MuonLow2 + MuonLow3 *
energy) * energy) *
energy;
367 return MuonHigh0 + MuonHigh1 *
energy;
372 inline float deriveMuonDirectPairPhotoNuclearLossMeanE(
double energy) {
373 if (energy < MuonHighLowThreshold) {
374 return MuonLow1 + (2 * MuonLow2 + 3 * MuonLow3 *
energy) * energy;
384 float qOverP,
float absQ) {
386 "pdg is not absolute");
394 const float x = slab.thicknessInX0();
397 const float momentum = absQ / qOverP;
398 const float energy = std::hypot(m, momentum);
400 float dEdx = computeBremsstrahlungLossMean(m, energy);
405 dEdx += computeMuonDirectPairPhotoNuclearLossMean(energy);
413 float qOverP,
float absQ) {
415 "pdg is not absolute");
423 const float x = slab.thicknessInX0();
426 const float momentum = absQ / qOverP;
427 const float energy = std::hypot(m, momentum);
430 float derE = deriveBremsstrahlungLossMeanE(m);
435 derE += deriveMuonDirectPairPhotoNuclearLossMeanE(energy);
443 const float derQOverP = -(absQ * absQ) / (qOverP * qOverP * qOverP * energy);
444 return derE * derQOverP *
x;
448 float m,
float qOverP,
float absQ) {
455 float qOverP,
float absQ) {
461 float m,
float qOverP,
float absQ) {
470 float qOverP,
float absQ) {
480 inline float theta0Highland(
float xOverX0,
float momentumInv,
483 const float t = std::sqrt(xOverX0 * q2OverBeta2);
486 return 13.6_MeV * momentumInv * t * (1.0f + 0.038f * 2 * std::log(t));
490 inline float theta0RossiGreisen(
float xOverX0,
float momentumInv,
493 const float t = std::sqrt(xOverX0 * q2OverBeta2);
494 return 17.5_MeV * momentumInv * t *
495 (1.0f + 0.125f * std::log10(10.0
f * xOverX0));
502 float qOverP,
float absQ) {
504 "pdg is not absolute");
512 const float xOverX0 = slab.thicknessInX0();
514 const float momentumInv = std::abs(qOverP / absQ);
516 const float q2OverBeta2 = RelativisticQuantities(m, qOverP, absQ).q2OverBeta2;
520 return theta0RossiGreisen(xOverX0, momentumInv, q2OverBeta2);
522 return theta0Highland(xOverX0, momentumInv, q2OverBeta2);