OPALX (Object Oriented Parallel Accelerator Library for Exascal) master (dc2a29eed580)
OPALX
Loading...
Searching...
No Matches
Gaussian.cpp
Go to the documentation of this file.
1#include "Gaussian.h"
2#include <algorithm>
3#include <memory>
4#include "Distribution.h"
5#include "SamplingBase.hpp"
7
16 std::shared_ptr<ParticleContainer_t> pc, std::shared_ptr<FieldContainer_t> fc,
17 Distribution_t* opalDist)
18 : SamplingBase(pc, fc, opalDist) {
19 samperTimer_m = IpplTimings::getTimer("Sampling");
21 setSigmaP(opalDist->getSigmaP());
22 setSigmaR(opalDist->getSigmaR());
23 setAvrgpz(opalDist->getAvrgpz());
24 setCutoffR(opalDist->getCutoffR());
25}
26
28 std::shared_ptr<ParticleContainer_t> pc, const Vector_t<double, 3>& sigmaR,
29 const Vector_t<double, 3>& sigmaP, double avrgpz, const Vector_t<double, 3>& cutoffR,
30 bool fixMeanR)
31 : SamplingBase(pc) {
32 setSigmaR(sigmaR);
33 setSigmaP(sigmaP);
34 setAvrgpz(avrgpz);
35 setCutoffR(cutoffR);
36 setFixMeanR(fixMeanR);
37
38 samperTimer_m = IpplTimings::getTimer("Sampling");
40}
41
43 extern Inform* gmsg;
44 size_t randInit;
45
46 if (Options::seed == -1) {
47 randInit = 1234567;
48 *gmsg << "* Seed = " << randInit << " on all ranks" << endl;
49 } else {
50 randInit = static_cast<size_t>(Options::seed + 100 * ippl::Comm->rank());
51 }
52
53 GeneratorPool rand_pool64(randInit);
54 randPool_m = rand_pool64;
55 return;
56}
57
64void Gaussian::generateParticles(size_t& numberOfParticles, Vector_t<double, 3> /*nr*/) {
65 if (emissionModel_m != "NONE")
66 throw OpalException(
67 "Gaussian::generateParticles",
68 "EMISSIONMODEL '" + emissionModel_m + "' is not supported for GAUSS distributions");
69
70 // Only generate during initial sampling (t0 <= 0). For t0 > 0, this
71 // distribution is time-independent and should not contribute here unless
72 // explicitly triggered via emitParticles (which sets hasEmittedOnce_m).
73 if (t0_m > 0.0 && !hasEmittedOnce_m) { // YES this !hasEmittedOnce_m is correct!
74 return;
75 }
76 auto rand_pool64 = randPool_m;
77
78 IpplTimings::startTimer(samperTimer_m);
79
80 double mu[3], sd[3];
81
84
85 for (int i = 0; i < 3; i++) {
86 mu[i] = 0.0;
87 sd[i] = sigmaR_m[i];
88 rmin(i) = (rmin(i) + mu[i]) * sigmaR_m[i];
89 rmax(i) = (rmax(i) + mu[i]) * sigmaR_m[i];
90 }
91
92 const double par[6] = {mu[0], sd[0], mu[1], sd[1], mu[2], sd[2]};
93
94 using Dist_t = ippl::random::NormalDistribution<double, 3>;
95 using sampling_t = ippl::random::InverseTransformSampling<
96 double, 3, Kokkos::DefaultExecutionSpace, Dist_t>;
97 Dist_t dist(par);
98
99 const int nranks = std::max(1, ippl::Comm->size());
100 // Use computeLocalEmitCount to distribute particles across ranks or uniform fallback.
101 size_t nlocal = pc_m ? computeLocalEmitCount(static_cast<size_t>(numberOfParticles))
102 : static_cast<size_t>(
103 floor(numberOfParticles / nranks)
104 + (ippl::Comm->rank() < static_cast<int>(
105 numberOfParticles % static_cast<size_t>(nranks))
106 ? 1
107 : 0));
108
109 sampling_t sampling(dist, rmax, rmin, rmax, rmin, nlocal);
110 nlocal = sampling.getLocalSamplesNum();
111
112 const size_t nlocalCurrent = pc_m->getLocalNum();
113 pc_m->createParticles(nlocal);
114
115 view_type RviewFull = pc_m->R.getView();
116 auto Rview = Kokkos::subview(RviewFull, std::make_pair(nlocalCurrent, nlocalCurrent + nlocal));
117
118 sampling.generate(Rview, rand_pool64);
119
120 if (fixMeanR_m) {
121 double meanR[3], loc_meanR[3];
122 for (int i = 0; i < 3; i++) {
123 meanR[i] = 0.0;
124 loc_meanR[i] = 0.0;
125 }
126
127 Kokkos::parallel_reduce(
128 "calc moments of particle distr.", nlocal,
129 KOKKOS_LAMBDA(const int k, double& cent0, double& cent1, double& cent2) {
130 cent0 += Rview(k)[0];
131 cent1 += Rview(k)[1];
132 cent2 += Rview(k)[2];
133 },
134 Kokkos::Sum<double>(loc_meanR[0]), Kokkos::Sum<double>(loc_meanR[1]),
135 Kokkos::Sum<double>(loc_meanR[2]));
136 Kokkos::fence();
137
138 MPI_Allreduce(loc_meanR, meanR, 3, MPI_DOUBLE, MPI_SUM, ippl::Comm->getCommunicator());
139 ippl::Comm->barrier();
140
141 for (int i = 0; i < 3; i++) {
142 meanR[i] = meanR[i] / (1.0 * numberOfParticles);
143 }
144
145 Kokkos::parallel_for(
146 nlocal, KOKKOS_LAMBDA(const int k) {
147 Rview(k)[0] -= meanR[0];
148 Rview(k)[1] -= meanR[1];
149 Rview(k)[2] -= meanR[2];
150 });
151 Kokkos::fence();
152 }
153
154 for (int i = 0; i < 3; i++) {
155 mu[i] = 0.0;
156 sd[i] = sigmaP_m[i];
157 }
158
159 view_type PviewFull = pc_m->P.getView();
160 auto Pview = Kokkos::subview(PviewFull, std::make_pair(nlocalCurrent, nlocalCurrent + nlocal));
161
162 Kokkos::parallel_for(nlocal, ippl::random::randn<double, 3>(Pview, rand_pool64, mu, sd));
163 Kokkos::fence();
164
165 double avrgpz = avrgpz_m;
166 Kokkos::parallel_for(nlocal, KOKKOS_LAMBDA(const size_t k) { Pview(k)[2] += avrgpz; });
167 Kokkos::fence();
168
169 // Apply per-emission-source offsets after all mean-fixing/corrections.
170 // EMISSIONSOURCE offsets are expected to translate the generated bunch
171 // without being affected by the internal "fix mean" logic.
172 const Vector_t<double, 3> R0 = R0_m;
173 const Vector_t<double, 3> P0 = P0_m;
174 Kokkos::parallel_for(
175 nlocal, KOKKOS_LAMBDA(const size_t k) {
176 Rview(k) += R0;
177 Pview(k) += P0;
178 });
179
180 fillPolarization(nlocalCurrent, nlocal);
181
182 pc_m->markMomentsDirty();
183
184 IpplTimings::stopTimer(samperTimer_m);
185}
186
187void Gaussian::emitParticles(double t, double dt) {
188 // One-shot delayed emission for GAUSS: emit once when [t, t+dt] crosses t0_m.
189 const double tStart = t;
190 const double tEnd = t + dt;
191
192 if (hasEmittedOnce_m) {
193 return;
194 }
195
196 // Only meaningful for t0 > 0.
197 if (t0_m <= 0.0) {
198 return;
199 }
200
201 // Fire when the time interval [tStart, tEnd] crosses t0_m.
202 if (!(tStart <= t0_m && t0_m < tEnd)) {
203 return;
204 }
205
206 if (!opalDist_m) {
207 return;
208 }
209 size_t Ndist = opalDist_m->getNumParticles();
210 if (Ndist == 0) {
211 return;
212 }
213
214 const size_t nlocalBefore = pc_m->getLocalNum();
215
216 // Mark as emitted so generateParticles will not early-return.
217 hasEmittedOnce_m = true;
218 Vector_t<double, 3> dummyNr(0.0);
219 generateParticles(Ndist, dummyNr);
220
221 // Set per-particle dt for newly created particles. Note that t0 can be in
222 // between particles, so we set the remaining fraction of the current time
223 // step for the new particles to ensure correct sub-stepping in the
224 // subsequent push.
225 //
226 // switchToUnitlessPositions(use_dt_per_particle=true) in pushParticles scales
227 // R by 1/(c * dtview(i)). New particles come with dtview = 0 (Kokkos
228 // zero-init), so that division produces inf, and the subsequent multiply by
229 // c*0 in switchOffUnitlessPositions gives inf*0 = NaN positions.
230 // Assign the remaining fraction of the current timestep so the push in
231 // timeIntegration2 moves them by the correct sub-step distance.
232 //
233 // Note that this is not necessary inside generateParticles, since for injection at start,
234 // setTime is called before any calculations inside ParallelTracker::execute.
235 const size_t nlocalAfter = pc_m->getLocalNum();
236 const size_t nNew = nlocalAfter - nlocalBefore;
237 if (nNew > 0) {
238 const double fracDt = tEnd - t0_m;
239 auto dtview = pc_m->dt.getView();
240 const size_t offset = nlocalBefore;
241 Kokkos::parallel_for(
242 "Gaussian_setDt", nNew,
243 KOKKOS_LAMBDA(const size_t j) { dtview(offset + j) = fracDt; });
244 Kokkos::fence();
245 }
246}
Inform * gmsg
Definition changes.cpp:7
ippl::Vector< T, Dim > Vector_t
typename ippl::detail::ViewType< ippl::Vector< double, Dim >, 1 >::view_type view_type
typename Kokkos::Random_XorShift64_Pool<> GeneratorPool
Definition Gaussian.h:13
ippl::random::NormalDistribution< double, 3 > Dist_t
Definition Gaussian.h:14
size_t getNumParticles() const
ippl::Vector< double, 3 > getSigmaP() const
ippl::Vector< double, 3 > getSigmaR() const
double getAvrgpz() const
ippl::Vector< double, 3 > getCutoffR() const
Vector_t< double, 3 > sigmaP_m
Definition Gaussian.h:121
Vector_t< double, 3 > sigmaR_m
Standard deviations for position and momentum distributions.
Definition Gaussian.h:120
void initRandomPool()
Initializes the random number generator pool.
Definition Gaussian.cpp:42
void setCutoffR(const Vector_t< double, 3 > &cutoffR)
Definition Gaussian.h:91
double avrgpz_m
Average momentum in the z-direction.
Definition Gaussian.h:126
void setFixMeanR(bool fixMeanR)
Definition Gaussian.h:102
bool fixMeanR_m
Flag to exactly fix the mean position of particles after sampling.
Definition Gaussian.h:136
void setSigmaR(const Vector_t< double, 3 > &sigmaR)
Definition Gaussian.h:85
void generateParticles(size_t &numberOfParticles, Vector_t< double, 3 > nr) override
Generates particles with a Gaussian distribution.
Definition Gaussian.cpp:64
Vector_t< double, 3 > cutoffR_m
Cutoff multiplier for position distribution.
Definition Gaussian.h:131
GeneratorPool randPool_m
Pool of random number generators for parallel sampling.
Definition Gaussian.h:115
Gaussian(std::shared_ptr< ParticleContainer_t > pc, std::shared_ptr< FieldContainer_t > fc, Distribution_t *opalDist)
Constructor for the Gaussian sampler.
Definition Gaussian.cpp:15
void emitParticles(double t, double dt) override
Time-stepped emission hook for one-shot delayed injection.
Definition Gaussian.cpp:187
void setAvrgpz(double avrgpz)
Definition Gaussian.h:89
void setSigmaP(const Vector_t< double, 3 > &sigmaP)
Definition Gaussian.h:87
IpplTimings::TimerRef samperTimer_m
Timer for performance profiling.
Definition Gaussian.h:43
Vector_t< double, 3 > P0_m
Vector_t< double, 3 > R0_m
Emission source offset: position R0, momentum P0, start time t0 (applied in sample step).
bool hasEmittedOnce_m
For one-shot emitters (e.g. Gaussian at delayed t0): guard to avoid double sampling.
size_t computeLocalEmitCount(size_t totalToSample) const
Computes the number of particles this rank should emit so that the global total equals totalToSample ...
std::string emissionModel_m
Distribution_t * opalDist_m
void fillPolarization(size_t startIdx, size_t count)
std::shared_ptr< ParticleContainer_t > pc_m
int seed
The current random seed.
Definition Options.cpp:37