OPALX (Object Oriented Parallel Accelerator Library for Exascal) master (dc2a29eed580)
OPALX
Loading...
Searching...
No Matches
TestSpinTBMTPusher.cpp
Go to the documentation of this file.
1
60#include <gtest/gtest.h>
61
62#include "Physics/Physics.h"
64#include "Utility/Inform.h"
65
66#include <cmath>
67
68extern Inform* gmsg;
69
70class SpinTBMTPusherTest : public ::testing::Test {
71protected:
72 static void SetUpTestSuite() {
73 int argc = 0;
74 char** argv = nullptr;
75 ippl::initialize(argc, argv);
76 if (gmsg == nullptr) {
77 gmsg = new Inform(nullptr, -1);
78 }
79 }
80 static void TearDownTestSuite() {
81 if (gmsg != nullptr) {
82 delete gmsg;
83 gmsg = nullptr;
84 }
85 ippl::finalize();
86 }
87};
88
89namespace {
90
91 // Muon: rest mass in eV, charge in proton charges, anomaly G = a_mu.
92 constexpr double mass_mu_eV = Physics::m_mu * 1.0e9;
93 constexpr double charge_mu = -1.0; // mu-
94 constexpr double anom_mu = Physics::a_mu;
95
96 // Pure cyclotron angular frequency for muon in a uniform B (omega_c, no anomaly).
97 // omega_c = q * c^2 * B / (gamma * mass), with mass in eV in OPALX conventions.
98 double cyclotronFreq(double B, double gamma) {
99 return std::fabs(charge_mu) * Physics::c * Physics::c * B / (gamma * mass_mu_eV);
100 }
101
102} // namespace
103
104TEST_F(SpinTBMTPusherTest, PrecessesAtCorrectFrequencyInStaticBz) {
105 // Setup: muon at rest (gamma=1, P=0). T-BMT reduces to Larmor precession
106 // omega_S = (q/m) (G + 1) B = (1 + a_mu) * omega_c
107 // We compare against (1 + a_mu) * omega_c. Pol starts along x, precesses in xy plane.
108 const Vector_t<double, 3> P{0.0, 0.0, 0.0};
109 const Vector_t<double, 3> E{0.0, 0.0, 0.0};
110 const Vector_t<double, 3> B{0.0, 0.0, 1.0}; // 1 T
111 const double dt = 1.0e-12; // 1 ps
112
113 ippl::Vector<float, 3> Pol{1.0f, 0.0f, 0.0f};
114
115 SpinTBMTPusher pusher;
116 pusher.evolve(Pol, P, E, B, dt, mass_mu_eV, charge_mu, anom_mu);
117
118 const double omegaC = cyclotronFreq(1.0, 1.0);
119 const double expectedPhi = (1.0 + anom_mu) * omegaC * dt;
120 // For q<0 the rotation is in the +Bf-aligned axis sense per our prefactor sign convention.
121 // We just check magnitude of the rotation angle here.
122 const double cosX = static_cast<double>(Pol[0]);
123 const double sinY = static_cast<double>(Pol[1]);
124 const double actualPhi = std::atan2(std::fabs(sinY), cosX);
125 EXPECT_NEAR(actualPhi, expectedPhi, 1.0e-9);
126
127 // |Pol| must remain 1.
128 const double mag2 = static_cast<double>(Pol[0]) * Pol[0] + static_cast<double>(Pol[1]) * Pol[1]
129 + static_cast<double>(Pol[2]) * Pol[2];
130 EXPECT_NEAR(std::sqrt(mag2), 1.0, 1.0e-6);
131
132 // z-component must remain 0 (rotation is about z).
133 EXPECT_NEAR(static_cast<double>(Pol[2]), 0.0, 1.0e-7);
134}
135
136TEST_F(SpinTBMTPusherTest, NoEvolutionWhenPolParallelToBAndNoE) {
137 // B along z, Pol along z, no E -> Omega has zero cross-product effect on Pol along its axis.
138 const Vector_t<double, 3> P{0.0, 0.0, 0.0};
139 const Vector_t<double, 3> E{0.0, 0.0, 0.0};
140 const Vector_t<double, 3> B{0.0, 0.0, 1.0};
141 const double dt = 1.0e-9;
142
143 ippl::Vector<float, 3> Pol{0.0f, 0.0f, 1.0f};
144
145 SpinTBMTPusher pusher;
146 pusher.evolve(Pol, P, E, B, dt, mass_mu_eV, charge_mu, anom_mu);
147
148 EXPECT_NEAR(static_cast<double>(Pol[0]), 0.0, 1.0e-12);
149 EXPECT_NEAR(static_cast<double>(Pol[1]), 0.0, 1.0e-12);
150 EXPECT_NEAR(static_cast<double>(Pol[2]), 1.0, 1.0e-7);
151}
152
153TEST_F(SpinTBMTPusherTest, NoEvolutionWhenFieldsAreZero) {
154 const Vector_t<double, 3> P{0.0, 0.0, 1.0};
155 const Vector_t<double, 3> E{0.0, 0.0, 0.0};
156 const Vector_t<double, 3> B{0.0, 0.0, 0.0};
157 const double dt = 1.0e-9;
158
159 ippl::Vector<float, 3> Pol{1.0f, 0.0f, 0.0f};
160
161 SpinTBMTPusher pusher;
162 pusher.evolve(Pol, P, E, B, dt, mass_mu_eV, charge_mu, anom_mu);
163
164 EXPECT_FLOAT_EQ(Pol[0], 1.0f);
165 EXPECT_FLOAT_EQ(Pol[1], 0.0f);
166 EXPECT_FLOAT_EQ(Pol[2], 0.0f);
167}
168
169TEST_F(SpinTBMTPusherTest, PreservesMagnitudeOverManySteps) {
170 const Vector_t<double, 3> P{0.0, 0.0, 0.0};
171 const Vector_t<double, 3> E{0.0, 0.0, 0.0};
172 const Vector_t<double, 3> B{0.0, 0.0, 1.0};
173 const double dt = 1.0e-12;
174
175 ippl::Vector<float, 3> Pol{1.0f, 0.0f, 0.0f};
176 SpinTBMTPusher pusher;
177 for (int i = 0; i < 1000; ++i) {
178 pusher.evolve(Pol, P, E, B, dt, mass_mu_eV, charge_mu, anom_mu);
179 }
180 const double mag2 = static_cast<double>(Pol[0]) * Pol[0] + static_cast<double>(Pol[1]) * Pol[1]
181 + static_cast<double>(Pol[2]) * Pol[2];
182 EXPECT_NEAR(std::sqrt(mag2), 1.0, 1.0e-5);
183}
ippl::Vector< T, Dim > Vector_t
TEST_F(SpinTBMTPusherTest, PrecessesAtCorrectFrequencyInStaticBz)
Inform * gmsg
Definition changes.cpp:7
static void TearDownTestSuite()
KOKKOS_INLINE_FUNCTION void evolve(SpinVec &Pol, const Vector_t< double, 3 > &P, const Vector_t< double, 3 > &Ef, const Vector_t< double, 3 > &Bf, const double &dt, const double &mass, const double &charge, const double &anom) const
constexpr double a_mu
The magnetic moment anomaly for muons, no dimension (PDG, (g-2)/2)
Definition Physics.h:135
constexpr double m_mu
The muon rest mass in GeV.
Definition Physics.h:129
constexpr double c
The velocity of light in m/s.
Definition Physics.h:60