OPALX (Object Oriented Parallel Accelerator Library for Exascal) master (dc2a29eed580)
OPALX
Loading...
Searching...
No Matches
TestDistributionMoments.cpp
Go to the documentation of this file.
1#include <gtest/gtest.h>
2
3#include <cmath>
4#include <vector>
5
7#include "Ippl.h"
9#include "Physics/Physics.h"
10#include "Physics/Units.h"
11
12namespace {
13 using Vec3 = Vector_t<double, 3>;
14
15 struct ExpectedMoments {
16 Vec3 meanR = 0.0;
17 Vec3 meanP = 0.0;
18 Vec3 stdR = 0.0;
19 Vec3 stdP = 0.0;
20 Vec3 covRP = 0.0;
21 Vec3 epsN = 0.0;
22 Vec3 epsGeo = 0.0;
23 double meanGamma{0.0};
24 double meanGammaZ{0.0};
25 double meanEkinMeV{0.0};
26 double stdEkinMeV_likeImplementation{0.0}; // matches current code path (no /Np)
27 double Dx{0.0}, DDx{0.0}, Dy{0.0}, DDy{0.0};
28 };
29
30 static ExpectedMoments computeExpected(
31 const std::vector<Vec3>& R, const std::vector<Vec3>& P, double massGeV) {
32 ExpectedMoments e;
33 const size_t N = R.size();
34 if (N == 0) {
35 return e;
36 }
37
38 // Means
39 for (size_t i = 0; i < N; ++i) {
40 for (int d = 0; d < 3; ++d) {
41 e.meanR(d) += R[i](d);
42 e.meanP(d) += P[i](d);
43 }
44 const double p2 = P[i](0) * P[i](0) + P[i](1) * P[i](1) + P[i](2) * P[i](2);
45 const double gamma = std::sqrt(p2 + 1.0);
46 e.meanGamma += gamma;
47 e.meanEkinMeV += (gamma - 1.0) * massGeV * Units::GeV2MeV;
48 e.meanGammaZ += P[i](2);
49 }
50 for (int d = 0; d < 3; ++d) {
51 e.meanR(d) /= static_cast<double>(N);
52 e.meanP(d) /= static_cast<double>(N);
53 }
54 e.meanGamma /= static_cast<double>(N);
55 e.meanEkinMeV /= static_cast<double>(N);
56 e.meanGammaZ /= static_cast<double>(N);
57 e.meanGammaZ = std::sqrt(e.meanGammaZ * e.meanGammaZ + 1.0);
58
59 // Central covariance of [x,px,y,py,z,pz]
60 double cov[6][6] = {};
61 double ncent[6][6] = {};
62 for (size_t i = 0; i < N; ++i) {
63 const double part[6] = {
64 R[i](0) - e.meanR(0), P[i](0) - e.meanP(0), R[i](1) - e.meanR(1),
65 P[i](1) - e.meanP(1), R[i](2) - e.meanR(2), P[i](2) - e.meanP(2),
66 };
67 const double part_nc[6] = {
68 R[i](0), P[i](0), R[i](1), P[i](1), R[i](2), P[i](2),
69 };
70 for (int a = 0; a < 6; ++a) {
71 for (int b = 0; b < 6; ++b) {
72 cov[a][b] += part[a] * part[b];
73 ncent[a][b] += part_nc[a] * part_nc[b];
74 }
75 }
76
77 const double p2 = P[i](0) * P[i](0) + P[i](1) * P[i](1) + P[i](2) * P[i](2);
78 const double gamma = std::sqrt(p2 + 1.0);
79 const double ekin = (gamma - 1.0) * massGeV * Units::GeV2MeV;
80 e.stdEkinMeV_likeImplementation += (ekin - e.meanEkinMeV) * (ekin - e.meanEkinMeV);
81 }
82 for (int a = 0; a < 6; ++a) {
83 for (int b = 0; b < 6; ++b) {
84 cov[a][b] /= static_cast<double>(N);
85 ncent[a][b] /= static_cast<double>(N);
86 }
87 }
88 e.stdEkinMeV_likeImplementation =
89 std::sqrt(e.stdEkinMeV_likeImplementation / static_cast<double>(N));
90
91 // stdR/stdP from central cov
92 for (int d = 0; d < 3; ++d) {
93 e.stdR(d) = std::sqrt(std::max(cov[2 * d][2 * d], 0.0));
94 e.stdP(d) = std::sqrt(std::max(cov[2 * d + 1][2 * d + 1], 0.0));
95 }
96
97 // cov(R,P) using the same formula as implementation:
98 // sumRP = notCentMoments(2d,2d+1) - meanR*meanP
99 for (int d = 0; d < 3; ++d) {
100 e.covRP(d) = ncent[2 * d][2 * d + 1] - e.meanR(d) * e.meanP(d);
101 const double squared = std::pow(e.stdR(d) * e.stdP(d), 2) - std::pow(e.covRP(d), 2);
102 e.epsN(d) = std::sqrt(std::max(squared, 0.0));
103 }
104 const double betaGamma = std::sqrt(std::max(e.meanGamma * e.meanGamma - 1.0, 0.0));
105 e.epsGeo = e.epsN / Vec3(betaGamma);
106
107 // Dispersion terms as exposed by getters (central moments against pz)
108 e.Dx = cov[0][5];
109 e.DDx = cov[1][5];
110 e.Dy = cov[2][5];
111 e.DDy = cov[3][5];
112
113 return e;
114 }
115} // namespace
116
117class DistributionMomentsTest : public ::testing::Test {
118protected:
119 static void SetUpTestSuite() {
120 int argc = 0;
121 char** argv = nullptr;
122 ippl::initialize(argc, argv);
123 }
124
125 static void TearDownTestSuite() { ippl::finalize(); }
126
127 void SetUp() override { bunchStateHandler = std::make_shared<BunchStateHandler>(); }
128
129 std::shared_ptr<BunchStateHandler> bunchStateHandler;
130
131 void TearDown() override { bunchStateHandler.reset(); }
132};
133
134TEST_F(DistributionMomentsTest, ComputeMoments_MeanStdEmittanceEnergyDispersion) {
135 // Deterministic small data set (units: R in m, P in beta*gamma, M in GeV/c^2).
136 const std::vector<Vec3> R = {
137 Vec3(1.0e-3, 2.0e-3, -1.0e-3), Vec3(-2.0e-3, 0.5e-3, 3.0e-3),
138 Vec3(0.0e-3, -1.5e-3, 2.0e-3), Vec3(1.5e-3, -0.5e-3, -2.5e-3),
139 Vec3(-0.5e-3, 1.0e-3, 0.0e-3),
140 };
141 const std::vector<Vec3> P = {
142 Vec3(0.02, -0.01, 0.10), Vec3(-0.03, 0.02, 0.08), Vec3(0.01, 0.01, 0.11),
143 Vec3(0.00, -0.02, 0.09), Vec3(0.04, 0.00, 0.105),
144 };
145 ASSERT_EQ(R.size(), P.size());
146 const size_t N = R.size();
147
148 // Build device views that match DistributionMoments signatures.
149 Kokkos::View<Vec3*> Rview_d("Rview_d", N);
150 Kokkos::View<Vec3*> Pview_d("Pview_d", N);
151 Kokkos::View<double*> Mview_d("Mview_d", 1); // scalar mass mode
152
153 auto Rview_h = Kokkos::create_mirror_view(Rview_d);
154 auto Pview_h = Kokkos::create_mirror_view(Pview_d);
155 auto Mview_h = Kokkos::create_mirror_view(Mview_d);
156
157 for (size_t i = 0; i < N; ++i) {
158 Rview_h(i) = R[i];
159 Pview_h(i) = P[i];
160 }
161 Mview_h(0) = Physics::m_e;
162
163 Kokkos::deep_copy(Rview_d, Rview_h);
164 Kokkos::deep_copy(Pview_d, Pview_h);
165 Kokkos::deep_copy(Mview_d, Mview_h);
166
168 auto containerState = bunchStateHandler->registerContainer();
169 dm.setContainerState(containerState);
170 dm.computeMoments(Rview_d, Pview_d, Mview_d, /*Np=*/N, /*Nlocal=*/N);
171
172 const auto exp = computeExpected(R, P, Physics::m_e);
173
174 const Vec3 meanR = dm.getMeanPosition();
175 const Vec3 meanP = dm.getMeanMomentum();
176 const Vec3 stdR = dm.getStandardDeviationPosition();
177 const Vec3 stdP = dm.getStandardDeviationMomentum();
178 const Vec3 epsN = dm.getNormalizedEmittance();
179 const Vec3 epsG = dm.getGeometricEmittance();
180
181 for (int d = 0; d < 3; ++d) {
182 EXPECT_NEAR(meanR(d), exp.meanR(d), 1e-15);
183 EXPECT_NEAR(meanP(d), exp.meanP(d), 1e-15);
184
185 EXPECT_NEAR(stdR(d), exp.stdR(d), 1e-15);
186 EXPECT_NEAR(stdP(d), exp.stdP(d), 1e-15);
187
188 EXPECT_NEAR(epsN(d), exp.epsN(d), 1e-15);
189 EXPECT_NEAR(epsG(d), exp.epsGeo(d), 1e-15);
190 }
191
192 EXPECT_NEAR(dm.getMeanGamma(), exp.meanGamma, 1e-15);
193 EXPECT_NEAR(dm.getMeanGammaZ(), exp.meanGammaZ, 1e-15);
194 EXPECT_NEAR(dm.getMeanKineticEnergy(), exp.meanEkinMeV, 1e-15);
195 EXPECT_NEAR(dm.getStdKineticEnergy(), exp.stdEkinMeV_likeImplementation, 1e-15);
196
197 EXPECT_NEAR(dm.getDx(), exp.Dx, 1e-15);
198 EXPECT_NEAR(dm.getDDx(), exp.DDx, 1e-15);
199 EXPECT_NEAR(dm.getDy(), exp.Dy, 1e-15);
200 EXPECT_NEAR(dm.getDDy(), exp.DDy, 1e-15);
201}
ippl::Vector< T, Dim > Vector_t
TEST_F(DistributionMomentsTest, ComputeMoments_MeanStdEmittanceEnergyDispersion)
std::shared_ptr< BunchStateHandler > bunchStateHandler
double getMeanKineticEnergy() const
Vector_t< double, 3 > getMeanMomentum() const
Vector_t< double, 3 > getStandardDeviationPosition() const
Vector_t< double, 3 > getNormalizedEmittance() const
void computeMoments(ippl::ParticleAttrib< Vector_t< double, 3 > >::view_type Rview, ippl::ParticleAttrib< Vector_t< double, 3 > >::view_type Pview, ippl::ParticleAttrib< double >::view_type Mview, size_t Np, size_t Nlocal)
double getStdKineticEnergy() const
Vector_t< double, 3 > getStandardDeviationMomentum() const
Vector_t< double, 3 > getMeanPosition() const
Vector_t< double, 3 > getGeometricEmittance() const
void setContainerState(std::weak_ptr< BunchStateHandler::ContainerState > containerState)
constexpr double m_e
The electron rest mass in GeV.
Definition Physics.h:93
constexpr double e
The value of.
Definition Physics.h:49
constexpr double GeV2MeV
Definition Units.h:80