75int main(
int argc,
char** argv) {
76 MPI_Init(&argc, &argv);
77 int rank = 0, size = 1;
78 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
79 MPI_Comm_size(MPI_COMM_WORLD, &size);
80 const std::int64_t N = 400;
82 std::vector<Vec<3>> p0(N), v0(N);
83 for (std::int64_t i = 0; i < N; ++i)
84 for (
int a = 0; a < 3; ++a) {
86 v0[i][a] = (
frac(i, 10 + a) - 0.5) * 0.2;
90 std::vector<Vec<3>> sp = p0, sv = v0;
91 for (
int step = 0; step <
kSteps; ++step) {
93 for (std::int64_t i = 0; i < N; ++i)
94 for (std::int64_t j = 0; j < N; ++j)
99 for (std::int64_t i = 0; i < N; ++i)
100 for (
int a = 0; a < 3; ++a) {
101 sv[i][a] = (sv[i][a] + F[i].v[a] *
kDt) *
kDamp;
102 sp[i][a] += sv[i][a] *
kDt;
103 sp[i][a] =
dmin[a] + std::fmod(std::fmod(sp[i][a] -
dmin[a],
Ld[a]) +
Ld[a],
Ld[a]);
108 IVec<3> gsize{40, 32, 24};
109 BlockDecomposer<3> dec(
static_cast<std::size_t
>(size), gsize);
111 for (
int a = 0; a < 3; ++a) {
112 map.origin[a] =
dmin[a];
113 map.cellSize[a] =
Ld[a] / gsize[a];
114 map.periodic[a] =
true;
116 ParticleMigrator<3> mig;
117 mig.init(dec, rank, map, MPI_COMM_WORLD);
118 ParticleHaloTopology<3> halo;
121 const std::size_t stride =
sizeof(
Pay);
122 std::vector<Vec<3>> pos;
123 std::vector<char> payload;
124 for (std::int64_t
id = rank;
id < N;
id += size) {
125 pos.push_back(p0[
id]);
127 for (
int a = 0; a < 3; ++a)
128 pay.
vel[a] = v0[
id][a];
130 std::size_t off = payload.size();
131 payload.resize(off + stride);
132 std::memcpy(&payload[off], &pay, stride);
135 for (
int step = 0; step <
kSteps; ++step) {
136 mig.migrate(pos, payload, stride);
137 std::size_t n = pos.size();
138 std::vector<std::int64_t> id(n);
139 std::vector<Vec<3>> vel(n);
140 for (std::size_t i = 0; i < n; ++i) {
142 std::memcpy(&p, &payload[i * stride], stride);
147 halo.build(pos,
kRcut);
148 std::size_t G = halo.numGhost();
149 const auto& gpos = halo.ghostPositions();
150 std::vector<double> ownIdD(n), ghIdD(G);
151 for (std::size_t i = 0; i < n; ++i)
152 ownIdD[i] = (
double)
id[i];
153 halo.forward(ownIdD.data(), ghIdD.data());
155 std::vector<F3> Fown(n), Fgh(G);
157 for (std::size_t i = 0; i < n; ++i)
158 for (std::size_t j = i + 1; j < n; ++j) {
161 F3 mf{{-f.
v[0], -f.
v[1], -f.
v[2]}};
165 for (std::size_t i = 0; i < n; ++i)
166 for (std::size_t g = 0; g < G; ++g) {
167 if (
id[i] >= (std::int64_t)std::llround(ghIdD[g]))
171 F3 mf{{-f.
v[0], -f.
v[1], -f.
v[2]}};
175 halo.reverse(Fgh.data(), Fown.data());
177 for (std::size_t i = 0; i < n; ++i)
178 for (
int a = 0; a < 3; ++a) {
179 vel[i][a] = (vel[i][a] + Fown[i].v[a] *
kDt) *
kDamp;
180 pos[i][a] += vel[i][a] *
kDt;
181 pos[i][a] =
dmin[a] + std::fmod(std::fmod(pos[i][a] -
dmin[a],
Ld[a]) +
Ld[a],
Ld[a]);
183 for (std::size_t i = 0; i < n; ++i) {
186 for (
int a = 0; a < 3; ++a)
187 p.
vel[a] = vel[i][a];
188 std::memcpy(&payload[i * stride], &p, stride);
193 for (std::size_t i = 0; i < pos.size(); ++i) {
195 std::memcpy(&p, &payload[i * stride], stride);
196 for (
int a = 0; a < 3; ++a)
197 if (std::fabs(pos[i][a] - sp[p.
id][a]) > 1e-9 || std::fabs(p.
vel[a] - sv[p.
id][a]) > 1e-9)
201 MPI_Allreduce(&fail, &total, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
205 "OK (np=%d): scheme B (compute-once + reverse-force, integrate owned) matches serial\n",
208 std::fprintf(stderr,
"FAILED (np=%d): %d state mismatches\n", size, total);
211 return total == 0 ? 0 : 1;