Skip to content

Commit b4fd39a

Browse files
authored
Merge pull request #827 from beomki-yeo/positron-brems
Update electron/positron bremss
2 parents 7275508 + 4504708 commit b4fd39a

File tree

5 files changed

+222
-20
lines changed

5 files changed

+222
-20
lines changed

core/include/detray/materials/interaction.hpp

+16-4
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ struct interaction {
2323
using relativistic_quantities =
2424
detail::relativistic_quantities<scalar_type>;
2525

26-
// @returns the total stopping power
26+
// @returns the total stopping power (<-dE/dx>)
2727
DETRAY_HOST_DEVICE scalar_type
2828
compute_stopping_power(const detray::material<scalar_type>& mat,
2929
const pdg_particle<scalar_type>& ptc,
@@ -68,9 +68,20 @@ struct interaction {
6868

6969
scalar_type stopping_power{0.f};
7070

71-
// Only consider electrons at the moment
71+
// Only consider electrons and positrons at the moment
7272
// For middle-heavy particles muons, the bremss is negligibe
73-
if (ptc.pdg_num() == electron<scalar_type>().pdg_num()) {
73+
//
74+
// Also, over 10 MeV, positron and electron bremss might be similar.
75+
// In ICRU 37, it is written that "In our tabulations, the radiative
76+
// stopping power for positrons has been assumed to be the same as that
77+
// for electrons, which is a good approximation at energies above, say,
78+
// 10 MeV. However, it should be mentioned that exploratory calculations
79+
// by Feng et at. (1981), employing the same method as that previously
80+
// used by them for electrons, indicate significant differences between
81+
// positrons and electrons in regard to the differential bremsstrahlung
82+
// cross sections in oxygen and uranium at 500, 50, and 10 keV"
83+
if (ptc.pdg_num() == electron<scalar_type>().pdg_num() ||
84+
ptc.pdg_num() == positron<scalar_type>().pdg_num()) {
7485
// Stopping power ~ E/X (B. B. Rossi, High-energy particles,1952)
7586
// This approximation gets poor in low energy below 10 MeV
7687
stopping_power = rq.m_gamma * ptc.mass() / mat.X0();
@@ -148,7 +159,8 @@ struct interaction {
148159

149160
scalar_type derivative{0.f};
150161

151-
if (ptc.pdg_num() == electron<scalar_type>().pdg_num()) {
162+
if (ptc.pdg_num() == electron<scalar_type>().pdg_num() ||
163+
ptc.pdg_num() == positron<scalar_type>().pdg_num()) {
152164
// Stopping power ~ E/X = gamma * m/X
153165
// Derivative = dgamma/dqop * m/X = -beta^2 gamma/qop * m/X
154166
// (Eq (D.5) of arXiv:2403.16720)

tests/unit_tests/cpu/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ macro(detray_add_cpu_test algebra)
6363
"material/material_maps.cpp"
6464
"material/materials.cpp"
6565
"material/stopping_power_derivative.cpp"
66+
"material/stopping_power.cpp"
6667
"navigation/intersection/cuboid_intersector.cpp"
6768
"navigation/intersection/cylinder_intersector.cpp"
6869
"navigation/intersection/helix_intersector.cpp"

tests/unit_tests/cpu/material/bethe_equation.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ INSTANTIATE_TEST_SUITE_P(
9595
0.1003f * unit<scalar>::GeV, 3.082f)));
9696

9797
/*
98-
//@fixme: Test fails with He Gas and 10 GeV muons (18 % difference)
98+
//@fixme: Test fails with He Gas and 1 GeV muons (18 % difference)
9999
INSTANTIATE_TEST_SUITE_P(
100100
detray_material_Bethe_1GeV_HeGas, EnergyLossBetheValidation,
101101
::testing::Values(std::make_tuple(helium_gas<scalar>(), muon<scalar>(),

tests/unit_tests/cpu/material/bremsstrahlung.cpp

+45-15
Original file line numberDiff line numberDiff line change
@@ -60,37 +60,67 @@ TEST_P(EnergyLossBremsValidation, bremsstrahlung) {
6060

6161
// We have not implemented the bremsstrahlung for the heavier charged
6262
// particles, which is negligible
63-
if (ptc.pdg_num() == electron<scalar>().pdg_num()) {
64-
// Check if difference is within 11% error
65-
EXPECT_NEAR((expected_dEdx - dEdx) / dEdx, 0.f, 0.11f);
63+
if ((ptc.pdg_num() == electron<scalar>().pdg_num()) ||
64+
(ptc.pdg_num() == positron<scalar>().pdg_num())) {
65+
// Check if difference is within 14% error
66+
EXPECT_NEAR((expected_dEdx - dEdx) / dEdx, 0.f, 0.14f);
6667
} else {
6768
EXPECT_FLOAT_EQ(static_cast<float>(dEdx), 0.f);
6869
}
6970
}
7071

71-
// REFERENCE
72-
//
73-
// Atomic Data and Nuclear Data Tables Volume 4, March 1972, Pages 1-27,
74-
//"Energy loss, range, and bremsstrahlung yield for 10-keV to 100-MeV electrons
75-
// in various elements and chemical compounds"
72+
// From https://physics.nist.gov/PhysRefData/Star/Text/ESTAR.html
73+
// Assumes that the stopping powers of electron and positron are the same
7674

75+
// Electrons
7776
INSTANTIATE_TEST_SUITE_P(
78-
detray_material_Bremsstrahlung_100MeV_He, EnergyLossBremsValidation,
77+
electron_Bremsstrahlung_100MeV_He, EnergyLossBremsValidation,
7978
::testing::Values(std::make_tuple(helium_gas<scalar>(), electron<scalar>(),
80-
100.0f * unit<scalar>::MeV, 0.95886f)));
79+
100.0f * unit<scalar>::MeV, 0.9229f)));
8180

8281
INSTANTIATE_TEST_SUITE_P(
83-
detray_material_Bremsstrahlung_100MeV_Al, EnergyLossBremsValidation,
82+
electron_Bremsstrahlung_100MeV_Al, EnergyLossBremsValidation,
8483
::testing::Values(std::make_tuple(aluminium<scalar>(), electron<scalar>(),
85-
100.0f * unit<scalar>::MeV, 3.8172f)));
84+
100.0f * unit<scalar>::MeV, 3.714f)));
8685

8786
INSTANTIATE_TEST_SUITE_P(
88-
detray_material_Bremsstrahlung_100MeV_Cu, EnergyLossBremsValidation,
87+
electron_Bremsstrahlung_100MeV_Si, EnergyLossBremsValidation,
88+
::testing::Values(std::make_tuple(silicon<scalar>(), electron<scalar>(),
89+
100.0f * unit<scalar>::MeV, 4.099f)));
90+
91+
INSTANTIATE_TEST_SUITE_P(
92+
electron_Bremsstrahlung_100MeV_Cu, EnergyLossBremsValidation,
8993
::testing::Values(std::make_tuple(copper<scalar>(), electron<scalar>(),
90-
100.0f * unit<scalar>::MeV, 7.2365f)));
94+
100.0f * unit<scalar>::MeV, 7.079f)));
95+
96+
// Positrons
97+
INSTANTIATE_TEST_SUITE_P(
98+
positron_Bremsstrahlung_100MeV_He, EnergyLossBremsValidation,
99+
::testing::Values(std::make_tuple(helium_gas<scalar>(), positron<scalar>(),
100+
100.0f * unit<scalar>::MeV, 0.9229f)));
101+
102+
INSTANTIATE_TEST_SUITE_P(
103+
positron_Bremsstrahlung_100MeV_Al, EnergyLossBremsValidation,
104+
::testing::Values(std::make_tuple(aluminium<scalar>(), positron<scalar>(),
105+
100.0f * unit<scalar>::MeV, 3.714f)));
106+
107+
INSTANTIATE_TEST_SUITE_P(
108+
positron_Bremsstrahlung_100MeV_Si, EnergyLossBremsValidation,
109+
::testing::Values(std::make_tuple(silicon<scalar>(), positron<scalar>(),
110+
100.0f * unit<scalar>::MeV, 4.099f)));
111+
112+
INSTANTIATE_TEST_SUITE_P(
113+
positron_Bremsstrahlung_100MeV_Cu, EnergyLossBremsValidation,
114+
::testing::Values(std::make_tuple(copper<scalar>(), positron<scalar>(),
115+
100.0f * unit<scalar>::MeV, 7.079f)));
91116

92117
// We have not implemented the bremsstrahlung for muons
93118
INSTANTIATE_TEST_SUITE_P(
94-
detray_material_Bremsstrahlung_100MeV_Cu_muon, EnergyLossBremsValidation,
119+
muon_Bremsstrahlung_100MeV_Cu_muon, EnergyLossBremsValidation,
95120
::testing::Values(std::make_tuple(copper<scalar>(), muon<scalar>(),
96121
100.0f * unit<scalar>::MeV, 0.f)));
122+
123+
INSTANTIATE_TEST_SUITE_P(
124+
antimuon_Bremsstrahlung_100MeV_Cu_muon, EnergyLossBremsValidation,
125+
::testing::Values(std::make_tuple(copper<scalar>(), antimuon<scalar>(),
126+
100.0f * unit<scalar>::MeV, 0.f)));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
/** Detray library, part of the ACTS project (R&D line)
2+
*
3+
* (c) 2024 CERN for the benefit of the ACTS project
4+
*
5+
* Mozilla Public License Version 2.0
6+
*/
7+
8+
// Project include(s).
9+
#include "detray/materials/interaction.hpp"
10+
#include "detray/materials/material.hpp"
11+
#include "detray/materials/predefined_materials.hpp"
12+
13+
// Detray test include(s)
14+
#include "detray/test/utils/types.hpp"
15+
16+
// GTest include(s).
17+
#include <gtest/gtest.h>
18+
19+
using namespace detray;
20+
21+
// Test class for the stopping power
22+
// Input tuple: < material, particle type, kinetic energy, expected output >
23+
class StoppingPowerValidation
24+
: public ::testing::TestWithParam<
25+
std::tuple<material<test::scalar>, pdg_particle<test::scalar>,
26+
test::scalar, test::scalar>> {};
27+
28+
TEST_P(StoppingPowerValidation, stopping_power) {
29+
30+
// Interaction object
31+
interaction<test::scalar> I;
32+
33+
// Material
34+
material<test::scalar> mat = std::get<0>(GetParam());
35+
36+
// Particle
37+
pdg_particle<test::scalar> ptc = std::get<1>(GetParam());
38+
39+
// Kinetic energy
40+
const test::scalar T = std::get<2>(GetParam());
41+
42+
// Total energy
43+
const test::scalar E = T + ptc.mass();
44+
45+
// Momentum
46+
const test::scalar p = math::sqrt(E * E - ptc.mass() * ptc.mass());
47+
48+
// qoverp
49+
const test::scalar qop{ptc.charge() / p};
50+
51+
// Stopping power in MeV * cm^2 / g
52+
const test::scalar dEdx{I.compute_stopping_power(mat, ptc, {ptc, qop}) /
53+
mat.mass_density() /
54+
(unit<test::scalar>::MeV * unit<test::scalar>::cm2 /
55+
unit<test::scalar>::g)};
56+
57+
const test::scalar expected_dEdx = std::get<3>(GetParam());
58+
59+
// Check if difference is within 8% error
60+
EXPECT_NEAR((expected_dEdx - dEdx) / dEdx, 0.f, 0.08f);
61+
}
62+
63+
/******************
64+
* Muon tests
65+
******************/
66+
67+
// From https://pdg.lbl.gov/2024/AtomicNuclearProperties/index.html
68+
// Note 1: that we took the PDG value only from Ionization loss (Radiative loss
69+
// is ignored) Note 2: assumes that the stopping powers of muon and antimuon are
70+
// the same Note 3: Test fails with He Gas and 1 GeV muons (18 % difference)
71+
INSTANTIATE_TEST_SUITE_P(
72+
muon_stopping_power_He, StoppingPowerValidation,
73+
::testing::Values(
74+
std::make_tuple(helium_gas<test::scalar>(), muon<test::scalar>(),
75+
100.0f * unit<test::scalar>::MeV, 2.165f),
76+
// std::make_tuple(helium_gas<test::scalar>(), muon<test::scalar>(),
77+
// 1.f * unit<test::scalar>::GeV, 2.133f),
78+
std::make_tuple(helium_gas<test::scalar>(), muon<test::scalar>(),
79+
10.0f * unit<test::scalar>::GeV, 2.768f),
80+
std::make_tuple(helium_gas<test::scalar>(), muon<test::scalar>(),
81+
100.0f * unit<test::scalar>::GeV, 3.188f)));
82+
83+
INSTANTIATE_TEST_SUITE_P(
84+
muon_stopping_power_Si, StoppingPowerValidation,
85+
::testing::Values(
86+
std::make_tuple(silicon<test::scalar>(), muon<test::scalar>(),
87+
100.0f * unit<test::scalar>::MeV, 1.849f),
88+
std::make_tuple(silicon<test::scalar>(), muon<test::scalar>(),
89+
1.f * unit<test::scalar>::GeV, 1.803f),
90+
std::make_tuple(silicon<test::scalar>(), muon<test::scalar>(),
91+
10.0f * unit<test::scalar>::GeV, 2.177f),
92+
std::make_tuple(silicon<test::scalar>(), muon<test::scalar>(),
93+
100.0f * unit<test::scalar>::GeV, 2.451f)));
94+
95+
INSTANTIATE_TEST_SUITE_P(
96+
anti_muon_stopping_power_He, StoppingPowerValidation,
97+
::testing::Values(
98+
std::make_tuple(helium_gas<test::scalar>(), antimuon<test::scalar>(),
99+
100.0f * unit<test::scalar>::MeV, 2.165f),
100+
// std::make_tuple(helium_gas<test::scalar>(), antimuon<test::scalar>(),
101+
// 1.f * unit<test::scalar>::GeV, 2.133f),
102+
std::make_tuple(helium_gas<test::scalar>(), antimuon<test::scalar>(),
103+
10.0f * unit<test::scalar>::GeV, 2.768f),
104+
std::make_tuple(helium_gas<test::scalar>(), antimuon<test::scalar>(),
105+
100.0f * unit<test::scalar>::GeV, 3.188f)));
106+
107+
INSTANTIATE_TEST_SUITE_P(
108+
anti_muon_stopping_power_Si, StoppingPowerValidation,
109+
::testing::Values(
110+
std::make_tuple(silicon<test::scalar>(), antimuon<test::scalar>(),
111+
100.0f * unit<test::scalar>::MeV, 1.849f),
112+
std::make_tuple(silicon<test::scalar>(), antimuon<test::scalar>(),
113+
1.f * unit<test::scalar>::GeV, 1.803f),
114+
std::make_tuple(silicon<test::scalar>(), antimuon<test::scalar>(),
115+
10.0f * unit<test::scalar>::GeV, 2.177f),
116+
std::make_tuple(silicon<test::scalar>(), antimuon<test::scalar>(),
117+
100.0f * unit<test::scalar>::GeV, 2.451f)));
118+
119+
/*********************
120+
* Electron tests
121+
*********************/
122+
123+
// From https://physics.nist.gov/PhysRefData/Star/Text/ESTAR.html
124+
// Assumes that the stopping powers of electron and positron are the same
125+
INSTANTIATE_TEST_SUITE_P(
126+
electron_stopping_power_He, StoppingPowerValidation,
127+
::testing::Values(std::make_tuple(helium_gas<test::scalar>(),
128+
electron<test::scalar>(),
129+
100.0f * unit<test::scalar>::MeV, 3.532f),
130+
std::make_tuple(helium_gas<test::scalar>(),
131+
electron<test::scalar>(),
132+
1.f * unit<test::scalar>::GeV, 13.14f)));
133+
134+
INSTANTIATE_TEST_SUITE_P(
135+
electron_stopping_power_Si, StoppingPowerValidation,
136+
::testing::Values(std::make_tuple(silicon<test::scalar>(),
137+
electron<test::scalar>(),
138+
100.0f * unit<test::scalar>::MeV, 6.017f),
139+
std::make_tuple(silicon<test::scalar>(),
140+
electron<test::scalar>(),
141+
1.f * unit<test::scalar>::GeV, 46.69f)));
142+
143+
INSTANTIATE_TEST_SUITE_P(
144+
positron_stopping_power_He, StoppingPowerValidation,
145+
::testing::Values(std::make_tuple(helium_gas<test::scalar>(),
146+
positron<test::scalar>(),
147+
100.0f * unit<test::scalar>::MeV, 3.532f),
148+
std::make_tuple(helium_gas<test::scalar>(),
149+
positron<test::scalar>(),
150+
1.f * unit<test::scalar>::GeV, 13.14f)));
151+
152+
INSTANTIATE_TEST_SUITE_P(
153+
positron_stopping_power_Si, StoppingPowerValidation,
154+
::testing::Values(std::make_tuple(silicon<test::scalar>(),
155+
positron<test::scalar>(),
156+
100.0f * unit<test::scalar>::MeV, 6.017f),
157+
std::make_tuple(silicon<test::scalar>(),
158+
positron<test::scalar>(),
159+
1.f * unit<test::scalar>::GeV, 46.69f)));

0 commit comments

Comments
 (0)