17 const double norm = std::sqrt(
dot(direction, direction));
20 where, std::string(
"\"") + argument +
"\" must be a non-zero vector.");
23 return direction / norm;
26 double clampCosine(
double value) {
return std::max(-1.0, std::min(1.0, value)); }
28 void ensureStrictlyPositive(
double value,
const char* where,
const char* argument) {
30 throw OpalException(where, std::string(
"\"") + argument +
"\" must be greater than 0.");
34 void ensureCosineRange(
double cosine,
const char* where,
const char* argument) {
35 if (cosine < -1.0 - 1.0e-12 || cosine > 1.0 + 1.0e-12) {
36 throw OpalException(where, std::string(
"\"") + argument +
"\" must lie in [-1, 1].");
43 if (std::abs(direction(0)) > 0.9) {
55 -
dot(boostDirectionLab, incomingDirectionERF) * incomingDirectionERF;
56 const double axisNorm = std::sqrt(
dot(axis, axis));
57 if (axisNorm > 1.0e-14) {
58 return axis / axisNorm;
61 axis =
cross(incomingDirectionERF, chooseReferenceDirection(incomingDirectionERF));
62 return normalizeDirection(axis, where,
"scatteringPlaneAxis");
68 double azimuthERF,
const char* where) {
69 ensureCosineRange(scatteringCosineERF, where,
"scatteringCosineERF");
70 const double clampedCosine = clampCosine(scatteringCosineERF);
71 const double sinTheta = std::sqrt(std::max(0.0, 1.0 - clampedCosine * clampedCosine));
73 scatteringPlaneAxis(incomingDirectionERF, boostDirectionLab, where);
75 normalizeDirection(
cross(incomingDirectionERF, axis1), where,
"axis2");
76 return normalizeDirection(
77 clampedCosine * incomingDirectionERF
78 + sinTheta * (std::cos(azimuthERF) * axis1 + std::sin(azimuthERF) * axis2),
79 where,
"scatteredDirectionERF");
82 std::uint64_t deterministicHostSeed() {
86 std::uint64_t mixSeed(std::uint64_t seed, std::uint64_t streamIndex) {
87 const std::uint64_t golden = 0x9E3779B97F4A7C15ULL;
88 std::uint64_t mixed =
seed + golden * (streamIndex + 1ULL);
89 mixed ^= (mixed >> 30);
90 mixed *= 0xBF58476D1CE4E5B9ULL;
91 mixed ^= (mixed >> 27);
92 mixed *= 0x94D049BB133111EBULL;
93 mixed ^= (mixed >> 31);
97 double rejectionUpperBoundSolidAngleERF(
double incomingPhotonEnergyERFGeV) {
98 constexpr int samples = 4096;
99 double upperBound = 0.0;
100 for (
int i = 0; i <= samples; ++i) {
101 const double mu = -1.0 + 2.0 *
static_cast<double>(i) /
static_cast<double>(samples);
102 upperBound = std::max(
104 incomingPhotonEnergyERFGeV, mu));
107 return std::max(upperBound * 1.0001, std::numeric_limits<double>::min());
112 namespace LinearCompton {
115 constexpr const char* where =
"Physics::LinearCompton::photonEnergyFromWavelengthGeV()";
116 ensureStrictlyPositive(wavelength_m, where,
"wavelength_m");
121 constexpr const char* where =
"Physics::LinearCompton::electronGamma()";
125 "Electron total energy must be larger than the electron rest energy.");
133 return std::sqrt(1.0 - 1.0 / (gamma * gamma));
137 double electronTotalEnergyGeV,
double laserPhotonEnergyGeV,
140 constexpr const char* where =
141 "Physics::LinearCompton::restFrameIncomingPhotonEnergyGeV()";
142 ensureStrictlyPositive(laserPhotonEnergyGeV, where,
"laserPhotonEnergyGeV");
145 const double beta =
electronBeta(electronTotalEnergyGeV);
147 normalizeDirection(beamDirection, where,
"beamDirection");
149 normalizeDirection(laserDirection, where,
"laserDirection");
150 const double cosAlpha =
151 clampCosine(
dot(normalizedBeamDirection, normalizedLaserDirection));
153 return gamma * laserPhotonEnergyGeV * (1.0 - beta * cosAlpha);
159 constexpr const char* where =
160 "Physics::LinearCompton::restFrameIncomingPhotonDirection()";
162 const double beta =
electronBeta(electronTotalEnergyGeV);
164 normalizeDirection(beamDirection, where,
"beamDirection");
166 normalizeDirection(laserDirection, where,
"laserDirection");
167 const double cosAlpha =
168 clampCosine(
dot(normalizedBeamDirection, normalizedLaserDirection));
169 const double denominator = 1.0 - beta * cosAlpha;
171 normalizedLaserDirection - cosAlpha * normalizedBeamDirection;
173 transverseComponent / (gamma * denominator)
174 + ((cosAlpha - beta) / denominator) * normalizedBeamDirection;
175 return normalizeDirection(directionERF, where,
"directionERF");
182 constexpr const char* where =
"Physics::LinearCompton::labPhotonDirection()";
184 const double beta =
electronBeta(electronTotalEnergyGeV);
186 normalizeDirection(beamDirection, where,
"beamDirection");
188 electronTotalEnergyGeV, beamDirection, laserDirection);
190 incomingDirectionERF, normalizedBeamDirection, scatteringCosineERF, azimuthERF,
192 const double parallelCosine =
dot(outgoingDirectionERF, normalizedBeamDirection);
194 outgoingDirectionERF - parallelCosine * normalizedBeamDirection;
196 transverseDirection + gamma * (parallelCosine + beta) * normalizedBeamDirection;
197 return normalizeDirection(boostedDirection, where,
"boostedDirection");
201 return std::mt19937_64(mixSeed(deterministicHostSeed(), streamIndex));
205 double electronTotalEnergyGeV,
double laserPhotonEnergyGeV,
208 constexpr const char* where =
"Physics::LinearCompton::makeSamplingKernel()";
209 ensureStrictlyPositive(laserPhotonEnergyGeV, where,
"laserPhotonEnergyGeV");
214 kernel.
beamDirection = normalizeDirection(beamDirection, where,
"beamDirection");
215 kernel.
laserDirection = normalizeDirection(laserDirection, where,
"laserDirection");
217 electronTotalEnergyGeV, laserPhotonEnergyGeV, kernel.
beamDirection,
225 constexpr const char* where =
"Physics::LinearCompton::sampleEvent()";
226 ensureStrictlyPositive(
228 ensureStrictlyPositive(
230 "rejectionUpperBoundSolidAngleERF");
232 std::uniform_real_distribution<double> cosineDistribution(-1.0, 1.0);
233 std::uniform_real_distribution<double> azimuthDistribution(0.0,
Physics::two_pi);
234 std::uniform_real_distribution<double> acceptDistribution(
237 for (
int attempt = 0; attempt < 1000000; ++attempt) {
238 const double scatteringCosineERF = cosineDistribution(engine);
241 if (acceptDistribution(engine) > differentialCrossSection) {
247 event.azimuthERF = azimuthDistribution(engine);
261 where,
"Rejection sampler failed to accept an event within the attempt limit.");
265 constexpr const char* where =
"Physics::LinearCompton::invariantKappa()";
266 ensureStrictlyPositive(incomingPhotonEnergyERFGeV, where,
"incomingPhotonEnergyERFGeV");
275 double electronTotalEnergyGeV,
double laserPhotonEnergyGeV,
279 electronTotalEnergyGeV, laserPhotonEnergyGeV, beamDirection, laserDirection));
284 return incomingPhotonEnergyERFGeV / (1.0 + 2.0 * kappa);
289 return incomingPhotonEnergyERFGeV;
293 double incomingPhotonEnergyERFGeV,
double scatteringCosineERF) {
294 constexpr const char* where =
"Physics::LinearCompton::scatteredPhotonEnergyERFGeV()";
296 ensureCosineRange(scatteringCosineERF, where,
"scatteringCosineERF");
297 const double clampedCosine = clampCosine(scatteringCosineERF);
298 return incomingPhotonEnergyERFGeV / (1.0 + kappa * (1.0 - clampedCosine));
302 double incomingPhotonEnergyERFGeV,
double scatteringCosineERF) {
303 constexpr const char* where =
304 "Physics::LinearCompton::differentialCrossSectionSolidAngleERF()";
306 ensureCosineRange(scatteringCosineERF, where,
"scatteringCosineERF");
307 const double clampedCosine = clampCosine(scatteringCosineERF);
308 const double omega2 =
310 const double energyRatio = omega2 / incomingPhotonEnergyERFGeV;
311 const double sin2 = std::max(0.0, 1.0 - clampedCosine * clampedCosine);
313 * (1.0 / energyRatio + energyRatio - sin2);
318 constexpr const char* where =
319 "Physics::LinearCompton::differentialCrossSectionOmegaERF()";
326 "\"scatteredPhotonEnergyERFGeV\" is outside the physical Compton support.");
329 const double clampedEnergy =
331 const double cosine =
332 1.0 -
Physics::m_e * (1.0 / clampedEnergy - 1.0 / incomingPhotonEnergyERFGeV);
333 const double clampedCosine = clampCosine(cosine);
335 incomingPhotonEnergyERFGeV, clampedCosine);
336 const double jacobian =
Physics::m_e / (clampedEnergy * clampedEnergy);
345 if (kappa < 1.0e-3) {
349 const double logTerm = std::log(1.0 + 2.0 * kappa);
351 const double bracket =
352 ((1.0 + kappa) / (kappa * kappa))
353 * ((2.0 * (1.0 + kappa)) / (1.0 + 2.0 * kappa) - logTerm / kappa)
354 + logTerm / (2.0 * kappa)
355 - (1.0 + 3.0 * kappa) / ((1.0 + 2.0 * kappa) * (1.0 + 2.0 * kappa));
356 return prefactor * bracket;
366 constexpr const char* where =
367 "Physics::LinearCompton::restFrameScatteringCosineForLabForwardPhoton()";
368 const double beta =
electronBeta(electronTotalEnergyGeV);
370 normalizeDirection(beamDirection, where,
"beamDirection");
372 normalizeDirection(laserDirection, where,
"laserDirection");
373 const double cosAlpha =
374 clampCosine(
dot(normalizedBeamDirection, normalizedLaserDirection));
375 return clampCosine((cosAlpha - beta) / (1.0 - beta * cosAlpha));
379 double electronTotalEnergyGeV,
double laserPhotonEnergyGeV,
381 double scatteringCosineERF,
double azimuthERF) {
382 constexpr const char* where =
"Physics::LinearCompton::labPhotonEnergyGeV()";
383 ensureStrictlyPositive(laserPhotonEnergyGeV, where,
"laserPhotonEnergyGeV");
384 ensureCosineRange(scatteringCosineERF, where,
"scatteringCosineERF");
387 const double beta =
electronBeta(electronTotalEnergyGeV);
389 normalizeDirection(beamDirection, where,
"beamDirection");
391 electronTotalEnergyGeV, beamDirection, laserDirection);
393 electronTotalEnergyGeV, laserPhotonEnergyGeV, beamDirection, laserDirection);
394 const double clampedCosine = clampCosine(scatteringCosineERF);
396 incomingDirectionERF, normalizedBeamDirection, clampedCosine, azimuthERF,
399 return gamma * omega2
400 * (1.0 + beta *
dot(outgoingDirectionERF, normalizedBeamDirection));
404 double electronTotalEnergyGeV,
double laserPhotonEnergyGeV,
408 electronTotalEnergyGeV, beamDirection, laserDirection);
410 electronTotalEnergyGeV, laserPhotonEnergyGeV, beamDirection, laserDirection,
411 scatteringCosine, 0.0);
ippl::Vector< T, Dim > Vector_t
Vector3D cross(const Vector3D &lhs, const Vector3D &rhs)
Vector cross product.
double dot(const Vector3D &lhs, const Vector3D &rhs)
Vector dot product.
int seed
The current random seed.
double differentialCrossSectionOmegaERF(double incomingPhotonEnergyERFGeV, double scatteredPhotonEnergyERFGeV)
Unpolarized Klein-Nishina differential cross section in the scattered-photon energy.
double restFrameScatteringCosineForLabForwardPhoton(double electronTotalEnergyGeV, const Vector_t< double, 3 > &beamDirection, const Vector_t< double, 3 > &laserDirection)
Rest-frame scattering cosine corresponding to a lab-forward photon.
double rejectionUpperBoundSolidAngleERF
SamplingKernel makeSamplingKernel(double electronTotalEnergyGeV, double laserPhotonEnergyGeV, const Vector_t< double, 3 > &beamDirection, const Vector_t< double, 3 > &laserDirection)
Build a cached sampling kernel for repeated event generation.
double labForwardPhotonEnergyGeV(double electronTotalEnergyGeV, double laserPhotonEnergyGeV, const Vector_t< double, 3 > &beamDirection, const Vector_t< double, 3 > &laserDirection)
Forward scattered photon energy in the laboratory frame.
double electronGamma(double electronTotalEnergyGeV)
Compute the electron Lorentz factor from total energy.
Vector_t< double, 3 > labPhotonDirection(double electronTotalEnergyGeV, const Vector_t< double, 3 > &beamDirection, const Vector_t< double, 3 > &laserDirection, double scatteringCosineERF, double azimuthERF)
Laboratory-frame photon direction from electron-rest-frame scattering angles.
double scatteringCosineERF
Rest-frame polar scattering cosine .
double totalCrossSection(double incomingPhotonEnergyERFGeV)
Total unpolarized Klein-Nishina cross section.
double differentialCrossSectionSolidAngleERF(double incomingPhotonEnergyERFGeV, double scatteringCosineERF)
Unpolarized Klein-Nishina differential cross section in solid angle.
double electronTotalEnergyGeV
Incoming electron total energy in the lab frame [GeV].
std::mt19937_64 makeHostRandomEngine(std::uint64_t streamIndex)
Create a deterministic host-side random engine for Monte Carlo validation.
SampledEvent sampleEvent(const SamplingKernel &kernel, std::mt19937_64 &engine)
Sample one unpolarized linear-Compton event on the host.
Vector_t< double, 3 > beamDirection
Normalized incoming electron direction in the lab frame.
double invariantX(double incomingPhotonEnergyERFGeV)
Compute CAIN's linear-Compton invariant .
double incomingPhotonEnergyERFGeV
double labPhotonEnergyGeV(double electronTotalEnergyGeV, double laserPhotonEnergyGeV, const Vector_t< double, 3 > &beamDirection, const Vector_t< double, 3 > &laserDirection, double scatteringCosineERF, double azimuthERF)
Laboratory-frame photon energy from electron-rest-frame scattering angles.
double laserPhotonEnergyGeV
Incoming laser-photon energy in the lab frame [GeV].
double thomsonCrossSection()
Thomson limit cross section.
double scatteredPhotonEnergyMaxERFGeV(double incomingPhotonEnergyERFGeV)
Maximum scattered photon energy in the electron rest frame.
Vector_t< double, 3 > laserDirection
Normalized incoming laser direction in the lab frame.
Vector_t< double, 3 > restFrameIncomingPhotonDirection(double electronTotalEnergyGeV, const Vector_t< double, 3 > &beamDirection, const Vector_t< double, 3 > &laserDirection)
Incoming laser-photon direction in the electron rest frame.
double scatteredPhotonEnergyERFGeV(double incomingPhotonEnergyERFGeV, double scatteringCosineERF)
Compute the scattered photon energy in the electron rest frame.
double electronBeta(double electronTotalEnergyGeV)
Compute the electron speed in units of c from total energy.
double restFrameIncomingPhotonEnergyGeV(double electronTotalEnergyGeV, double laserPhotonEnergyGeV, const Vector_t< double, 3 > &beamDirection, const Vector_t< double, 3 > &laserDirection)
Compute the incoming photon energy in the electron rest frame.
double photonEnergyFromWavelengthGeV(double wavelength_m)
Convert a laser wavelength to a single-photon energy.
double scatteredPhotonEnergyMinERFGeV(double incomingPhotonEnergyERFGeV)
Minimum scattered photon energy in the electron rest frame.
double invariantKappa(double incomingPhotonEnergyERFGeV)
Compute the dimensionless Klein-Nishina parameter .
Sampled single-photon Compton event.
Host-only cached data for repeated linear-Compton event sampling.
constexpr double two_pi
The value of.
constexpr double h_bar
The reduced Planck constant in GeVs.
constexpr double m_e
The electron rest mass in GeV.
constexpr double c
The velocity of light in m/s.
constexpr double pi
The value of.
constexpr double r_e
The classical electron radius in m.