80int main(
int argc,
char** argv) {
81 MPI_Init(&argc, &argv);
82 int rank = 0, size = 1;
83 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
84 MPI_Comm_size(MPI_COMM_WORLD, &size);
86 const std::int64_t N = 400;
89 std::vector<Vec<3>> p0(N);
90 std::vector<Vec<3>> v0(N);
91 for (std::int64_t i = 0; i < N; ++i) {
92 for (
int a = 0; a < 3; ++a) {
94 v0[i][a] = (
frac(i, 10 + a) - 0.5) * 0.2;
99 std::vector<Vec<3>> sp = p0, sv = v0;
100 for (
int step = 0; step <
kSteps; ++step) {
101 std::vector<Vec<3>> F(N);
102 for (std::int64_t i = 0; i < N; ++i) {
103 std::vector<std::pair<std::int64_t, Vec<3>>> nbr;
105 for (std::int64_t j = 0; j < N; ++j)
106 nbr.push_back({j, sp[j]});
107 F[i] =
force(sp[i], i, nbr);
109 for (std::int64_t i = 0; i < N; ++i)
110 for (
int a = 0; a < 3; ++a) {
111 sv[i][a] = (sv[i][a] + F[i][a] *
kDt) *
kDamp;
112 sp[i][a] += sv[i][a] *
kDt;
113 sp[i][a] =
dmin[a] + std::fmod(std::fmod(sp[i][a] -
dmin[a],
L[a]) +
L[a],
L[a]);
118 IVec<3> gsize{40, 32, 24};
119 BlockDecomposer<3> dec(
static_cast<std::size_t
>(size), gsize);
121 for (
int a = 0; a < 3; ++a) {
122 map.origin[a] =
dmin[a];
123 map.cellSize[a] =
L[a] / gsize[a];
124 map.periodic[a] =
true;
126 ParticleMigrator<3> mig;
127 mig.init(dec, rank, map, MPI_COMM_WORLD);
129 const std::size_t stride =
sizeof(
Pay);
130 std::vector<Vec<3>> pos;
131 std::vector<char> payload;
132 for (std::int64_t
id = rank;
id < N;
id += size) {
133 pos.push_back(p0[
id]);
135 pay.
vel[0] = v0[id][0];
136 pay.vel[1] = v0[id][1];
137 pay.vel[2] = v0[id][2];
139 std::size_t off = payload.size();
140 payload.resize(off + stride);
141 std::memcpy(&payload[off], &pay, stride);
144 for (
int step = 0; step <
kSteps; ++step) {
145 mig.migrate(pos, payload, stride);
146 std::vector<Vec<3>> gpos;
147 std::vector<char> gpay;
148 mig.gatherGhosts(pos, payload, stride,
kRcut, gpos, gpay);
150 std::size_t n = pos.size();
151 std::vector<Vec<3>> F(n);
152 for (std::size_t i = 0; i < n; ++i) {
154 std::memcpy(&pi, &payload[i * stride], stride);
155 std::vector<std::pair<std::int64_t, Vec<3>>> nbr;
156 nbr.reserve(n + gpos.size());
157 for (std::size_t j = 0; j < n; ++j) {
159 std::memcpy(&pj, &payload[j * stride], stride);
160 nbr.push_back({pj.
id, pos[j]});
162 for (std::size_t j = 0; j < gpos.size(); ++j) {
164 std::memcpy(&pj, &gpay[j * stride], stride);
165 nbr.push_back({pj.
id, gpos[j]});
167 F[i] =
force(pos[i], pi.
id, nbr);
169 for (std::size_t i = 0; i < n; ++i) {
171 std::memcpy(&pi, &payload[i * stride], stride);
172 for (
int a = 0; a < 3; ++a) {
174 pos[i][a] += pi.
vel[a] *
kDt;
175 pos[i][a] =
dmin[a] + std::fmod(std::fmod(pos[i][a] -
dmin[a],
L[a]) +
L[a],
L[a]);
177 std::memcpy(&payload[i * stride], &pi, stride);
183 for (std::size_t i = 0; i < pos.size(); ++i) {
185 std::memcpy(&pi, &payload[i * stride], stride);
186 for (
int a = 0; a < 3; ++a) {
187 if (std::fabs(pos[i][a] - sp[pi.
id][a]) > 1e-9)
189 if (std::fabs(pi.
vel[a] - sv[pi.
id][a]) > 1e-9)
193 std::int64_t lcount = (std::int64_t)pos.size(), gcount = 0;
194 MPI_Reduce(&lcount, &gcount, 1, MPI_INT64_T, MPI_SUM, 0, MPI_COMM_WORLD);
196 MPI_Allreduce(&fail, &total, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
198 std::printf(
"# DEM loop: %lld particles, %d steps (count check %lld==%lld)\n", (
long long)N,
199 kSteps, (
long long)gcount, (
long long)N);
201 std::printf(
"OK (np=%d): distributed migrate+ghost+force+integrate matches serial\n", size);
203 std::fprintf(stderr,
"FAILED (np=%d): %d state mismatches\n", size, total);
206 return total == 0 ? 0 : 1;