OPALX (Object Oriented Parallel Accelerator Library for Exascal) master (dc2a29eed580)
OPALX
Loading...
Searching...
No Matches
TestBunchStateHandler.cpp
Go to the documentation of this file.
1#include <gtest/gtest.h>
2
3#include "Ippl.h"
5#include "Utilities/Options.h"
6
7class BunchStateHandlerTest : public ::testing::Test {
8protected:
9 static void SetUpTestSuite() {
10 int argc = 0;
11 char** argv = nullptr;
12 ippl::initialize(argc, argv);
13 }
14
15 static void TearDownTestSuite() { ippl::finalize(); }
16
17 // Each test starts with the sync flag off; tests that need it turn it on.
18 void SetUp() override { Options::aggressiveStateSync = false; }
19 void TearDown() override { Options::aggressiveStateSync = false; }
20};
21
22TEST_F(BunchStateHandlerTest, RegisterContainerReturnsFreshSlot) {
24 auto s = h.registerContainer();
25 ASSERT_TRUE(s);
26 EXPECT_FALSE(s->unitlessPositions);
27 EXPECT_TRUE(s->momentsDirty); // default is dirty so first compute runs
28}
29
30TEST_F(BunchStateHandlerTest, BunchWideFirstRepartitionDefault) {
32 EXPECT_TRUE(h.isFirstRepartition());
33}
34
35TEST_F(BunchStateHandlerTest, UnitlessPositionsToggle) {
37 auto s = h.registerContainer();
38
39 s->setUnitlessPositions(true);
40 EXPECT_TRUE(s->unitlessPositions);
41 s->setUnitlessPositions(false);
42 EXPECT_FALSE(s->unitlessPositions);
43}
44
45TEST_F(BunchStateHandlerTest, MomentsDirtyLifecycle) {
47 auto s = h.registerContainer();
48
49 // Starts dirty
50 EXPECT_TRUE(s->momentsDirty);
51
52 s->markMomentsClean();
53 EXPECT_FALSE(s->momentsDirty);
54
55 s->markMomentsDirty();
56 EXPECT_TRUE(s->momentsDirty);
57
58 s->markMomentsClean();
59 EXPECT_FALSE(s->momentsDirty);
60
61 // Multiple marks are idempotent
62 s->markMomentsDirty();
63 s->markMomentsDirty();
64 EXPECT_TRUE(s->momentsDirty);
65}
66
67TEST_F(BunchStateHandlerTest, FirstRepartition) {
69 EXPECT_TRUE(h.isFirstRepartition());
70
71 h.setFirstRepartition(false);
72 EXPECT_FALSE(h.isFirstRepartition());
73
74 h.setFirstRepartition(true);
75 EXPECT_TRUE(h.isFirstRepartition());
76}
77
78TEST_F(BunchStateHandlerTest, PerContainerSlotsAreIndependent) {
80 auto a = h.registerContainer();
81 auto b = h.registerContainer();
82 ASSERT_NE(a.get(), b.get());
83
84 a->markMomentsClean();
85 b->markMomentsClean();
86
87 // Marking only `a` dirty must not affect `b`.
88 a->markMomentsDirty();
89 EXPECT_TRUE(a->momentsDirty);
90 EXPECT_FALSE(b->momentsDirty);
91
92 // And vice-versa.
93 a->markMomentsClean();
94 b->markMomentsDirty();
95 EXPECT_FALSE(a->momentsDirty);
96 EXPECT_TRUE(b->momentsDirty);
97}
98
99TEST_F(BunchStateHandlerTest, SlotLifetimeFollowsCallerOwnership) {
101
102 // Keep a weak_ptr to observe when the slot dies.
103 std::weak_ptr<BunchStateHandler::ContainerState> observer;
104 {
105 auto s = h.registerContainer();
106 observer = s;
107 EXPECT_FALSE(observer.expired());
108 }
109 // Last strong ref released: slot is destroyed, handler's weak_ptr expires.
110 EXPECT_TRUE(observer.expired());
111}
112
113// -----------------------------------------------------------------------------
114// Aggressive-sync tests: require >1 MPI rank to actually exercise divergence.
115// With a single rank the allreduce is a no-op and proves nothing, so skip.
116// -----------------------------------------------------------------------------
117
118TEST_F(BunchStateHandlerTest, AggressiveSync_MomentsDirty_OrAcrossRanks) {
119 if (ippl::Comm->size() < 2) {
120 GTEST_SKIP() << "Requires at least 2 MPI ranks to exercise divergent setter calls.";
121 }
122
125 auto s = h.registerContainer();
126 s->markMomentsClean(); // sync'd clear: all ranks agree false -> false
127 ASSERT_FALSE(s->momentsDirty);
128
129 // Only rank 0 marks dirty; logical_or should propagate to every rank.
130 if (ippl::Comm->rank() == 0) {
131 s->markMomentsDirty();
132 } else {
133 // Non-zero ranks must also call a setter so they participate in the
134 // collective. Clearing locally; the OR with rank 0's "true" wins.
135 s->markMomentsClean();
136 }
137 EXPECT_TRUE(s->momentsDirty);
138}
139
140TEST_F(BunchStateHandlerTest, AggressiveSync_ClearOnlyWhenAllAgree) {
141 if (ippl::Comm->size() < 2) {
142 GTEST_SKIP() << "Requires at least 2 MPI ranks to exercise divergent setter calls.";
143 }
144
147 auto s = h.registerContainer();
148 s->markMomentsDirty();
149 ASSERT_TRUE(s->momentsDirty);
150
151 // rank 0 -> markMomentsClean passes `false`
152 // rank i -> markMomentsDirty passes `true`
153 // => OR == true => every rank stays dirty, including rank 0.
154 if (ippl::Comm->rank() == 0) {
155 s->markMomentsClean();
156 } else {
157 s->markMomentsDirty();
158 }
159 EXPECT_TRUE(s->momentsDirty);
160}
161
162TEST_F(BunchStateHandlerTest, AggressiveSync_UnitlessPositionsConverge) {
163 if (ippl::Comm->size() < 2) {
164 GTEST_SKIP() << "Requires at least 2 MPI ranks to exercise divergent setter calls.";
165 }
166
169 auto s = h.registerContainer();
170 EXPECT_FALSE(s->unitlessPositions);
171
172 // Only last rank flips to unitless; all ranks should converge to true.
173 const bool isLast = (ippl::Comm->rank() == ippl::Comm->size() - 1);
174 s->setUnitlessPositions(isLast);
175 EXPECT_TRUE(s->unitlessPositions);
176}
177
178TEST_F(BunchStateHandlerTest, AggressiveSync_FirstRepartitionConverge) {
179 if (ippl::Comm->size() < 2) {
180 GTEST_SKIP() << "Requires at least 2 MPI ranks to exercise divergent setter calls.";
181 }
182
185 ASSERT_TRUE(h.isFirstRepartition());
186
187 // Every rank except rank 0 tries to mark first-repartition done; rank 0
188 // still reports true. OR wins => first-repartition remains true everywhere
189 // (the conservative choice: do not skip first-time init until all ranks
190 // agree it is done).
191 h.setFirstRepartition(ippl::Comm->rank() != 0 ? false : true);
192 EXPECT_TRUE(h.isFirstRepartition());
193}
194
195TEST_F(BunchStateHandlerTest, AggressiveSync_OffDoesNotSynchronize) {
196 if (ippl::Comm->size() < 2) {
197 GTEST_SKIP() << "Requires at least 2 MPI ranks to distinguish synced from unsynced.";
198 }
199
202 auto s = h.registerContainer();
203 s->markMomentsClean();
204 ASSERT_FALSE(s->momentsDirty);
205
206 // Only rank 0 marks dirty; with sync off, other ranks must stay clean.
207 if (ippl::Comm->rank() == 0) {
208 s->markMomentsDirty();
209 EXPECT_TRUE(s->momentsDirty);
210 } else {
211 EXPECT_FALSE(s->momentsDirty);
212 }
213}
TEST_F(BunchStateHandlerTest, RegisterContainerReturnsFreshSlot)
Centralised tracking of mutable bunch-level and per-container status flags.
bool isFirstRepartition() const
void setFirstRepartition(bool v)
std::shared_ptr< ContainerState > registerContainer()
Allocate a new per-container slot and return a strong reference to it. The handler itself retains onl...
bool aggressiveStateSync
Definition Options.cpp:111