PeriDEM 0.3.0
PeriDEM -- Peridynamics-based high-fidelity model for granular media
Loading...
Searching...
No Matches
anonymous_namespace{particleMpi.cpp} Namespace Reference

Functions

void factor2d (int nproc, double Lx, double Ly, int &nx, int &ny)
 
void packGrainKinematics (const data::ModelData &data, const particle::BaseParticle &p, std::vector< double > &buf)
 
size_t unpackGrainKinematics (data::ModelData &data, particle::BaseParticle &p, const std::vector< double > &buf, size_t offset)
 
size_t ghostRebuildInterval (const data::ModelData &data)
 
void rebuildGhostPlan (data::ModelData &data)
 
void exchangeCachedKinematics (data::ModelData &data)
 

Function Documentation

◆ exchangeCachedKinematics()

void anonymous_namespace{particleMpi.cpp}::exchangeCachedKinematics ( data::ModelData data)

Definition at line 261 of file particleMpi.cpp.

261 {
262 MPI_Comm comm = util::parallel::mpiComm();
263 const int size = util::parallel::mpiSize();
264 const auto &grains = data.d_particlesListTypeParticle;
265
266 std::vector<std::vector<double>> kin_send(static_cast<size_t>(size));
267 for (int r = 0; r < size; ++r) {
268 for (int g : data.d_mpiGhostServeTo[static_cast<size_t>(r)])
269 packGrainKinematics(data, *grains[static_cast<size_t>(g)],
270 kin_send[static_cast<size_t>(r)]);
271 }
272
273 std::vector<int> kin_sendcounts(static_cast<size_t>(size), 0);
274 std::vector<int> kin_recvcounts(static_cast<size_t>(size), 0);
275 for (int r = 0; r < size; ++r)
276 kin_sendcounts[static_cast<size_t>(r)] =
277 static_cast<int>(kin_send[static_cast<size_t>(r)].size());
278 MPI_Alltoall(kin_sendcounts.data(), 1, MPI_INT, kin_recvcounts.data(), 1,
279 MPI_INT, comm);
280
281 std::vector<int> kin_sdispls(static_cast<size_t>(size), 0);
282 std::vector<int> kin_rdispls(static_cast<size_t>(size), 0);
283 int kin_send_total = 0;
284 int kin_recv_total = 0;
285 for (int r = 0; r < size; ++r) {
286 kin_sdispls[static_cast<size_t>(r)] = kin_send_total;
287 kin_rdispls[static_cast<size_t>(r)] = kin_recv_total;
288 kin_send_total += kin_sendcounts[static_cast<size_t>(r)];
289 kin_recv_total += kin_recvcounts[static_cast<size_t>(r)];
290 }
291
292 std::vector<double> kin_sendbuf(static_cast<size_t>(kin_send_total));
293 for (int r = 0; r < size; ++r) {
294 const auto &v = kin_send[static_cast<size_t>(r)];
295 std::copy(v.begin(), v.end(),
296 kin_sendbuf.begin() + kin_sdispls[static_cast<size_t>(r)]);
297 }
298 std::vector<double> kin_recvbuf(static_cast<size_t>(kin_recv_total));
299 MPI_Alltoallv(kin_sendbuf.data(), kin_sendcounts.data(), kin_sdispls.data(),
300 MPI_DOUBLE, kin_recvbuf.data(), kin_recvcounts.data(),
301 kin_rdispls.data(), MPI_DOUBLE, comm);
302
303 for (int r = 0; r < size; ++r) {
304 size_t off = static_cast<size_t>(kin_rdispls[static_cast<size_t>(r)]);
305 for (int g : data.d_mpiGhostNeedFrom[static_cast<size_t>(r)])
306 off = unpackGrainKinematics(data, *grains[static_cast<size_t>(g)],
307 kin_recvbuf, off);
308 }
309}
void packGrainKinematics(const data::ModelData &data, const particle::BaseParticle &p, std::vector< double > &buf)
size_t unpackGrainKinematics(data::ModelData &data, particle::BaseParticle &p, const std::vector< double > &buf, size_t offset)
Definition contact.h:20
int mpiSize()
Get size (number) of processors.
MPI_Comm mpiComm()
Get MPI comm.

References util::parallel::mpiComm(), util::parallel::mpiSize(), packGrainKinematics(), and unpackGrainKinematics().

Referenced by particle::exchangeGhostKinematics().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ factor2d()

void anonymous_namespace{particleMpi.cpp}::factor2d ( int  nproc,
double  Lx,
double  Ly,
int &  nx,
int &  ny 
)

Definition at line 52 of file particleMpi.cpp.

52 {
53 // nx*ny = nproc; prefer subdomain aspect close to domain aspect.
54 nx = 1;
55 ny = nproc;
56 double best = std::numeric_limits<double>::max();
57 for (int i = 1; i <= nproc; ++i) {
58 if (nproc % i != 0)
59 continue;
60 const int j = nproc / i;
61 const double ax = Lx / static_cast<double>(i);
62 const double ay = Ly / static_cast<double>(j);
63 const double score = std::abs(ax - ay);
64 if (score < best) {
65 best = score;
66 nx = i;
67 ny = j;
68 }
69 }
70}

Referenced by particle::assignMpiOwners().

Here is the caller graph for this function:

◆ ghostRebuildInterval()

size_t anonymous_namespace{particleMpi.cpp}::ghostRebuildInterval ( const data::ModelData data)

Definition at line 110 of file particleMpi.cpp.

110 {
111 size_t interval = data.d_contNeighUpdateInterval;
112 if (interval == 0 && data.d_particleDeck_p)
113 interval = data.d_particleDeck_p->d_pNeighDeck.d_neighUpdateInterval;
114 if (interval == 0)
115 interval = 1;
116 return interval;
117}

Referenced by particle::exchangeGhostKinematics().

Here is the caller graph for this function:

◆ packGrainKinematics()

void anonymous_namespace{particleMpi.cpp}::packGrainKinematics ( const data::ModelData data,
const particle::BaseParticle p,
std::vector< double > &  buf 
)

Definition at line 72 of file particleMpi.cpp.

74 {
75 const size_t n = p.getNumNodes();
76 const size_t base = buf.size();
77 buf.resize(base + 9 * n);
78 for (size_t i = 0; i < n; ++i) {
79 const size_t g = p.getNodeId(i);
80 const auto &x = data.d_x[g];
81 const auto &u = data.d_u[g];
82 const auto &v = data.d_v[g];
83 const size_t o = base + 9 * i;
84 buf[o + 0] = x.d_x;
85 buf[o + 1] = x.d_y;
86 buf[o + 2] = x.d_z;
87 buf[o + 3] = u.d_x;
88 buf[o + 4] = u.d_y;
89 buf[o + 5] = u.d_z;
90 buf[o + 6] = v.d_x;
91 buf[o + 7] = v.d_y;
92 buf[o + 8] = v.d_z;
93 }
94}
size_t getNumNodes() const
Get the number of nodes.
size_t getNodeId(size_t i_loc) const
Get global id of node given the local id of node in this object.

References particle::BaseParticle::getNodeId(), and particle::BaseParticle::getNumNodes().

Referenced by exchangeCachedKinematics().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ rebuildGhostPlan()

void anonymous_namespace{particleMpi.cpp}::rebuildGhostPlan ( data::ModelData data)

Definition at line 119 of file particleMpi.cpp.

119 {
120 MPI_Comm comm = util::parallel::mpiComm();
121 const int size = util::parallel::mpiSize();
122 const int rank = util::parallel::mpiRank();
123 const auto &grains = data.d_particlesListTypeParticle;
124 const size_t n_grains = grains.size();
125
126 std::vector<double> local_c(4 * n_grains, 0.);
127 for (size_t g = 0; g < n_grains; ++g) {
128 const auto c = grains[g]->getXCenter();
129 local_c[4 * g + 0] = c.d_x;
130 local_c[4 * g + 1] = c.d_y;
131 local_c[4 * g + 2] = c.d_z;
132 local_c[4 * g + 3] = grains[g]->d_geom_p->boundingRadius();
133 }
134 std::vector<double> all_c(4 * n_grains * static_cast<size_t>(size), 0.);
135 MPI_Allgather(local_c.data(), static_cast<int>(local_c.size()), MPI_DOUBLE,
136 all_c.data(), static_cast<int>(local_c.size()), MPI_DOUBLE,
137 comm);
138
139 std::vector<util::Point> centers(n_grains);
140 std::vector<double> radii(n_grains);
141 for (size_t g = 0; g < n_grains; ++g) {
142 const int own = grains[g]->d_mpiOwner;
143 const size_t base = static_cast<size_t>(own) * 4 * n_grains + 4 * g;
144 centers[g] = util::Point(all_c[base + 0], all_c[base + 1], all_c[base + 2]);
145 radii[g] = all_c[base + 3];
146 }
147
148 data.d_mpiIncludeInContactCloud.assign(data.d_particlesListTypeAll.size(), 0);
149 for (auto *p : data.d_particlesListTypeAll) {
150 if (p->isWall() || particle::isLocallyOwned(*p))
151 data.d_mpiIncludeInContactCloud[p->getId()] = 1;
152 }
153
154 // Contact search radius already carries sFactor skin when interval > 1;
155 // add a small extra buffer for MPI plan lifetime between rebuilds.
156 const double extra =
157 std::max(0.25 * data.d_maxContactR, std::max(data.d_hMax, 1.0e-16));
158 const double search_r =
159 (data.d_contNeighSearchRadius > 1.0e-16)
160 ? data.d_contNeighSearchRadius
161 : data.d_maxContactR;
162 const double cutoff = search_r + extra;
163
164 data.d_mpiGhostNeedFrom.assign(static_cast<size_t>(size), {});
165 size_t n_ghost = 0;
166 auto add_ghost = [&](size_t g) {
167 auto *pj = grains[g];
169 return;
170 const int own = pj->d_mpiOwner;
171 if (own < 0 || own == rank)
172 return;
173 auto &vec = data.d_mpiGhostNeedFrom[static_cast<size_t>(own)];
174 const int gid = static_cast<int>(g);
175 if (std::find(vec.begin(), vec.end(), gid) != vec.end())
176 return;
177 vec.push_back(gid);
178 data.d_mpiIncludeInContactCloud[pj->getId()] = 1;
179 ++n_ghost;
180 };
181
182 for (size_t g = 0; g < n_grains; ++g) {
183 bool near = false;
184 for (size_t i = 0; i < n_grains && !near; ++i) {
185 if (!particle::isLocallyOwned(*grains[i]))
186 continue;
187 const double lim = radii[i] + radii[g] + cutoff;
188 if (centers[i].dist(centers[g]) < lim)
189 near = true;
190 }
191 if (near)
192 add_ghost(g);
193 }
194
195 // Wall contact is assembled on ranks that own wall nodes (rank 0 for
196 // particle-MPI). Ghost any grain near a wall so plate/cup searches see them.
197 for (auto *w : data.d_particlesListTypeWall) {
198 if (!w)
199 continue;
200 const auto wc = w->getXCenter();
201 const double wr = w->d_geom_p->boundingRadius();
202 for (size_t g = 0; g < n_grains; ++g) {
203 const double lim = wr + radii[g] + cutoff;
204 if (wc.dist(centers[g]) < lim)
205 add_ghost(g);
206 }
207 }
208
209 std::vector<int> req_sendcounts(static_cast<size_t>(size), 0);
210 std::vector<int> req_recvcounts(static_cast<size_t>(size), 0);
211 for (int r = 0; r < size; ++r)
212 req_sendcounts[static_cast<size_t>(r)] =
213 static_cast<int>(data.d_mpiGhostNeedFrom[static_cast<size_t>(r)].size());
214 MPI_Alltoall(req_sendcounts.data(), 1, MPI_INT, req_recvcounts.data(), 1,
215 MPI_INT, comm);
216
217 std::vector<int> req_sdispls(static_cast<size_t>(size), 0);
218 std::vector<int> req_rdispls(static_cast<size_t>(size), 0);
219 int req_send_total = 0;
220 int req_recv_total = 0;
221 for (int r = 0; r < size; ++r) {
222 req_sdispls[static_cast<size_t>(r)] = req_send_total;
223 req_rdispls[static_cast<size_t>(r)] = req_recv_total;
224 req_send_total += req_sendcounts[static_cast<size_t>(r)];
225 req_recv_total += req_recvcounts[static_cast<size_t>(r)];
226 }
227
228 std::vector<int> req_sendbuf(static_cast<size_t>(req_send_total));
229 for (int r = 0; r < size; ++r) {
230 const auto &v = data.d_mpiGhostNeedFrom[static_cast<size_t>(r)];
231 std::copy(v.begin(), v.end(),
232 req_sendbuf.begin() + req_sdispls[static_cast<size_t>(r)]);
233 }
234 std::vector<int> req_recvbuf(static_cast<size_t>(req_recv_total));
235 MPI_Alltoallv(req_sendbuf.data(), req_sendcounts.data(), req_sdispls.data(),
236 MPI_INT, req_recvbuf.data(), req_recvcounts.data(),
237 req_rdispls.data(), MPI_INT, comm);
238
239 data.d_mpiGhostServeTo.assign(static_cast<size_t>(size), {});
240 for (int r = 0; r < size; ++r) {
241 const int off = req_rdispls[static_cast<size_t>(r)];
242 const int nreq = req_recvcounts[static_cast<size_t>(r)];
243 auto &dst = data.d_mpiGhostServeTo[static_cast<size_t>(r)];
244 dst.resize(static_cast<size_t>(nreq));
245 for (int k = 0; k < nreq; ++k) {
246 const int g = req_recvbuf[static_cast<size_t>(off + k)];
247 if (g < 0 || static_cast<size_t>(g) >= n_grains)
248 throw std::runtime_error("rebuildGhostPlan: bad grain id");
249 if (grains[static_cast<size_t>(g)]->d_mpiOwner != rank)
250 throw std::runtime_error(
251 "rebuildGhostPlan: requested grain not owned here");
252 dst[static_cast<size_t>(k)] = g;
253 }
254 }
255
256 data.d_mpiGhostPlanValid = true;
257 data.d_mpiGhostStepsSinceRebuild = 0;
258 data.setKeyData("mpi_ghost_grain_count", static_cast<double>(n_ghost));
259}
bool near(double a, double b, double tol=1.e-12)
Definition main.cpp:25
bool isLocallyOwned(const BaseParticle &p)
True if this rank updates / assembles forces for the particle. Walls are replicated on every rank....
int mpiRank()
get rank (id) of this processor
A structure to represent 3d vectors.
Definition point.h:30

References particle::isLocallyOwned(), util::parallel::mpiComm(), util::parallel::mpiRank(), and util::parallel::mpiSize().

Referenced by particle::exchangeGhostKinematics().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ unpackGrainKinematics()

size_t anonymous_namespace{particleMpi.cpp}::unpackGrainKinematics ( data::ModelData data,
particle::BaseParticle p,
const std::vector< double > &  buf,
size_t  offset 
)

Definition at line 96 of file particleMpi.cpp.

97 {
98 const size_t n = p.getNumNodes();
99 for (size_t i = 0; i < n; ++i) {
100 const size_t g = p.getNodeId(i);
101 const size_t o = offset + 9 * i;
102 data.d_x[g] = util::Point(buf[o + 0], buf[o + 1], buf[o + 2]);
103 data.d_u[g] = util::Point(buf[o + 3], buf[o + 4], buf[o + 5]);
104 data.d_v[g] = util::Point(buf[o + 6], buf[o + 7], buf[o + 8]);
105 data.d_vMag[g] = data.d_v[g].length();
106 }
107 return offset + 9 * n;
108}

References particle::BaseParticle::getNodeId(), and particle::BaseParticle::getNumNodes().

Referenced by exchangeCachedKinematics().

Here is the call graph for this function:
Here is the caller graph for this function: