18#include "gtest/gtest.h"
31 double proposalYReference(
double invariantSGeV2,
double z) {
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);
38 double proposalJacobianReference(
double invariantSGeV2,
double z,
double y) {
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);
45 double unpolarizedAngularWeightReference(
double invariantSGeV2,
double z) {
47 if (invariantSGeV2 <= threshold) {
51 const double w = 0.5 * std::sqrt(invariantSGeV2);
52 const double beta = std::sqrt(std::max(0.0, 1.0 - threshold / invariantSGeV2));
55 const double y = proposalYReference(invariantSGeV2, z);
57 * (beta * (1.0 - 2.0 * y) - 1.0);
59 * (beta * (1.0 - 2.0 * y) + 1.0);
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);
68TEST(TestLinearBreitWheeler, HeadOnThresholdMatchesAnalyticCondition) {
69 const double wavelength_m = 1.0e-9;
70 const double laserPhotonEnergyGeV =
72 const double expectedThresholdPhotonEnergyGeV =
75 const auto n1 = makeDirection(0.0, 0.0, 1.0);
76 const auto n2 = makeDirection(0.0, 0.0, -1.0);
79 expectedThresholdPhotonEnergyGeV, laserPhotonEnergyGeV, n1, n2);
81 EXPECT_NEAR(computedS, thresholdS, thresholdS * 1.0e-12);
85TEST(TestLinearBreitWheeler, TotalCrossSectionVanishesBelowThreshold) {
90TEST(TestLinearBreitWheeler, TotalCrossSectionIsPositiveAboveThreshold) {
93 EXPECT_GT(sigma, 0.0);
96TEST(TestLinearBreitWheeler, ProposalCoordinateMapsToExpectedScatteringCosine) {
98 for (
const double z : {-0.75, 0.0, 0.65}) {
99 const double expected = 1.0 - 2.0 * proposalYReference(sGeV2, z);
106TEST(TestLinearBreitWheeler, UnpolarizedAngularWeightMatchesIndependentReference) {
108 for (
const double z : {-0.75, -0.10, 0.55}) {
109 const double expected = unpolarizedAngularWeightReference(sGeV2, z);
112 std::max(1.0e-15, std::abs(expected) * 1.0e-13));
116TEST(TestLinearBreitWheeler, SampledEventConservesFourMomentumForHeadOnGeometry) {
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);
128 highEnergyPhotonEnergyGeV, laserPhotonEnergyGeV, highEnergyDirection, laserDirection);
133 incomingMomentum += highEnergyPhotonEnergyGeV * highEnergyDirection;
134 incomingMomentum += laserPhotonEnergyGeV * laserDirection;
137 event.electronMomentumLabGeV +
event.positronMomentumLabGeV;
138 const double incomingEnergy = highEnergyPhotonEnergyGeV + laserPhotonEnergyGeV;
139 const double outgoingEnergy =
event.electronEnergyLabGeV +
event.positronEnergyLabGeV;
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);
153TEST(TestLinearBreitWheeler, FixedSeedProducesDeterministicSampleSequence) {
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);
165 highEnergyPhotonEnergyGeV, laserPhotonEnergyGeV, highEnergyDirection, laserDirection);
169 for (
int i = 0; i < 8; ++i) {
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);
ippl::Vector< T, Dim > Vector_t
TEST(TestLinearBreitWheeler, HeadOnThresholdMatchesAnalyticCondition)
int seed
The current random seed.
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.
constexpr double m_e
The electron rest mass in GeV.