74int main(
int argc,
char** argv) {
75 MPI_Init(&argc, &argv);
76 int rank = 0, size = 1;
77 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
78 MPI_Comm_size(MPI_COMM_WORLD, &size);
79 const std::int64_t N = 400;
81 std::vector<Vec<3>> p0(N), v0(N);
82 for (std::int64_t i = 0; i < N; ++i)
83 for (
int a = 0; a < 3; ++a) {
85 v0[i][a] = (
frac(i, 10 + a) - 0.5) * 0.2;
89 std::vector<Vec<3>> sp = p0, sv = v0;
90 for (
int step = 0; step <
kSteps; ++step) {
92 for (std::int64_t i = 0; i < N; ++i)
93 for (std::int64_t j = 0; j < N; ++j)
95 F[i] +=
pf(sp[i], sp[j]);
96 for (std::int64_t i = 0; i < N; ++i)
97 for (
int a = 0; a < 3; ++a) {
98 sv[i][a] = (sv[i][a] + F[i].v[a] *
kDt) *
kDamp;
99 sp[i][a] += sv[i][a] *
kDt;
104 IVec<3> gsize{40, 32, 24};
105 BlockDecomposer<3> dec(
static_cast<std::size_t
>(size), gsize);
107 for (
int a = 0; a < 3; ++a) {
108 map.origin[a] =
dmin[a];
109 map.cellSize[a] =
Ld[a] / gsize[a];
110 map.periodic[a] =
true;
112 ParticleMigrator<3> mig;
113 mig.init(dec, rank, map, MPI_COMM_WORLD);
114 ParticleHaloTopology<3> halo;
117 const std::size_t stride =
sizeof(
Pay);
118 std::vector<Vec<3>> pos;
119 std::vector<char> payload;
120 for (std::int64_t
id = rank;
id < N;
id += size) {
121 pos.push_back(p0[
id]);
123 for (
int a = 0; a < 3; ++a)
124 p.
vel[a] = v0[
id][a];
126 std::size_t off = payload.size();
127 payload.resize(off + stride);
128 std::memcpy(&payload[off], &p, stride);
130 mig.migrate(pos, payload, stride);
131 std::size_t n = pos.size();
132 std::vector<std::int64_t> id(n);
133 std::vector<Vec<3>> vel(n);
134 for (std::size_t i = 0; i < n; ++i) {
136 std::memcpy(&p, &payload[i * stride], stride);
143 std::size_t G = halo.numGhost();
144 std::vector<Vec<3>> gpos = halo.ghostPositions();
145 std::vector<Vec<3>> gvel(G);
146 std::vector<double> ownIdD(n), ghIdD(G);
147 for (std::size_t i = 0; i < n; ++i)
148 ownIdD[i] = (
double)
id[i];
149 halo.forward(ownIdD.data(), ghIdD.data());
150 halo.forward(vel.data(), gvel.data());
152 for (
int step = 0; step <
kSteps; ++step) {
153 std::vector<F3> Fown(n), Fgh(G);
154 for (std::size_t i = 0; i < n; ++i)
155 for (std::size_t j = i + 1; j < n; ++j) {
156 F3 f =
pf(pos[i], pos[j]);
158 Fown[j] +=
F3{{-f.
v[0], -f.
v[1], -f.
v[2]}};
160 for (std::size_t i = 0; i < n; ++i)
161 for (std::size_t g = 0; g < G; ++g) {
162 if (
id[i] >= (std::int64_t)std::llround(ghIdD[g]))
164 F3 f =
pf(pos[i], gpos[g]);
166 Fgh[g] +=
F3{{-f.
v[0], -f.
v[1], -f.
v[2]}};
168 halo.reverse(Fgh.data(), Fown.data());
169 std::vector<F3> FghTot(G);
170 halo.forward(Fown.data(), FghTot.data());
172 for (std::size_t i = 0; i < n; ++i)
173 for (
int a = 0; a < 3; ++a) {
174 vel[i][a] = (vel[i][a] + Fown[i].v[a] *
kDt) *
kDamp;
175 pos[i][a] += vel[i][a] *
kDt;
177 for (std::size_t g = 0; g < G; ++g)
178 for (
int a = 0; a < 3; ++a) {
179 gvel[g][a] = (gvel[g][a] + FghTot[g].v[a] *
kDt) *
kDamp;
180 gpos[g][a] += gvel[g][a] *
kDt;
185 for (std::size_t i = 0; i < n; ++i)
186 for (
int a = 0; a < 3; ++a)
187 if (std::fabs(pos[i][a] - sp[
id[i]][a]) > 1e-9 || std::fabs(vel[i][a] - sv[
id[i]][a]) > 1e-9)
190 std::vector<Vec<3>> fresh(G);
191 halo.forwardPositions(pos.data(), fresh.data());
192 for (std::size_t g = 0; g < G; ++g)
193 for (
int a = 0; a < 3; ++a)
194 if (std::fabs(fresh[g][a] - gpos[g][a]) > 1e-9)
198 MPI_Allreduce(&fail, &total, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
202 "OK (np=%d): scheme C (local ghost integration via forwarded forces) matches serial"
203 " AND ghosts stay consistent\n",
206 std::fprintf(stderr,
"FAILED (np=%d): %d\n", size, total);
209 return total == 0 ? 0 : 1;