OPALX (Object Oriented Parallel Accelerator Library for Exascal) master (dc2a29eed580)
OPALX
Loading...
Searching...
No Matches
PionDecay.cpp
Go to the documentation of this file.
2
4#include "Physics/Physics.h"
5
7 std::size_t localDestroyNum, std::size_t oldDaughterLocal,
8 const Kokkos::View<ippl::Vector<double, 3>*>& parentR,
9 const Kokkos::View<ippl::Vector<double, 3>*>& parentP,
10 const Kokkos::View<double*>& parentDt,
11 const Kokkos::View<ippl::Vector<float, 3>*>& /*parentPol*/) {
12 using pc_size_type = ippl::detail::size_type;
13
14 auto dR = daughterPC_m->R.getView();
15 auto dP = daughterPC_m->P.getView();
16 auto dDt = daughterPC_m->dt.getView();
17
18 // Daughter muon polarization view (empty unallocated View when daughter has no Pol).
19 const bool daughterHasSpin = daughterPC_m->hasSpin();
20 using PolView_t = Kokkos::View<ippl::Vector<float, 3>*>;
21 PolView_t dPol;
22 if (daughterHasSpin) {
23 dPol = daughterPC_m->Pol.getView();
24 }
25
26 // V-A: in the pion rest frame the muon is fully helicity-polarized.
27 // pi- -> mu- + anti-nu_mu : mu- spin parallel to its momentum (h = +1)
28 // pi+ -> mu+ + nu_mu : mu+ spin anti-parallel to momentum (h = -1)
29 // parentChargeSign_m is set in Decay::apply from the parent reference.
30 const double helicitySign = (parentChargeSign_m < 0) ? +1.0 : -1.0;
31
32 const auto pool = randPool_m;
33 const double daughterMass = daughterMassGeV_m;
34
35 /* Kinematics of pi -> mu + nu_mu (two-body decay).
36 *
37 * With one massless neutrino, energy-momentum conservation in the pion
38 * rest frame fixes the muon momentum uniquely:
39 * E_mu = (M_pi^2 + m_mu^2) / (2 M_pi)
40 * |p| = (M_pi^2 - m_mu^2) / (2 M_pi)
41 *
42 * The direction is isotropic (pion is spin-0).
43 *
44 * Procedure per daughter:
45 * 1. Compute the fixed rest-frame momentum (done once, outside the loop).
46 * 2. Pick an isotropic direction in the parent rest frame.
47 * 3. Lorentz-boost the rest-frame 4-momentum to the lab frame using the
48 * parent's beta*gamma vector.
49 * 4. Store the result as beta*gamma = p_lab / m_daughter. */
50 const double M = parentMassGeV_m;
51 const double md = daughterMassGeV_m;
52 const double pFixed = (M * M - md * md) / (2.0 * M);
53 const double eDaughter = Kokkos::sqrt(pFixed * pFixed + md * md);
54
55 Kokkos::parallel_for(
56 "PionDecay::createDaughters", static_cast<pc_size_type>(localDestroyNum),
57 KOKKOS_LAMBDA(const pc_size_type j) {
58 auto gen = pool.get_state();
59
60 /* Step 2: Isotropic direction in parent rest frame. */
61 const double cosTheta = 2.0 * gen.drand(0.0, 1.0) - 1.0;
62 const double sinTheta = Kokkos::sqrt(1.0 - cosTheta * cosTheta);
63 const double phi = 2.0 * Physics::pi * gen.drand(0.0, 1.0);
64 const double pxRF = pFixed * sinTheta * Kokkos::cos(phi);
65 const double pyRF = pFixed * sinTheta * Kokkos::sin(phi);
66 const double pzRF = pFixed * cosTheta;
67
68 /* Step 3: Lorentz boost to lab frame.
69 * Parent momentum is stored as beta*gamma (dimensionless).
70 * beta = P / gamma, with gamma = sqrt(1 + |P|^2).
71 * The boost formula for the spatial components is:
72 * p_lab = p_RF + [(gamma-1)(beta . p_RF)/beta^2 + gamma*E] * beta */
73 const auto& parentMom = parentP(j);
74 const double bg2 = parentMom[0] * parentMom[0] + parentMom[1] * parentMom[1]
75 + parentMom[2] * parentMom[2];
76 const double gamma = Kokkos::sqrt(1.0 + bg2);
77 const double betaX = parentMom[0] / gamma;
78 const double betaY = parentMom[1] / gamma;
79 const double betaZ = parentMom[2] / gamma;
80 const double beta2 = bg2 / (gamma * gamma);
81
82 double pxLab, pyLab, pzLab;
83 if (beta2 > 0.0) {
84 const double betaDotP = betaX * pxRF + betaY * pyRF + betaZ * pzRF;
85 const double factor = (gamma - 1.0) * betaDotP / beta2 + gamma * eDaughter;
86 pxLab = pxRF + factor * betaX;
87 pyLab = pyRF + factor * betaY;
88 pzLab = pzRF + factor * betaZ;
89 } else {
90 pxLab = pxRF;
91 pyLab = pyRF;
92 pzLab = pzRF;
93 }
94
95 /* Step 4: Write daughter attributes.
96 * Position inherits from parent. Momentum stored as beta*gamma = p / m. */
97 const pc_size_type idx = static_cast<pc_size_type>(oldDaughterLocal) + j;
98 dR(idx) = parentR(j);
99 dP(idx)[0] = pxLab / daughterMass;
100 dP(idx)[1] = pyLab / daughterMass;
101 dP(idx)[2] = pzLab / daughterMass;
102 dDt(idx) = parentDt(j);
103
104 // Daughter muon polarization: full helicity along its rest-frame momentum.
105 // The boost from pion rest frame to lab is along the muon's rest-frame
106 // momentum, so the spin component along that axis is invariant — the
107 // rest-frame unit vector is the correct lab-frame polarization vector.
108 if (daughterHasSpin) {
109 const double inv = helicitySign / pFixed;
110 dPol(idx)[0] = static_cast<float>(pxRF * inv);
111 dPol(idx)[1] = static_cast<float>(pyRF * inv);
112 dPol(idx)[2] = static_cast<float>(pzRF * inv);
113 }
114
115 pool.free_state(gen);
116 });
117 Kokkos::fence();
118}
Kokkos::Random_XorShift64_Pool randPool_m
Random pool for decay sampling.
Definition Decay.h:101
double parentMassGeV_m
Rest mass of the parent particle [GeV].
Definition Decay.h:114
std::shared_ptr< ParticleContainer< double, 3 > > daughterPC_m
Daughter container for decay products (nullptr = destroy-only mode).
Definition Decay.h:104
int parentChargeSign_m
Definition Decay.h:118
double daughterMassGeV_m
Rest mass of the daughter particle [GeV].
Definition Decay.h:111
void createDaughterParticles(std::size_t localDestroyNum, std::size_t oldDaughterLocal, const Kokkos::View< ippl::Vector< double, 3 > * > &parentR, const Kokkos::View< ippl::Vector< double, 3 > * > &parentP, const Kokkos::View< double * > &parentDt, const Kokkos::View< ippl::Vector< float, 3 > * > &parentPol) override
Create daughter particles from collected parent data.
Definition PionDecay.cpp:6
constexpr double pi
The value of.
Definition Physics.h:36