OPALX (Object Oriented Parallel Accelerator Library for Exascal) master (dc2a29eed580)
OPALX
Loading...
Searching...
No Matches
Decay.cpp
Go to the documentation of this file.
2
5#include "Physics/Physics.h"
7#include "Utilities/Options.h"
8
9#include <cmath>
10#include <cstdint>
11#include <functional>
12#include <string>
13
14namespace {
15
16 uint64_t decayRngSeed(std::size_t containerIndex) {
17 const uint64_t seedBase =
18 static_cast<uint64_t>((Options::seed == -1) ? 1234567 : Options::seed);
19 return seedBase + 104729ULL * static_cast<uint64_t>(containerIndex + 1)
20 + 8191ULL * static_cast<uint64_t>(ippl::Comm->rank() + 1);
21 }
22
23} // namespace
24
26 double restLifetimeSeconds, std::size_t containerIndex, double parentMassGeV,
27 int parentChargeSign)
28 : tau0_m(restLifetimeSeconds),
29 randPool_m(decayRngSeed(containerIndex)),
30 parentMassGeV_m(parentMassGeV),
31 parentChargeSign_m(parentChargeSign) {}
32
34 ParticleContainer<double, 3>& pc, double dt, [[maybe_unused]] long long globalTrackStep,
35 [[maybe_unused]] size_t containerIdx) {
36 using pc_size_type = ippl::detail::size_type;
37
38 if (dt <= 0.0 || !(tau0_m > 0.0) || !std::isfinite(tau0_m)) {
39 return 0;
40 }
41
42 const pc_size_type nLocal = pc.getLocalNum();
43
44 /* Phase 1: Mark decayed particles using relativistic decay probability. */
45 Kokkos::View<bool*> decayed("Decay::decayed", nLocal);
46 const pc_size_type localDestroyNum = markDecayedParticles(nLocal, dt, pc.P.getView(), decayed);
47
48 auto invalidMask = pc.InvalidMask.getView();
49 Kokkos::parallel_for(
50 "Decay::orInvalidMask", nLocal,
51 KOKKOS_LAMBDA(const pc_size_type i) { invalidMask(i) = invalidMask(i) || decayed(i); });
52 Kokkos::fence();
53
54 pc_size_type globalDestroyNum = 0;
55 ippl::Comm->allreduce(localDestroyNum, globalDestroyNum, 1, std::plus<pc_size_type>());
56 if (globalDestroyNum == 0) {
57 return 0;
58 }
59
60 if (daughterPC_m) {
61 /* Phase 2: Gather kinematics of decayed parents into compact views. */
62 // parentPolView is empty for containers not tracking spin.
63 Kokkos::View<ippl::Vector<float, 3>*> parentPolView;
64 if (pc.hasSpin()) {
65 parentPolView = pc.Pol.getView();
66 }
68 nLocal, localDestroyNum, decayed, pc.R.getView(), pc.P.getView(), pc.dt.getView(),
69 parentPolView);
70
71 /* Phase 3: Create daughters — subclass-specific momentum sampling. */
72 // createParticles() is non-destructive and warns if the daughter buffer must grow.
73 const pc_size_type oldDaughterLocal = daughterPC_m->getLocalNum();
74 daughterPC_m->createParticles(localDestroyNum);
75
76 if (localDestroyNum > 0) {
78 localDestroyNum, oldDaughterLocal, parents.R, parents.P, parents.dt,
79 parents.Pol);
80 }
81 }
82
83 return static_cast<size_t>(globalDestroyNum);
84}
85
86ippl::detail::size_type Decay::markDecayedParticles(
87 ippl::detail::size_type nLocal, double dt, Kokkos::View<ippl::Vector<double, 3>*> Pview,
88 Kokkos::View<bool*> decayed) {
89 using pc_size_type = ippl::detail::size_type;
90
91 // Local copies — KOKKOS_LAMBDA captures these, not `this`.
92 const double tau0 = tau0_m;
93 auto pool = randPool_m;
94
95 pc_size_type localDestroyNum = 0;
96 Kokkos::parallel_reduce(
97 "Decay::mark", nLocal,
98 KOKKOS_LAMBDA(const pc_size_type i, pc_size_type& count) {
99 auto generator = pool.get_state();
100 const double p2 = Pview(i)[0] * Pview(i)[0] + Pview(i)[1] * Pview(i)[1]
101 + Pview(i)[2] * Pview(i)[2];
102 const double gamma = Kokkos::sqrt(1.0 + p2);
103 const double tauLab = gamma * tau0;
104 const double pDecay = 1.0 - Kokkos::exp(-dt / tauLab);
105 const bool didDecay = generator.drand(0.0, 1.0) < pDecay;
106 decayed(i) = didDecay;
107 count += didDecay ? 1 : 0;
108 pool.free_state(generator);
109 },
110 localDestroyNum);
111 Kokkos::fence();
112 return localDestroyNum;
113}
114
116 ippl::detail::size_type nLocal, ippl::detail::size_type localDestroyNum,
117 Kokkos::View<bool*> decayed, Kokkos::View<ippl::Vector<double, 3>*> Rview,
118 Kokkos::View<ippl::Vector<double, 3>*> Pview, Kokkos::View<double*> dtView,
119 Kokkos::View<ippl::Vector<float, 3>*> PolView) {
120 using pc_size_type = ippl::detail::size_type;
121
122 Kokkos::View<pc_size_type*> compactIdx("Decay::compactIdx", nLocal);
123 Kokkos::parallel_scan(
124 "Decay::compact", nLocal,
125 KOKKOS_LAMBDA(const pc_size_type i, pc_size_type& runningTotal, const bool isFinal) {
126 if (decayed(i)) {
127 if (isFinal) {
128 compactIdx(i) = runningTotal;
129 }
130 ++runningTotal;
131 }
132 });
133 Kokkos::fence();
134
135 const bool hasPol = PolView.extent(0) > 0;
137 Kokkos::View<ippl::Vector<double, 3>*>("Decay::parentR", localDestroyNum),
138 Kokkos::View<ippl::Vector<double, 3>*>("Decay::parentP", localDestroyNum),
139 Kokkos::View<double*>("Decay::parentDt", localDestroyNum),
140 Kokkos::View<ippl::Vector<float, 3>*>(
141 "Decay::parentPol", hasPol ? localDestroyNum : 0)};
142
143 Kokkos::parallel_for(
144 "Decay::collectParents", nLocal, KOKKOS_LAMBDA(const pc_size_type i) {
145 if (decayed(i)) {
146 const pc_size_type j = compactIdx(i);
147 out.R(j) = Rview(i);
148 out.P(j) = Pview(i);
149 out.dt(j) = dtView(i);
150 // Does not cause thread divergence because hasPol is always either
151 // true of false -> All threads do the same thing.
152 if (hasPol) {
153 out.Pol(j) = PolView(i);
154 }
155 }
156 });
157 Kokkos::fence();
158 return out;
159}
160
162 std::shared_ptr<ParticleContainer<double, 3>> daughterPC, double daughterMassGeV) {
163 if (daughterPC && daughterPC->Sp != allowedDaughterSpecies_m) {
164 const auto expected = static_cast<ParticleType>(allowedDaughterSpecies_m);
165 const auto actual = static_cast<ParticleType>(daughterPC->Sp);
166 throw OpalException(
167 "Decay::setDaughterContainer",
168 "Daughter container species mismatch: expected \""
169 + ParticleProperties::getParticleTypeString(expected) + "\" but got \""
171 }
172 daughterPC_m = std::move(daughterPC);
173 daughterMassGeV_m = daughterMassGeV;
174}
DecayedParentViews collectDecayedParents(ippl::detail::size_type nLocal, ippl::detail::size_type localDestroyNum, Kokkos::View< bool * > decayed, Kokkos::View< ippl::Vector< double, 3 > * > Rview, Kokkos::View< ippl::Vector< double, 3 > * > Pview, Kokkos::View< double * > dtView, Kokkos::View< ippl::Vector< float, 3 > * > PolView)
Gather R/P/dt of parents marked for decay into compact views.
Definition Decay.cpp:115
ippl::detail::size_type markDecayedParticles(ippl::detail::size_type nLocal, double dt, Kokkos::View< ippl::Vector< double, 3 > * > Pview, Kokkos::View< bool * > decayed)
Mark particles for decay using the relativistic exponential law.
Definition Decay.cpp:86
Kokkos::View< ippl::Vector< double, 3 > * > R
Definition Decay.h:68
double tau0_m
Mean lifetime at rest [s].
Definition Decay.h:98
Kokkos::Random_XorShift64_Pool randPool_m
Random pool for decay sampling.
Definition Decay.h:101
virtual 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)=0
Create daughter particles from collected parent data.
Kokkos::View< ippl::Vector< double, 3 > * > P
Definition Decay.h:69
Kokkos::View< double * > dt
Definition Decay.h:70
Decay(double restLifetimeSeconds, std::size_t containerIndex, double parentMassGeV, int parentChargeSign)
Definition Decay.cpp:25
size_t apply(ParticleContainer< double, 3 > &pc, double dt, long long globalTrackStep, size_t containerIdx) override
Definition Decay.cpp:33
Kokkos::View< ippl::Vector< float, 3 > * > Pol
Definition Decay.h:71
std::shared_ptr< ParticleContainer< double, 3 > > daughterPC_m
Daughter container for decay products (nullptr = destroy-only mode).
Definition Decay.h:104
short allowedDaughterSpecies_m
Definition Decay.h:108
void setDaughterContainer(std::shared_ptr< ParticleContainer< double, 3 > > daughterPC, double daughterMassGeV)
Set the daughter particle container and its rest mass.
Definition Decay.cpp:161
double daughterMassGeV_m
Rest mass of the daughter particle [GeV].
Definition Decay.h:111
Container for all per-particle (and per-simulation) fields tracked during OPALX tracking.
ippl::ParticleAttrib< bool > InvalidMask
particle deletion mask (indicates which particles are deleted every timestep)
ippl::ParticleAttrib< double > dt
timestep in [s]
ippl::ParticleAttrib< spin_vector_type > Pol
Base::particle_position_type P
particle momenta [\beta\gamma]
static std::string getParticleTypeString(const ParticleType &type)
int seed
The current random seed.
Definition Options.cpp:37