OPALX (Object Oriented Parallel Accelerator Library for Exascal) master (dc2a29eed580)
OPALX
Loading...
Searching...
No Matches
TestGaussian.cpp
Go to the documentation of this file.
1#include <gtest/gtest.h>
2#include <mpi.h>
3#include <algorithm>
4#include <cmath>
5#include <memory>
6
8#include "Ippl.h"
10#include "Utility/IpplTimings.h"
11
12class GaussianTest : public ::testing::Test {
13protected:
14 static void SetUpTestSuite() {
15 int argc = 0;
16 char** argv = nullptr;
17
18 ippl::initialize(argc, argv);
19 }
20
21 static void TearDownTestSuite() { ippl::finalize(); }
22
23 void SetUp() override {
24 // Minimal 3D grid parameters
25 nr = 32;
26 ippl::Vector<double, 3> rmin = -4.0;
27 ippl::Vector<double, 3> rmax = 4.0;
28 ippl::Vector<double, 3> origin = rmin;
29 ippl::Vector<double, 3> hr = (rmax - rmin) / ippl::Vector<double, 3>(nr);
30 std::array<bool, 3> decomp = {true, true, true};
31
32 ippl::NDIndex<3> domain;
33 for (unsigned i = 0; i < 3; i++) {
34 domain[i] = ippl::Index(this->nr[i]);
35 }
36
37 // Create FieldContainer
38 auto fc = std::make_shared<FieldContainer_t>(
39 hr, rmin, rmax, decomp, domain, origin, this->isAllPeriodic_m);
40
41 // Create mesh and fieldlayout
42 Mesh_t<3> mesh(domain, hr, origin);
43 FieldLayout_t<3> fl(MPI_COMM_WORLD, domain, decomp, this->isAllPeriodic_m);
44
45 // Create ParticleContainer
46 pc = std::make_shared<ParticleContainer<double, 3>>(mesh, fl);
47
48 bunchStateHandler = std::make_shared<BunchStateHandler>();
49 pc->setBunchStateHandler(bunchStateHandler);
50 }
51
52 void TearDown() override {
53 // nothing special
54 }
55
56 std::shared_ptr<ParticleContainer<double, 3>> pc;
57 std::shared_ptr<BunchStateHandler> bunchStateHandler;
58 ippl::Vector<int, 3> nr;
59 bool isAllPeriodic_m = true;
60};
61
62template <typename ViewType>
63void computeMean(ViewType& view, size_t nlocal, size_t total_nparticles, double meanR[3]) {
64 double sumR[3] = {0.0, 0.0, 0.0};
65
66 Kokkos::parallel_reduce(
67 nlocal,
68 KOKKOS_LAMBDA(const int k, double& cent0, double& cent1, double& cent2) {
69 cent0 += view(k)[0];
70 cent1 += view(k)[1];
71 cent2 += view(k)[2];
72 },
73 Kokkos::Sum<double>(sumR[0]), Kokkos::Sum<double>(sumR[1]),
74 Kokkos::Sum<double>(sumR[2]));
75 Kokkos::fence();
76
77 MPI_Allreduce(sumR, meanR, 3, MPI_DOUBLE, MPI_SUM, ippl::Comm->getCommunicator());
78 ippl::Comm->barrier();
79
80 for (int i = 0; i < 3; i++)
81 meanR[i] /= total_nparticles;
82}
83
84template <typename ViewType>
86 ViewType& view, size_t nlocal, size_t total_nparticles, const double meanR[3],
87 double stddevR[3]) {
88 double sumR2[3] = {0.0, 0.0, 0.0};
89
90 // Compute sum of squares locally
91 Kokkos::parallel_reduce(
92 nlocal,
93 KOKKOS_LAMBDA(const int k, double& s0, double& s1, double& s2) {
94 s0 += view(k)[0] * view(k)[0];
95 s1 += view(k)[1] * view(k)[1];
96 s2 += view(k)[2] * view(k)[2];
97 },
98 Kokkos::Sum<double>(sumR2[0]), Kokkos::Sum<double>(sumR2[1]),
99 Kokkos::Sum<double>(sumR2[2]));
100 Kokkos::fence();
101
102 // MPI reduce to get global sums
103 double global_sumR2[3] = {0.0, 0.0, 0.0};
104 MPI_Allreduce(sumR2, global_sumR2, 3, MPI_DOUBLE, MPI_SUM, ippl::Comm->getCommunicator());
105 ippl::Comm->barrier();
106
107 // Compute variance and stddev
108 for (int i = 0; i < 3; i++) {
109 double mean_square = global_sumR2[i] / static_cast<double>(total_nparticles);
110 double variance = mean_square - meanR[i] * meanR[i];
111 stddevR[i] = std::sqrt(variance);
112 }
113}
114
115template <typename ViewType>
117 ViewType& view, size_t nlocal, double global_maxAbsR[3], double& global_maxRadius) {
118 // Local maxima initialized
119 double local_maxAbsR[3] = {0.0, 0.0, 0.0};
120 double local_maxRadius = 0.0;
121
122 // Compute local maxima using Kokkos
123 Kokkos::parallel_reduce(
124 nlocal,
125 KOKKOS_LAMBDA(const int k, double& max0, double& max1, double& max2, double& maxr) {
126 double x = view(k)[0];
127 double y = view(k)[1];
128 double z = view(k)[2];
129
130 double ax = Kokkos::fabs(x);
131 double ay = Kokkos::fabs(y);
132 double az = Kokkos::fabs(z);
133 double r = Kokkos::sqrt(x * x + y * y + z * z);
134
135 if (ax > max0) max0 = ax;
136 if (ay > max1) max1 = ay;
137 if (az > max2) max2 = az;
138 if (r > maxr) maxr = r;
139 },
140 Kokkos::Max<double>(local_maxAbsR[0]), Kokkos::Max<double>(local_maxAbsR[1]),
141 Kokkos::Max<double>(local_maxAbsR[2]), Kokkos::Max<double>(local_maxRadius));
142 Kokkos::fence();
143
144 // MPI reduction to get global maxima
145 MPI_Allreduce(
146 local_maxAbsR, global_maxAbsR, 3, MPI_DOUBLE, MPI_MAX, ippl::Comm->getCommunicator());
147 MPI_Allreduce(
148 &local_maxRadius, &global_maxRadius, 1, MPI_DOUBLE, MPI_MAX,
149 ippl::Comm->getCommunicator());
150
151 ippl::Comm->barrier();
152}
153
156static void preallocateParticleCapacity(
157 const std::shared_ptr<ParticleContainer<double, 3>>& pc, size_t totalParticles) {
158 const int nranks = std::max(1, ippl::Comm->size());
159 const size_t nranksU = static_cast<size_t>(nranks);
160
161 // Mirror TrackRun: give each rank enough headroom above its ideal share.
162 const size_t maxLocalNum = totalParticles / nranksU + 2 * nranksU + 1;
163
164 pc->allocateParticles(maxLocalNum);
165}
166
167TEST_F(GaussianTest, meanR_stddevR) {
168 const Vector_t<double, 3> sigmaR_ref = 0.5;
169 const Vector_t<double, 3> sigmaP_ref = 1.0;
170 double avrgpz = 0.0;
171 const Vector_t<double, 3> cutoffR = 4.0;
172
173 Gaussian sampler(pc, sigmaR_ref, sigmaP_ref, avrgpz, cutoffR);
174
175 size_t total_nparticles = 100000;
176
177 preallocateParticleCapacity(pc, total_nparticles);
178 sampler.generateParticles(total_nparticles, nr);
179
180 auto Rview = pc->R.getView();
181 auto Pview = pc->P.getView();
182
183 // compute mean of R
184 double meanR[3] = {0.0, 0.0, 0.0};
185 computeMean(Rview, pc->getLocalNum(), total_nparticles, meanR);
186
187 EXPECT_NEAR(meanR[0], 0.0, 1e-15);
188 EXPECT_NEAR(meanR[1], 0.0, 1e-15);
189 EXPECT_NEAR(meanR[2], 0.0, 1e-15);
190
191 // compute variance of R
192 double sigmaR[3] = {0.0, 0.0, 0.0};
193 computeStdDev(Rview, pc->getLocalNum(), total_nparticles, meanR, sigmaR);
194
195 EXPECT_NEAR(sigmaR[0], sigmaR_ref[0], 1e-2);
196 EXPECT_NEAR(sigmaR[1], sigmaR_ref[1], 1e-2);
197 EXPECT_NEAR(sigmaR[2], sigmaR_ref[2], 1e-2);
198}
199
201 const Vector_t<double, 3> sigmaR = 1.0;
202 const Vector_t<double, 3> sigmaP = 1.0;
203 double avrgpz = 0.0;
204 const Vector_t<double, 3> cutoffR = 3.0;
205
206 bool fixMeanR = false;
207 Gaussian sampler(pc, sigmaR, sigmaP, avrgpz, cutoffR, fixMeanR);
208
209 size_t total_nparticles = 100000;
210
211 preallocateParticleCapacity(pc, total_nparticles);
212 sampler.generateParticles(total_nparticles, nr);
213
214 auto Rview = pc->R.getView();
215
216 // compute max of abs(R)
217 double global_maxAbsR[3] = {0.0, 0.0, 0.0};
218 double global_maxRadius = 0.0;
219 computeMaxAbsR(Rview, pc->getLocalNum(), global_maxAbsR, global_maxRadius);
220
221 // --------- Assertions ---------
222
223 // Each coordinate individually should be inside cutoff
224 EXPECT_LE(global_maxAbsR[0], cutoffR[0]);
225 EXPECT_LE(global_maxAbsR[1], cutoffR[1]);
226 EXPECT_LE(global_maxAbsR[2], cutoffR[2]);
227}
228
229TEST_F(GaussianTest, meanP_and_steddevP) {
230 const Vector_t<double, 3> sigmaR_ref = 1.0;
231 const Vector_t<double, 3> sigmaP_ref = 1.0;
232 double avrgpz = 0.1;
233 const Vector_t<double, 3> cutoffR = 4.0;
234
235 Gaussian sampler(pc, sigmaR_ref, sigmaP_ref, avrgpz, cutoffR);
236
237 size_t total_nparticles = 100000;
238 preallocateParticleCapacity(pc, total_nparticles);
239 sampler.generateParticles(total_nparticles, nr);
240
241 auto Pview = pc->P.getView();
242
243 //---------------------------------------------------------------------
244 // 1. Compute mean(P)
245 //---------------------------------------------------------------------
246 double meanP[3] = {0.0, 0.0, 0.0};
247
248 computeMean(Pview, pc->getLocalNum(), total_nparticles, meanP);
249
250 //---------------------------------------------------------------------
251 // 2. Compute stddev(P)
252 //---------------------------------------------------------------------
253 double sigmaP[3] = {0.0, 0.0, 0.0};
254 computeStdDev(Pview, pc->getLocalNum(), total_nparticles, meanP, sigmaP);
255
256 //---------------------------------------------------------------------
257 // 3. Expectations
258 //---------------------------------------------------------------------
259 EXPECT_NEAR(meanP[0], 0.0, 1e-2);
260 EXPECT_NEAR(meanP[1], 0.0, 1e-2);
261 EXPECT_NEAR(meanP[2], avrgpz, 1e-2);
262
263 EXPECT_NEAR(sigmaP[0], sigmaP_ref[0], 1e-2);
264 EXPECT_NEAR(sigmaP[1], sigmaP_ref[1], 1e-2);
265 EXPECT_NEAR(sigmaP[2], sigmaP_ref[2], 1e-2);
266}
ippl::Vector< T, Dim > Vector_t
const int nr
ippl::FieldLayout< Dim > FieldLayout_t
Definition PBunchDefs.h:25
ippl::UniformCartesian< double, 3 > Mesh_t
Definition PBunchDefs.h:18
void computeStdDev(ViewType &view, size_t nlocal, size_t total_nparticles, const double meanR[3], double stddevR[3])
void computeMean(ViewType &view, size_t nlocal, size_t total_nparticles, double meanR[3])
void computeMaxAbsR(ViewType &view, size_t nlocal, double global_maxAbsR[3], double &global_maxRadius)
TEST_F(GaussianTest, meanR_stddevR)
void SetUp() override
std::shared_ptr< BunchStateHandler > bunchStateHandler
static void SetUpTestSuite()
ippl::Vector< int, 3 > nr
static void TearDownTestSuite()
void TearDown() override
std::shared_ptr< ParticleContainer< double, 3 > > pc
Generating particles following a Gaussian distribution.
Definition Gaussian.h:38
void generateParticles(size_t &numberOfParticles, Vector_t< double, 3 > nr) override
Generates particles with a Gaussian distribution.
Definition Gaussian.cpp:64
Container for all per-particle (and per-simulation) fields tracked during OPALX tracking.