OPALX (Object Oriented Parallel Accelerator Library for Exascal) master (dc2a29eed580)
OPALX
Loading...
Searching...
No Matches
TestLinearBreitWheeler.cpp
Go to the documentation of this file.
1
16#include "Physics/Physics.h"
17#include "Utilities/Options.h"
18#include "gtest/gtest.h"
19
20#include <cmath>
21
22namespace {
23 Vector_t<double, 3> makeDirection(double x, double y, double z) {
24 Vector_t<double, 3> value(0.0);
25 value(0) = x;
26 value(1) = y;
27 value(2) = z;
28 return value;
29 }
30
31 double proposalYReference(double invariantSGeV2, double z) {
32 const double cz = 2.0 * std::log(invariantSGeV2 / (4.0 * Physics::m_e * Physics::m_e));
33 const double expCZ = std::exp(cz);
34 const double expCZZ = std::exp(cz * z);
35 return (1.0 + (1.0 - expCZZ) / (expCZ - 1.0)) / (1.0 + expCZZ);
36 }
37
38 double proposalJacobianReference(double invariantSGeV2, double z, double y) {
39 const double cz = 2.0 * std::log(invariantSGeV2 / (4.0 * Physics::m_e * Physics::m_e));
40 const double expCZ = std::exp(cz);
41 const double expCZZ = std::exp(cz * z);
42 return -cz * expCZZ / ((expCZ - 1.0) * (1.0 + expCZZ)) - y * cz * expCZZ / (1.0 + expCZZ);
43 }
44
45 double unpolarizedAngularWeightReference(double invariantSGeV2, double z) {
46 const double threshold = 4.0 * Physics::m_e * Physics::m_e;
47 if (invariantSGeV2 <= threshold) {
48 return 0.0;
49 }
50
51 const double w = 0.5 * std::sqrt(invariantSGeV2);
52 const double beta = std::sqrt(std::max(0.0, 1.0 - threshold / invariantSGeV2));
53 const double root = std::sqrt(std::max(0.0, w * w - Physics::m_e * Physics::m_e));
54 const double x1 = Physics::m_e * Physics::m_e / (w * w + w * root);
55 const double y = proposalYReference(invariantSGeV2, z);
56 const double sm = 2.0 * Physics::m_e * Physics::m_e / ((1.0 + beta) * x1)
57 * (beta * (1.0 - 2.0 * y) - 1.0);
58 const double um = -2.0 * Physics::m_e * Physics::m_e / ((1.0 + beta) * x1)
59 * (beta * (1.0 - 2.0 * y) + 1.0);
60 const double costh1 = 1.0 + 2.0 * Physics::m_e * Physics::m_e * (1.0 / um + 1.0 / sm);
61 const double d = -(sm / um + um / sm);
62 const double f0 = d - (1.0 - costh1 * costh1);
63 const double jacobian = proposalJacobianReference(invariantSGeV2, z, y);
64 return std::max(0.0, f0 * beta * (1.0 + beta) * x1 * jacobian / 4.0);
65 }
66} // namespace
67
68TEST(TestLinearBreitWheeler, HeadOnThresholdMatchesAnalyticCondition) {
69 const double wavelength_m = 1.0e-9;
70 const double laserPhotonEnergyGeV =
72 const double expectedThresholdPhotonEnergyGeV =
73 Physics::m_e * Physics::m_e / laserPhotonEnergyGeV;
74
75 const auto n1 = makeDirection(0.0, 0.0, 1.0);
76 const auto n2 = makeDirection(0.0, 0.0, -1.0);
78 const double computedS = Physics::LinearBreitWheeler::invariantSGeV2(
79 expectedThresholdPhotonEnergyGeV, laserPhotonEnergyGeV, n1, n2);
80
81 EXPECT_NEAR(computedS, thresholdS, thresholdS * 1.0e-12);
83}
84
85TEST(TestLinearBreitWheeler, TotalCrossSectionVanishesBelowThreshold) {
86 const double belowThresholdS = 0.99 * Physics::LinearBreitWheeler::thresholdInvariantSGeV2();
87 EXPECT_DOUBLE_EQ(Physics::LinearBreitWheeler::totalCrossSection(belowThresholdS), 0.0);
88}
89
90TEST(TestLinearBreitWheeler, TotalCrossSectionIsPositiveAboveThreshold) {
91 const double aboveThresholdS = 16.0 * Physics::m_e * Physics::m_e;
92 const double sigma = Physics::LinearBreitWheeler::totalCrossSection(aboveThresholdS);
93 EXPECT_GT(sigma, 0.0);
94}
95
96TEST(TestLinearBreitWheeler, ProposalCoordinateMapsToExpectedScatteringCosine) {
97 const double sGeV2 = 10.0 * Physics::LinearBreitWheeler::thresholdInvariantSGeV2();
98 for (const double z : {-0.75, 0.0, 0.65}) {
99 const double expected = 1.0 - 2.0 * proposalYReference(sGeV2, z);
100 EXPECT_NEAR(
102 1.0e-14);
103 }
104}
105
106TEST(TestLinearBreitWheeler, UnpolarizedAngularWeightMatchesIndependentReference) {
107 const double sGeV2 = 10.0 * Physics::LinearBreitWheeler::thresholdInvariantSGeV2();
108 for (const double z : {-0.75, -0.10, 0.55}) {
109 const double expected = unpolarizedAngularWeightReference(sGeV2, z);
110 EXPECT_NEAR(
112 std::max(1.0e-15, std::abs(expected) * 1.0e-13));
113 }
114}
115
116TEST(TestLinearBreitWheeler, SampledEventConservesFourMomentumForHeadOnGeometry) {
117 const int previousSeed = Options::seed;
118 Options::seed = 20260404;
119
120 const double wavelength_m = 1.0e-9;
121 const double laserPhotonEnergyGeV =
123 const double highEnergyPhotonEnergyGeV = 0.5;
124 const auto highEnergyDirection = makeDirection(0.0, 0.0, 1.0);
125 const auto laserDirection = makeDirection(0.0, 0.0, -1.0);
126
128 highEnergyPhotonEnergyGeV, laserPhotonEnergyGeV, highEnergyDirection, laserDirection);
130 const auto event = Physics::LinearBreitWheeler::sampleEvent(kernel, engine);
131
132 Vector_t<double, 3> incomingMomentum(0.0);
133 incomingMomentum += highEnergyPhotonEnergyGeV * highEnergyDirection;
134 incomingMomentum += laserPhotonEnergyGeV * laserDirection;
135
136 const Vector_t<double, 3> outgoingMomentum =
137 event.electronMomentumLabGeV + event.positronMomentumLabGeV;
138 const double incomingEnergy = highEnergyPhotonEnergyGeV + laserPhotonEnergyGeV;
139 const double outgoingEnergy = event.electronEnergyLabGeV + event.positronEnergyLabGeV;
140
141 EXPECT_NEAR(outgoingEnergy, incomingEnergy, incomingEnergy * 1.0e-10);
142 EXPECT_NEAR(outgoingMomentum(0), incomingMomentum(0), 1.0e-10);
143 EXPECT_NEAR(outgoingMomentum(1), incomingMomentum(1), 1.0e-10);
144 EXPECT_NEAR(outgoingMomentum(2), incomingMomentum(2), 1.0e-10);
145 EXPECT_GE(event.scatteringCosineCM, -1.0);
146 EXPECT_LE(event.scatteringCosineCM, 1.0);
147 EXPECT_GE(event.azimuthCM, 0.0);
148 EXPECT_LT(event.azimuthCM, Physics::two_pi);
149
150 Options::seed = previousSeed;
151}
152
153TEST(TestLinearBreitWheeler, FixedSeedProducesDeterministicSampleSequence) {
154 const int previousSeed = Options::seed;
155 Options::seed = 424242;
156
157 const double wavelength_m = 1.0e-9;
158 const double laserPhotonEnergyGeV =
160 const double highEnergyPhotonEnergyGeV = 0.5;
161 const auto highEnergyDirection = makeDirection(0.0, 0.0, 1.0);
162 const auto laserDirection = makeDirection(0.0, 0.0, -1.0);
163
165 highEnergyPhotonEnergyGeV, laserPhotonEnergyGeV, highEnergyDirection, laserDirection);
168
169 for (int i = 0; i < 8; ++i) {
170 const auto event1 = Physics::LinearBreitWheeler::sampleEvent(kernel, engine1);
171 const auto event2 = Physics::LinearBreitWheeler::sampleEvent(kernel, engine2);
172 EXPECT_DOUBLE_EQ(event1.scatteringCosineCM, event2.scatteringCosineCM);
173 EXPECT_DOUBLE_EQ(event1.azimuthCM, event2.azimuthCM);
174 EXPECT_DOUBLE_EQ(event1.electronEnergyLabGeV, event2.electronEnergyLabGeV);
175 EXPECT_DOUBLE_EQ(event1.positronEnergyLabGeV, event2.positronEnergyLabGeV);
176 }
177
178 Options::seed = previousSeed;
179}
ippl::Vector< T, Dim > Vector_t
TEST(TestLinearBreitWheeler, HeadOnThresholdMatchesAnalyticCondition)
int seed
The current random seed.
Definition Options.cpp:37
double proposalZToScatteringCosineCM(double sGeV2, double proposalZ)
Map CAIN's proposal parameter to the CM scattering cosine.
double unpolarizedAngularWeight(double sGeV2, double proposalZ)
Evaluate the unpolarized CAIN-aligned angular kernel in proposal coordinates.
double thresholdInvariantSGeV2()
Threshold invariant for .
SampledEvent sampleEvent(const SamplingKernel &kernel, std::mt19937_64 &engine)
Sample one unpolarized linear Breit-Wheeler event.
SamplingKernel makeSamplingKernel(double highEnergyPhotonEnergyGeV, double laserPhotonEnergyGeV, const Vector_t< double, 3 > &highEnergyDirection, const Vector_t< double, 3 > &laserDirection)
Build a cached sampling kernel for repeated sampled events.
double invariantSGeV2(double photon1EnergyGeV, double photon2EnergyGeV, const Vector_t< double, 3 > &photon1Direction, const Vector_t< double, 3 > &photon2Direction)
Two-photon invariant for Breit-Wheeler pair production.
std::mt19937_64 makeHostRandomEngine(std::uint64_t streamIndex)
Build a deterministic host RNG from Options::seed.
double totalCrossSection(double sGeV2)
Total unpolarized linear Breit-Wheeler cross section.
double photonEnergyFromWavelengthGeV(double wavelength_m)
Convert a laser wavelength to a single-photon energy.
bool isAboveThreshold(double sGeV2)
Check whether pair creation is kinematically allowed.
constexpr double two_pi
The value of.
Definition Physics.h:40
constexpr double m_e
The electron rest mass in GeV.
Definition Physics.h:93