OpenLB 1.7
Loading...
Searching...
No Matches
particleContactCommunicationFunctions.h
Go to the documentation of this file.
1/* This file is part of the OpenLB library
2 *
3 * Copyright (C) 2021 Jan E. Marquardt, Mathias J. Krause
4 * E-mail contact: info@openlb.net
5 * The most recent release of OpenLB can be downloaded at
6 * <http://www.openlb.net/>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public
19 * License along with this program; if not, write to the Free
20 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22*/
23
24/*
25 * This file contains functions used for the particle-wall and particle-particle communication.
26*/
27
28#ifndef PARTICLE_CONTACT_COMMUNICATION_FUNCTIONS_H
29#define PARTICLE_CONTACT_COMMUNICATION_FUNCTIONS_H
30
31namespace olb {
32
33namespace particles {
34
35namespace contact {
36
37template <typename T, typename PARTICLECONTACTTYPE, typename WALLCONTACTTYPE>
40{
41#ifdef PARALLEL_MODE_MPI
42 OstreamManager clout(std::cout, "contactCommunication");
43
44 if (singleton::mpi().getSize() > 1) {
45 int sumParticleContact = 0;
46 int sumWallContact = 0;
47 int countParticleContactsPerRank[singleton::mpi().getSize()];
48 int countWallContactsPerRank[singleton::mpi().getSize()];
49 int localCountParticleContacts = 0;
50 int localCountWallContacts = 0;
51
52 //clout << "communicate number of contacts ..." << std::endl;
53 if (singleton::mpi().isMainProcessor()) {
54 countParticleContactsPerRank[singleton::mpi().bossId()] =
55 contactContainer.particleContacts.size();
56 sumParticleContact +=
57 countParticleContactsPerRank[singleton::mpi().bossId()];
58 countWallContactsPerRank[singleton::mpi().bossId()] =
59 contactContainer.wallContacts.size();
60 sumWallContact += countWallContactsPerRank[singleton::mpi().bossId()];
61 for (int rank = 0; rank < singleton::mpi().getSize(); ++rank) {
62 if (rank != singleton::mpi().bossId()) {
63 singleton::mpi().receive(&countParticleContactsPerRank[rank], 1,
64 rank);
65 sumParticleContact += countParticleContactsPerRank[rank];
66 singleton::mpi().receive(&countWallContactsPerRank[rank], 1, rank);
67 sumWallContact += countWallContactsPerRank[rank];
68 }
69 }
70 }
71 else {
72 localCountParticleContacts =
73 contactContainer.particleContacts
74 .size(); // size of std::vector (called contacts in container)
75 singleton::mpi().send(&localCountParticleContacts, 1,
76 singleton::mpi().bossId()); // 0 is rank of master
77 localCountWallContacts = contactContainer.wallContacts.size();
78 singleton::mpi().send(&localCountWallContacts, 1,
79 singleton::mpi().bossId());
80 }
81 //clout << "communicate number of contacts ... OK" << std::endl;
82
83 //clout << "broadcasting number of contacts ..." << std::endl;
84 singleton::mpi().bCast(&sumParticleContact, 1, singleton::mpi().bossId());
85 singleton::mpi().bCast(&sumWallContact, 1, singleton::mpi().bossId());
86 //std::cout << sumParticleContact << std::endl;
87 //singleton::mpi().barrier();
88 //clout << "broadcasting number of contacts ... OK" << std::endl;
89
90 if (sumParticleContact > 0 || sumWallContact > 0) {
92 sumParticleContact, sumWallContact);
93 int particleContactObjSize = sizeof(PARTICLECONTACTTYPE);
94 int wallContactObjSize = sizeof(WALLCONTACTTYPE);
95
96 //clout << "Bundling contact objects ..." << std::endl;
98 PARTICLECONTACTTYPE* currParticle = container.particleContacts.data();
99 WALLCONTACTTYPE* currWall = container.wallContacts.data();
100 for (int rank = 0; rank < singleton::mpi().getSize(); ++rank) {
101 std::uint8_t* bytesParticle = (std::uint8_t*)currParticle;
102 std::uint8_t* bytesWall = (std::uint8_t*)currWall;
103 if (rank != singleton::mpi().bossId()) {
104 singleton::mpi().receive(bytesParticle,
105 countParticleContactsPerRank[rank] *
106 particleContactObjSize,
107 rank);
109 bytesWall, countWallContactsPerRank[rank] * wallContactObjSize,
110 rank);
111 }
112 else {
113 std::memcpy(
114 bytesParticle,
115 (std::uint8_t*)contactContainer.particleContacts.data(),
116 countParticleContactsPerRank[singleton::mpi().bossId()] *
117 particleContactObjSize);
118 std::memcpy(bytesWall,
119 (std::uint8_t*)contactContainer.wallContacts.data(),
120 countWallContactsPerRank[singleton::mpi().bossId()] *
121 wallContactObjSize);
122 }
123 currParticle += countParticleContactsPerRank[rank];
124 currWall += countWallContactsPerRank[rank];
125 }
126 }
127 else {
128 PARTICLECONTACTTYPE* currParticle =
129 contactContainer.particleContacts.data();
130 WALLCONTACTTYPE* currWall = contactContainer.wallContacts.data();
131 std::uint8_t* bytesParticle = (std::uint8_t*)currParticle;
132 std::uint8_t* bytesWall = (std::uint8_t*)currWall;
134 bytesParticle, localCountParticleContacts * particleContactObjSize,
135 singleton::mpi().bossId());
136 singleton::mpi().send(bytesWall,
137 localCountWallContacts * wallContactObjSize,
138 singleton::mpi().bossId());
139 }
140 //clout << "Bundling contact objects ... OK" << std::endl;
141
142 if (singleton::mpi().isMainProcessor()) {
143 container.combineContacts();
144 }
145
146 //clout << "Broadcasting contact objects ..." << std::endl;
147 std::uint8_t* containerParticlesBytes =
148 (std::uint8_t*)container.particleContacts.data();
149 singleton::mpi().bCast(containerParticlesBytes,
150 sumParticleContact * particleContactObjSize,
151 singleton::mpi().bossId());
152 std::uint8_t* containerWallBytes =
153 (std::uint8_t*)container.wallContacts.data();
154 singleton::mpi().bCast(containerWallBytes,
155 sumWallContact * wallContactObjSize,
156 singleton::mpi().bossId());
157 //singleton::mpi().barrier();
158 //clout << "Broadcasting contact objects ... OK" << std::endl;
159 /*
160 clout << "#######" << std::endl;
161 clout << "id: " << container.wallContacts[3].id << std::endl;
162 clout << "#######" << std::endl;
163 */
164 //std::cout << "size before: " << container.particleContacts.size() << std::endl;
165 container.cleanContacts();
166 //std::cout << "size after: " << container.particleContacts.size() << std::endl;
167 contactContainer = container;
168 }
169 }
170#endif
171}
172
174template <typename T, typename PARTICLETYPE, typename PARTICLECONTACTTYPE,
175 typename WALLCONTACTTYPE, typename F>
179 contactContainer,
180 const SuperGeometry<T, PARTICLETYPE::d>& sGeometry, F getSetupPeriodicity)
181{
182 using namespace descriptors;
183
184 // if a periodic setup is used, we have to update min and max in rare occasions
185 // i.e. when the first/only particle "jumps" from the end to the beginning
186 // because then the min and max from the step before isn't valid anymore
187 const T physDeltaX =
188 sGeometry.getCuboidGeometry().getMotherCuboid().getDeltaR();
189 const PhysR<T, PARTICLETYPE::d> cellMin =
190 particles::communication::getCuboidMin<T, PARTICLETYPE::d>(
191 sGeometry.getCuboidGeometry());
192 const PhysR<T, PARTICLETYPE::d> cellMax =
193 particles::communication::getCuboidMax<T, PARTICLETYPE::d>(
194 sGeometry.getCuboidGeometry(), cellMin);
195
197 sParticleSystem,
198 [&](Particle<T, PARTICLETYPE>& particle,
199 ParticleSystem<T, PARTICLETYPE>& particleSystem, int globiC) {
200 const std::size_t currentGlobalParticleID =
201 particle.template getField<PARALLELIZATION, ID>();
202
203 for (WALLCONTACTTYPE& wallContact : contactContainer.wallContacts) {
204 if (!wallContact.isEmpty() && !wallContact.isNew() &&
205 wallContact.getParticleID() == currentGlobalParticleID &&
206 wallContact.getResponsibleRank() == singleton::mpi().getRank()) {
207 const PhysR<T, PARTICLETYPE::d> min = wallContact.getMin();
208 const PhysR<T, PARTICLETYPE::d> max = wallContact.getMax();
209 wallContact.resetMinMax();
211 particle, cellMin, cellMax, getSetupPeriodicity, physDeltaX);
212 wallContact.setParticlePosition(unifiedPos);
213 const PhysR<T, PARTICLETYPE::d> newMin =
215 particle, wallContact.getParticlePosition(), min, cellMin,
216 cellMax, getSetupPeriodicity, physDeltaX);
217 const PhysR<T, PARTICLETYPE::d> newMax =
219 particle, wallContact.getParticlePosition(), max, cellMin,
220 cellMax, getSetupPeriodicity, physDeltaX);
221 wallContact.updateMinMax(newMin);
222 wallContact.updateMinMax(newMax);
223 }
224 }
225 for (PARTICLECONTACTTYPE& particleContact :
226 contactContainer.particleContacts) {
227 if (!particleContact.isEmpty() && !particleContact.isNew() &&
228 particleContact.getIDs()[0] == currentGlobalParticleID &&
229 particleContact.getResponsibleRank() ==
231 const PhysR<T, PARTICLETYPE::d> min = particleContact.getMin();
232 const PhysR<T, PARTICLETYPE::d> max = particleContact.getMax();
233 particleContact.resetMinMax();
234 std::array<PhysR<T, PARTICLETYPE::d>, 2> unifiedPos;
235 auto particleB = sParticleSystem.get(particleContact.getIDs()[1]);
236 contact::unifyPositions(particle, particleB, cellMin, cellMax,
237 getSetupPeriodicity, unifiedPos,
238 physDeltaX);
239 particleContact.setParticlePositions(unifiedPos);
240 const PhysR<T, PARTICLETYPE::d> newMin =
242 particle, particleContact.getParticlePositions()[0], min,
243 cellMin, cellMax, getSetupPeriodicity, physDeltaX);
244 const PhysR<T, PARTICLETYPE::d> newMax =
246 particle, particleContact.getParticlePositions()[0], max,
247 cellMin, cellMax, getSetupPeriodicity, physDeltaX);
248 particleContact.updateMinMax(newMin);
249 particleContact.updateMinMax(newMax);
250 }
251 }
252 });
253}
254
256template <typename T, typename PARTICLETYPE, bool CONVEX>
259 contact,
260 SuperParticleSystem<T, PARTICLETYPE>& sParticleSystem, const T deltaX,
261 const Vector<bool, PARTICLETYPE::d>& periodicity)
262{
263 using namespace descriptors;
264 auto& superStructure = sParticleSystem.getSuperStructure();
265 auto& loadBalancer = superStructure.getLoadBalancer();
266 /* auto& cuboidGeometry = superStructure.getCuboidGeometry(); */
267
268 // Set instead of unordered_set so that it's sorted
269 // Therefore, it is easier to find the the rank with smallest ID
270 std::set<int> intersectingRanks {};
271 std::array<int, 2> responsibleRank = {-1, -1};
272 std::array<std::set<int>, 2> touchedRanks {};
273 std::unordered_set<int> destRanks {};
274
275 // Function that is called on failure
276 const auto errorTreatment = [&contact, &destRanks]() {
277 contact.resetMinMax();
278 resetResponsibleRank(contact);
279 destRanks.clear();
280 };
281
282 // Iterate over all particles to populate responsible and touched ranks of each particle in the contact
285 sParticleSystem,
286 [&](Particle<T, PARTICLETYPE>& particle,
287 ParticleSystem<T, PARTICLETYPE>& particleSystem, int globiC) {
288 const std::size_t globalParticleID =
289 particle.template getField<PARALLELIZATION, ID>();
290
291 for (unsigned short index = 0; index < 2; ++index) {
292 if (globalParticleID == contact.getIDs()[index]) {
293 const int globiCcenter =
294 particle.template getField<PARALLELIZATION, IC>();
295 responsibleRank[index] = loadBalancer.rank(globiCcenter);
296 touchedRanks[index].insert(responsibleRank[index]);
297
298 const PhysR<T, PARTICLETYPE::d> position =
299 access::getPosition(particle);
300 const T circumRadius = evalCircumRadius(particle, deltaX);
302 sParticleSystem, position, circumRadius, periodicity,
303 globiCcenter, [&](const int iC) {
304 touchedRanks[index].insert(loadBalancer.rank(iC));
305 });
306 }
307 }
308 });
309
310 // If at least one particle has no touched cuboids then cancel function call
311 // (this should be caused by an invalid particle that somehow still has an existing contact)
312 if (touchedRanks[0].empty() || touchedRanks[1].empty() ||
313 responsibleRank[0] < 0 || responsibleRank[1] < 0) {
314 errorTreatment();
315 }
316 // If both particles have the same responsible rank then let that rank take care of the contact
317 else if (responsibleRank[0] == responsibleRank[1]) {
318 destRanks.insert(responsibleRank[0]);
319 contact.setResponsibleRank(responsibleRank[0]);
320 }
321 // If not, then determine treatment rank from intersection of ranks that touch the particles
322 else {
323 std::set_intersection(
324 touchedRanks[0].begin(), touchedRanks[0].end(), touchedRanks[1].begin(),
325 touchedRanks[1].end(),
326 std::inserter(intersectingRanks, intersectingRanks.begin()));
327
328 /*
329 * It's possible that no intersection is found, if a rank loses responsibility
330 * for the contact, but still holds the old information. At the same time,
331 * the contact of both particles breaks, and they are also on different ranks,
332 * without any processor that knows both. This is very unlikely, but it can
333 * happen.
334 * However, the following simple if-condition shouldn't be expensive as it is
335 * almost always true.
336 */
337 if (!intersectingRanks.empty()) {
338 destRanks.insert(responsibleRank[0]);
339 destRanks.insert(responsibleRank[1]);
340
341 // Sort responsible ranks so that the following is consistent
342 if (responsibleRank[0] > responsibleRank[1]) {
343 std::swap(responsibleRank[0], responsibleRank[1]);
344 }
345
346 // First use one of the responsible ranks if they know both particles
347 if (intersectingRanks.find(responsibleRank[0]) !=
348 intersectingRanks.end()) {
349 contact.setResponsibleRank(responsibleRank[0]);
350 }
351 else if (intersectingRanks.find(responsibleRank[1]) !=
352 intersectingRanks.end()) {
353 contact.setResponsibleRank(responsibleRank[1]);
354 }
355 // Otherwise use smallest rank of intersection
356 else {
357 const int responsibleRankContact = *intersectingRanks.begin();
358 contact.setResponsibleRank(responsibleRankContact);
359 destRanks.insert(responsibleRankContact);
360 }
361 }
362 else {
363 errorTreatment();
364 }
365 }
366
367#ifdef VERBOSE_CONTACT_COMMUNICATION
368 std::cout << "Contact of ids " << contact.getIDs()[0] << " & "
369 << contact.getIDs()[1] << " goes to " << destRanks << " and "
370 << contact.getResponsibleRank()
371 << " is responsible for the calculation (Rank "
372 << singleton::mpi().getRank() << ")"
373 << " the intersection was " << intersectingRanks
374 << " from touched ranks " << touchedRanks[0] << " and "
375 << touchedRanks[1] << std::endl;
376#endif
377
378 return destRanks;
379}
380
382template <typename T, typename PARTICLETYPE, bool CONVEX>
385 SuperParticleSystem<T, PARTICLETYPE>& sParticleSystem, const T deltaX,
386 [[maybe_unused]] const Vector<bool, PARTICLETYPE::d>& periodicity)
387{
388 using namespace descriptors;
389 auto& superStructure = sParticleSystem.getSuperStructure();
390 auto& loadBalancer = superStructure.getLoadBalancer();
391
392 bool isSuccessful = false;
393 std::unordered_set<int> destRanks {};
394 int destRank;
395 // Find responsible rank
398 sParticleSystem,
399 [&](Particle<T, PARTICLETYPE>& particle,
400 ParticleSystem<T, PARTICLETYPE>& particleSystem, int globiC) {
401 const std::size_t globalParticleID =
402 particle.template getField<PARALLELIZATION, ID>();
403 if (globalParticleID == contact.getParticleID()) {
404 std::size_t globiCcenter =
405 particle.template getField<PARALLELIZATION, IC>();
406 destRank = loadBalancer.rank(globiCcenter);
407 isSuccessful = true;
408 }
409 });
410
411 if (isSuccessful) {
412 contact.setResponsibleRank(destRank);
413 if (destRank != singleton::mpi().getRank()) {
414 destRanks.insert(destRank);
415 }
416
417#ifdef VERBOSE_CONTACT_COMMUNICATION
418 std::cout << "Contact of id " << contact.getParticleID()
419 << " & solid boundary " << contact.getWallID() << " goes to "
420 << destRanks << std::endl;
421#endif
422 }
423
424 return destRanks;
425}
426
428template <typename T, typename PARTICLETYPE, bool CONVEX>
431 contact,
432 SuperParticleSystem<T, PARTICLETYPE>& sParticleSystem, const T deltaX,
433 const Vector<bool, PARTICLETYPE::d>& periodicity)
434{
435 using namespace descriptors;
436 auto& superStructure = sParticleSystem.getSuperStructure();
437 auto& loadBalancer = superStructure.getLoadBalancer();
438 auto& cuboidGeometry = superStructure.getCuboidGeometry();
439
440 std::unordered_set<int> destRanks;
441 std::array<std::set<int>, 2> touchedRanks;
442
443 // Iterate over all particles to populate touched ranks of each particle in the contact
446 sParticleSystem,
447 [&](Particle<T, PARTICLETYPE>& particle,
448 ParticleSystem<T, PARTICLETYPE>& particleSystem, int globiC) {
449 const std::size_t globalParticleID =
450 particle.template getField<PARALLELIZATION, ID>();
451
452 for (unsigned short index = 0; index < 2; ++index) {
453 if (globalParticleID == contact.getIDs()[index]) {
454 const PhysR<T, PARTICLETYPE::d> position =
455 access::getPosition(particle);
456 // the particle moved, therefore, the iC may have changed
457 int globiCcenter;
458 const bool cuboidFound = communication::getCuboid(
459 cuboidGeometry, periodicity, position, globiCcenter);
460 if (cuboidFound) {
461 touchedRanks[index].insert(loadBalancer.rank(globiCcenter));
462
463 const T circumRadius = evalCircumRadius(particle, deltaX);
465 sParticleSystem, position, circumRadius, periodicity,
466 globiCcenter, [&](const int iC) {
467 touchedRanks[index].insert(loadBalancer.rank(iC));
468 });
469 }
470 }
471 }
472 });
473
474 std::set_intersection(touchedRanks[0].begin(), touchedRanks[0].end(),
475 touchedRanks[1].begin(), touchedRanks[1].end(),
476 std::inserter(destRanks, destRanks.begin()));
477
478#ifdef VERBOSE_CONTACT_COMMUNICATION
479 std::cout << "Contact of ids " << contact.getIDs()[0] << " & "
480 << contact.getIDs()[1] << " goes to " << destRanks << " and "
481 << contact.getResponsibleRank()
482 << " is responsible for the calculation (Rank "
483 << singleton::mpi().getRank() << ")" << '\n'
484 << "Touched ranks of 1. particle: " << touchedRanks[0]
485 << " and touched ranks of 2. particle: " << touchedRanks[1]
486 << std::endl;
487#endif
488
489 return destRanks;
490}
491
493template <typename T, typename PARTICLETYPE, bool CONVEX>
496 SuperParticleSystem<T, PARTICLETYPE>& sParticleSystem, const T deltaX,
497 const Vector<bool, PARTICLETYPE::d>& periodicity)
498{
499 using namespace descriptors;
500 auto& superStructure = sParticleSystem.getSuperStructure();
501 auto& loadBalancer = superStructure.getLoadBalancer();
502 auto& cuboidGeometry = superStructure.getCuboidGeometry();
503
504 std::unordered_set<int> destRanks;
505
506 // Find responsible rank
509 sParticleSystem,
510 [&](Particle<T, PARTICLETYPE>& particle,
511 ParticleSystem<T, PARTICLETYPE>& particleSystem, int globiC) {
512 const std::size_t globalParticleID =
513 particle.template getField<PARALLELIZATION, ID>();
514 if (globalParticleID == contact.getParticleID()) {
515 const PhysR<T, PARTICLETYPE::d> position =
516 access::getPosition(particle);
517 // the particle moved, therefore, the iC may have changed
518 int globiCcenter;
519 const bool cuboidFound = communication::getCuboid(
520 cuboidGeometry, periodicity, position, globiCcenter);
521 if (cuboidFound) {
522 destRanks.insert(loadBalancer.rank(globiCcenter));
523
524 const T circumRadius {evalCircumRadius(particle, deltaX)};
526 sParticleSystem, position, circumRadius, periodicity,
527 globiCcenter, [&](const int iC) {
528 destRanks.insert(loadBalancer.rank(iC));
529 });
530 }
531 }
532 });
533
534#ifdef VERBOSE_CONTACT_COMMUNICATION
535 std::cout << "Contact of id " << contact.getParticleID()
536 << " & solid boundary " << contact.getWallID() << " goes to "
537 << destRanks << " and rank " << contact.getResponsibleRank()
538 << " is responsible for the calculation" << std::endl;
539#endif
540
541 return destRanks;
542}
543
544template <typename T, typename PARTICLETYPE, typename CONTACTTYPE, bool CONVEX,
545 bool SANITYCHECK = false>
548 newcontact,
550 std::vector<CONTACTTYPE>& contacts, [[maybe_unused]] int rankOrig)
551{
552 using namespace descriptors;
553
554 bool addNewContact = true;
555
556 // Optional sanity check
557 if constexpr (SANITYCHECK) {
558 if (newcontact.getResponsibleRank() == singleton::mpi().getRank()) {
559 bool isParticleDataAvailable[2] = {false, false};
560 // Iterate over all particles to check for errors
562 sParticleSystem,
563 [&](Particle<T, PARTICLETYPE>& particle,
564 ParticleSystem<T, PARTICLETYPE>& particleSystem, int globiC) {
565 for (unsigned short i = 0; i < 2; ++i) {
566 if (particle.template getField<PARALLELIZATION, ID>() ==
567 newcontact.getIDs()[i]) {
568 isParticleDataAvailable[i] = true;
569 }
570 }
571 });
572 if (!isParticleDataAvailable[0] || !isParticleDataAvailable[1]) {
573 addNewContact = false;
574 std::cout << "WARNING: rank " << singleton::mpi().getRank()
575 << " received a contact of particles with the global ids "
576 << newcontact.getIDs()[0] << " and " << newcontact.getIDs()[1]
577 << " from rank " << rankOrig
578 << " for which no particle data are available." << std::endl;
579 }
580 }
581 }
582
583 if (addNewContact) {
584 const std::array<std::size_t, 2> particleIDs = newcontact.getIDs();
585
586 const std::function<bool(const CONTACTTYPE&)> condition =
587 [&particleIDs](const CONTACTTYPE& contact) {
588 return particleContactConsistsOfIDs(contact, particleIDs);
589 };
590
591 auto contactIt = getContactIterator(contacts, condition);
592
593 if (contactIt != std::end(contacts)) {
594 contactIt->combineWith(newcontact);
595 }
596 else {
597 contacts.push_back(newcontact);
598 }
599
600#ifdef VERBOSE_CONTACT_COMMUNICATION
601 std::cout << "Rank " << singleton::mpi().getRank()
602 << " received contact of ids " << newcontact.getIDs()[0] << " & "
603 << newcontact.getIDs()[1] << ". Rank "
604 << newcontact.getResponsibleRank()
605 << " is responsible for the calculation." << std::endl;
606#endif
607 }
608}
609
610template <typename T, typename PARTICLETYPE, typename CONTACTTYPE, bool CONVEX,
611 bool SANITYCHECK = false>
613 CONVEX>& newcontact,
615 std::vector<CONTACTTYPE>& contacts,
616 [[maybe_unused]] int rankOrig)
617{
618 using namespace descriptors;
619
620 bool addNewContact = true;
621
622 if constexpr (SANITYCHECK) {
623 if (newcontact.getResponsibleRank() == singleton::mpi().getRank()) {
624 bool isParticleDataAvailable = false;
625 // Iterate over all particles to check for errors
627 sParticleSystem,
628 [&](Particle<T, PARTICLETYPE>& particle,
629 ParticleSystem<T, PARTICLETYPE>& particleSystem, int globiC) {
630 if (particle.template getField<PARALLELIZATION, ID>() ==
631 newcontact.getParticleID()) {
632 isParticleDataAvailable = true;
633 }
634 });
635 if (!isParticleDataAvailable) {
636 addNewContact = false;
637 std::cout << "WARNING: rank " << singleton::mpi().getRank()
638 << " received a contact of particle with the global id "
639 << newcontact.getParticleID() << " from rank " << rankOrig
640 << " for which no particle data is available." << std::endl;
641 }
642 }
643 }
644
645 if (addNewContact) {
646 const std::size_t particleID = newcontact.getParticleID();
647 const std::size_t solidBoundaryMaterial = newcontact.getWallID();
648
649 const std::function<bool(const CONTACTTYPE&)> condition =
650 [&particleID, &solidBoundaryMaterial](const CONTACTTYPE& contact) {
651 return particleID == contact.getParticleID() &&
652 solidBoundaryMaterial == contact.getWallID();
653 };
654
655 auto contactIt = getContactIterator(contacts, condition);
656
657 if (contactIt != std::end(contacts)) {
658 contactIt->combineWith(newcontact);
659 }
660 else {
661 contacts.push_back(newcontact);
662 }
663 }
664}
665
666template <typename T, typename PARTICLETYPE, typename CONTACTTYPE>
668 std::vector<CONTACTTYPE>& contacts,
669 SuperParticleSystem<T, PARTICLETYPE>& sParticleSystem, const T deltaX,
670#ifdef PARALLEL_MODE_MPI
671 MPI_Comm contactDetectionComm,
672#endif
673 const Vector<bool, PARTICLETYPE::d>& periodicity)
674{
675#ifdef PARALLEL_MODE_MPI
676 OstreamManager clout(std::cout, "contactCommunication");
677
678 // Retrieve rank of direct neighbours
679 auto& listNeighbourRanks = sParticleSystem.getNeighbourRanks();
680 constexpr std::size_t serialSize = CONTACTTYPE::getSerialSize();
681
682 std::multimap<int, std::unique_ptr<std::uint8_t[]>> dataMap;
683
684 for (CONTACTTYPE& contact : contacts) {
685 // Reset responsible rank because we evaluate the new one in the next step
686 resetResponsibleRank(contact);
687
688 if (!contact.isEmpty()) {
689 std::unordered_set<int> destRanks {evalDestRanksForDetectionCommunication(
690 contact, sParticleSystem, deltaX, periodicity)};
691
692 // Only new contacts need communication because they originate from the on-lattice
693 // contact detection. Known contacts are not updated during that step.
695 // not new contacts need to be communicated as the responsible processors for each particle must know the responsible rank for contact treatment, which is evaluated above
696 && (contact.isNew() ||
697 contact.getResponsibleRank() == singleton::mpi().getRank())) {
698 for (const int destRank : destRanks) {
699 if (destRank != singleton::mpi().getRank()) {
700 std::unique_ptr<std::uint8_t[]> buffer(
701 new std::uint8_t[serialSize] {});
702 contact.serialize(buffer.get());
703 dataMap.insert(std::make_pair(destRank, std::move(buffer)));
704 }
705 }
706 }
707
708 // Reset contact on other ranks to avoid unnecessary communication
709 //if (destRanks.find(singleton::mpi().getRank()) == destRanks.end()) {
710 // contact.resetMinMax();
711 //}
712 }
713 }
714
715 //Create non blocking mpi helper
717
718 std::map<int, std::vector<std::uint8_t>> rankDataMapSorted;
719 communication::fillSendBuffer(dataMap, rankDataMapSorted, serialSize);
720
721 //Send mapped data
722 communication::sendMappedData(rankDataMapSorted, listNeighbourRanks,
723 serialSize, contactDetectionComm, mpiNbHelper);
724
725 //Receive data and iterate buffer
727 listNeighbourRanks, serialSize, contactDetectionComm, mpiNbHelper,
728 [&](int rankOrig, std::uint8_t* buffer) {
729 CONTACTTYPE newcontact;
730 newcontact.deserialize(buffer);
731
732#ifdef VERBOSE_CONTACT_COMMUNICATION
733 std::cout << "From Rank " << rankOrig << ": ";
734#endif
735
736 receiveContact(newcontact, sParticleSystem, contacts, rankOrig);
737 });
738#endif
739}
740
741template <typename T, typename PARTICLETYPE, typename PARTICLECONTACTTYPE,
742 typename WALLCONTACTTYPE>
745 SuperParticleSystem<T, PARTICLETYPE>& sParticleSystem, const T deltaX,
746#ifdef PARALLEL_MODE_MPI
747 MPI_Comm particleContactDetectionComm, MPI_Comm wallContactDetectionComm,
748#endif
749 const Vector<bool, PARTICLETYPE::d>& periodicity)
750{
751#ifdef PARALLEL_MODE_MPI
752 OstreamManager clout(std::cout, "contactCommunication");
753
754 communicateParallelContacts(contactContainer.wallContacts, sParticleSystem,
755 deltaX, wallContactDetectionComm, periodicity);
756
758 sParticleSystem, deltaX,
759 particleContactDetectionComm, periodicity);
760#endif
761}
762
763template <typename T, typename PARTICLETYPE, typename CONTACTTYPE>
765 std::vector<CONTACTTYPE>& contacts,
766 SuperParticleSystem<T, PARTICLETYPE>& sParticleSystem, const T deltaX,
767#ifdef PARALLEL_MODE_MPI
768 MPI_Comm postContactTreatmentComm,
769#endif
770 const Vector<bool, PARTICLETYPE::d>& periodicity)
771{
772#ifdef PARALLEL_MODE_MPI
773 OstreamManager clout(std::cout, "postContactTreatmentCommunication");
774
775 // Retrieve rank of direct neighbours
776 auto& listNeighbourRanks = sParticleSystem.getNeighbourRanks();
777 constexpr std::size_t serialSize = CONTACTTYPE::getSerialSize();
778
779 std::multimap<int, std::unique_ptr<std::uint8_t[]>> dataMap;
780
781 for (CONTACTTYPE& contact : contacts) {
782 if (contact.getResponsibleRank() == singleton::mpi().getRank()) {
783 if (!contact.isEmpty()) {
784 std::unordered_set<int> destRanks {
786 contact, sParticleSystem, deltaX, periodicity)};
787 resetResponsibleRank(contact);
788 for (const int destRank : destRanks) {
789 if (destRank != singleton::mpi().getRank()) {
790 std::unique_ptr<std::uint8_t[]> buffer(
791 new std::uint8_t[serialSize] {});
792 contact.serialize(buffer.get());
793 dataMap.insert(std::make_pair(destRank, std::move(buffer)));
794 }
795 }
796 }
797 }
798 // Otherwise reset min and max as they are not accurate anymore
799 else {
800 contact.resetMinMax();
801 }
802 }
803
804 //Create non blocking mpi helper
806
807 std::map<int, std::vector<std::uint8_t>> rankDataMapSorted;
808 communication::fillSendBuffer(dataMap, rankDataMapSorted, serialSize);
809
810 //Send mapped data
811 communication::sendMappedData(rankDataMapSorted, listNeighbourRanks,
812 serialSize, postContactTreatmentComm,
813 mpiNbHelper);
814
815 // Remove all empty contacts (= all contacts that were empty and all contacts where the process wasn't responsible) to ensure that 100% of the data send by the responsible rank is used
816 cleanContacts(contacts);
817
818 //Receive data and iterate buffer
820 listNeighbourRanks, serialSize, postContactTreatmentComm, mpiNbHelper,
821 [&](int rankOrig, std::uint8_t* buffer) {
822 CONTACTTYPE newcontact;
823 newcontact.deserialize(buffer);
824 receiveContact(newcontact, sParticleSystem, contacts, rankOrig);
825 });
826#endif
827}
828
829template <typename T, typename PARTICLETYPE, typename PARTICLECONTACTTYPE,
830 typename WALLCONTACTTYPE>
834 SuperParticleSystem<T, PARTICLETYPE>& sParticleSystem, const T deltaX,
835#ifdef PARALLEL_MODE_MPI
836 MPI_Comm particlePostContactTreatmentComm,
837 MPI_Comm wallPostContactTreatmentComm,
838#endif
839 const Vector<bool, PARTICLETYPE::d>& periodicity)
840{
841#ifdef PARALLEL_MODE_MPI
842 OstreamManager clout(std::cout, "postContactTreatmentCommunication");
843
845 contactContainer.wallContacts, sParticleSystem, deltaX,
846 wallPostContactTreatmentComm, periodicity);
847
849 contactContainer.particleContacts, sParticleSystem, deltaX,
850 particlePostContactTreatmentComm, periodicity);
851#endif
852}
853
854// Contact force communication
855#ifdef PARALLEL_MODE_MPI
856template <typename T, unsigned D>
858{
859 constexpr unsigned Drot = utilities::dimensions::convert<D>::rotation;
860
861 // Create communicatables
862 std::array<std::size_t, 1> commGlobalParticleID {1};
863 ConcreteCommunicatable communicatableID =
864 ConcreteCommunicatable(commGlobalParticleID);
865 Vector<T, D> forceDummy(1);
866 ConcreteCommunicatable communicatableForce =
867 ConcreteCommunicatable(forceDummy);
868 Vector<T, Drot> torqueDummy(1);
869 ConcreteCommunicatable communicatableTorque =
870 ConcreteCommunicatable(torqueDummy);
871
872 // Setting dimension dependent indices
873 const std::vector<unsigned> indicesID {0};
874 std::vector<unsigned> indicesForce;
875 if constexpr (D == 3) {
876 indicesForce = std::vector<unsigned> {0, 1, 2};
877 }
878 else {
879 indicesForce = std::vector<unsigned> {0, 1};
880 }
881 std::vector<unsigned> indicesTorque;
882 if constexpr (D == 3) {
883 indicesTorque = indicesForce;
884 }
885 else {
886 indicesTorque = std::vector<unsigned> {0};
887 }
888
889 // Retrieve serial size
890 return communicatableID.size(indicesID) +
891 communicatableForce.size(indicesForce) +
892 communicatableTorque.size(indicesTorque);
893}
894#endif
895
896template <typename T, unsigned D>
898 const std::size_t& globalParticleID, Vector<T, D>& force,
900 const int destRank,
901 std::multimap<int, std::unique_ptr<std::uint8_t[]>>& dataMap)
902{
903#ifdef PARALLEL_MODE_MPI
904 // Create communicatables
905 std::array<std::size_t, 1> commGlobalParticleID {globalParticleID};
906 ConcreteCommunicatable communicatableID =
907 ConcreteCommunicatable(commGlobalParticleID);
908 ConcreteCommunicatable communicatableForce = ConcreteCommunicatable(force);
909 ConcreteCommunicatable communicatableTorque = ConcreteCommunicatable(torque);
910
911 // Setting dimension dependent indices
912 const std::vector<unsigned> indicesID {0};
913 std::vector<unsigned> indicesForce;
914 if constexpr (D == 3) {
915 indicesForce = std::vector<unsigned> {0, 1, 2};
916 }
917 else {
918 indicesForce = std::vector<unsigned> {0, 1};
919 }
920 std::vector<unsigned> indicesTorque;
921 if constexpr (D == 3) {
922 indicesTorque = indicesForce;
923 }
924 else {
925 indicesTorque = std::vector<unsigned> {0};
926 }
927
928 // Retrieve serial size
929 const std::size_t serialSize = communicatableID.size(indicesID) +
930 communicatableForce.size(indicesForce) +
931 communicatableTorque.size(indicesTorque);
932
933 std::unique_ptr<std::uint8_t[]> buffer(new std::uint8_t[serialSize] {});
934 std::uint8_t* bufferRaw = buffer.get();
935 std::size_t serialIdx = communicatableID.serialize(indicesID, bufferRaw);
936 serialIdx +=
937 communicatableForce.serialize(indicesForce, &bufferRaw[serialIdx]);
938 serialIdx +=
939 communicatableTorque.serialize(indicesTorque, &bufferRaw[serialIdx]);
940
941 dataMap.insert(std::make_pair(destRank, std::move(buffer)));
942#ifdef VERBOSE_CONTACT_COMMUNICATION
943 std::cout << "Rank " << singleton::mpi().getRank()
944 << " sends contact treatment results of particle "
945 << globalParticleID << " to " << destRank << std::endl;
946#endif
947#endif
948}
949
950#ifdef PARALLEL_MODE_MPI
951template <typename T, typename PARTICLETYPE>
954 MPI_Comm contactTreatmentComm, singleton::MpiNonBlockingHelper& mpiNbHelper)
955{
956 using namespace descriptors;
957
958 // Retrieve dimensions
959 constexpr unsigned D = PARTICLETYPE::d;
960 constexpr unsigned Drot = utilities::dimensions::convert<D>::rotation;
961
962 // Retrieve rank of direct neighbours
963 auto& listNeighbourRanks = sParticleSystem.getNeighbourRanks();
964
965#ifdef VERBOSE_CONTACT_COMMUNICATION
966 std::cout << "Rank " << singleton::mpi().getRank() << " has the neighbours: ";
967 for (auto& neighbourRank : listNeighbourRanks) {
968 std::cout << neighbourRank << " ";
969 }
970 std::cout << std::endl;
971#endif
972
973 // Setting dimension dependent indices
974 const std::vector<unsigned> indicesID {0};
975 std::vector<unsigned> indicesForce;
976 if constexpr (D == 3) {
977 indicesForce = std::vector<unsigned> {0, 1, 2};
978 }
979 else {
980 indicesForce = std::vector<unsigned> {0, 1};
981 }
982 std::vector<unsigned> indicesTorque;
983 if constexpr (D == 3) {
984 indicesTorque = indicesForce;
985 }
986 else {
987 indicesTorque = std::vector<unsigned> {0};
988 }
989
990 std::array<std::size_t, 1> commGlobalParticleID;
991 Vector<T, D> contactForce;
992 Vector<T, Drot> contactTorque;
993
994 // Create communicatables
995 ConcreteCommunicatable communicatableID =
996 ConcreteCommunicatable(commGlobalParticleID);
997 ConcreteCommunicatable communicatableForce =
998 ConcreteCommunicatable(contactForce);
999 ConcreteCommunicatable communicatableTorque =
1000 ConcreteCommunicatable(contactTorque);
1001
1002 // Retrieve serial size
1003 const std::size_t serialSize = communicatableID.size(indicesID) +
1004 communicatableForce.size(indicesForce) +
1005 communicatableTorque.size(indicesTorque);
1006
1007 // Receive data and iterate buffer
1008 // TODO: Improve processing of data, do not iterate through the all particles every time (maybe also add main iC to the communicated data to reduce processed particles)
1010 listNeighbourRanks, serialSize, contactTreatmentComm, mpiNbHelper,
1011 [&](int rankOrig, std::uint8_t* buffer) {
1012 std::size_t serialIdx = communicatableID.deserialize(indicesID, buffer);
1013 serialIdx +=
1014 communicatableForce.deserialize(indicesForce, &buffer[serialIdx]);
1015 serialIdx +=
1016 communicatableTorque.deserialize(indicesTorque, &buffer[serialIdx]);
1017
1018#ifdef VERBOSE_CONTACT_COMMUNICATION
1019 std::cout << "Rank " << singleton::mpi().getRank()
1020 << " received contact treatment results for particle "
1021 << commGlobalParticleID[0] << " (force = " << contactForce
1022 << "; torque = " << contactTorque << ") from rank "
1023 << rankOrig << std::endl;
1024#endif
1025
1027 T, PARTICLETYPE, conditions::valid_particles>(
1028 sParticleSystem,
1029 [&](Particle<T, PARTICLETYPE>& particle,
1030 ParticleSystem<T, PARTICLETYPE>& particleSystem, int globiC) {
1031 const std::size_t currentGlobalParticleID =
1032 particle.template getField<PARALLELIZATION, ID>();
1033 if (commGlobalParticleID[0] == currentGlobalParticleID) {
1034 // Process contact force
1035 const Vector<T, D> force =
1036 particle.template getField<FORCING, FORCE>();
1037 particle.template setField<FORCING, FORCE>(force +
1038 contactForce);
1039 // Process torque from contact
1040 const Vector<T, Drot> torque(
1041 particle.template getField<FORCING, TORQUE>());
1042 particle.template setField<FORCING, TORQUE>(
1044 torque + contactTorque));
1045 }
1046 });
1047 });
1048}
1049#endif
1050
1051template <typename T, typename PARTICLETYPE>
1054 std::multimap<int, std::unique_ptr<std::uint8_t[]>>& dataMap
1055#ifdef PARALLEL_MODE_MPI
1056 ,
1057 MPI_Comm contactTreatmentComm
1058#endif
1059)
1060{
1061#ifdef PARALLEL_MODE_MPI
1062 if constexpr (access::providesParallelization<PARTICLETYPE>()) {
1063
1064 std::size_t serialSize =
1065 getContactTreatmentResultsSerialSize<T, PARTICLETYPE::d>();
1066
1067 //Create non blocking mpi helper
1069
1070 std::map<int, std::vector<std::uint8_t>> rankDataMapSorted;
1071 communication::fillSendBuffer(dataMap, rankDataMapSorted, serialSize);
1072
1073 // Send mapped data
1075 rankDataMapSorted, particleSystem.getNeighbourRanks(), serialSize,
1076 contactTreatmentComm, mpiNbHelper);
1077
1078 receiveContactTreatmentResults(particleSystem, contactTreatmentComm,
1079 mpiNbHelper);
1080 }
1081#endif
1082}
1083
1084} //namespace contact
1085
1086} //namespace particles
1087
1088} //namespace olb
1089
1090#endif
std::size_t deserialize(ConstSpan< CellID > indices, const std::uint8_t *buffer) override
Deserialize data at locations indices to buffer
std::size_t serialize(ConstSpan< CellID > indices, std::uint8_t *buffer) const override
Serialize data at locations indices to buffer
std::size_t size(ConstSpan< CellID > indices) const override
Get serialized size for data at locations indices
class for marking output with some text
Representation of a statistic for a parallel 2D geometry.
CuboidGeometry< T, D > & getCuboidGeometry()
Read and write access to cuboid geometry.
LoadBalancer< T > & getLoadBalancer()
Read and write access to the load balancer.
Plain old scalar vector.
Definition vector.h:47
SuperStructure< T, PARTICLETYPE::d > & getSuperStructure()
Particle< T, PARTICLETYPE > get(std::size_t globalParticleID)
const std::unordered_set< int > & getNeighbourRanks()
void send(T *buf, int count, int dest, int tag=0, MPI_Comm comm=MPI_COMM_WORLD)
Sends data at *buf, blocking.
void bCast(T *sendBuf, int sendCount, int root=0, MPI_Comm comm=MPI_COMM_WORLD)
Broadcast data from one processor to multiple processors.
int getSize() const
Returns the number of processes.
bool isMainProcessor() const
Tells whether current processor is main processor.
int getRank() const
Returns the process ID.
int bossId() const
Returns process ID of main processor.
void receive(T *buf, int count, int source, int tag=0, MPI_Comm comm=MPI_COMM_WORLD)
Receives data at *buf, blocking.
Helper class for non blocking MPI communication.
Definition mpiManager.h:51
Vector< T, PARTICLETYPE::d > getPosition(Particle< T, PARTICLETYPE > particle)
void receiveAndExecuteForData(const std::unordered_set< int > &availableRanks, std::size_t serialSize, MPI_Comm Comm, singleton::MpiNonBlockingHelper &mpiNbHelper, F f)
Definition relocation.h:572
std::unordered_set< int > getSurfaceTouchingICs(CuboidGeometry< T, D > &cuboidGeometry, Vector< T, D > position, T circumRadius, const Vector< bool, D > &periodicity, int globiC, F f=[](int){})
Get a set of surface touching iCs (that are not globiC) Allows to run an optional function per unique...
void sendMappedData(std::map< int, std::vector< std::uint8_t > > &rankDataMapSorted, const std::unordered_set< int > &availableRanks, const std::size_t serialSize, MPI_Comm Comm, singleton::MpiNonBlockingHelper &mpiNbHelper)
Definition relocation.h:408
bool getCuboid(const CuboidGeometry< T, D > &cuboidGeometry, const Vector< bool, D > &periodicity, const PhysR< T, D > &position, int &iC)
Function returns true if cuboid was found and gives iC.
Definition utilities.h:320
void fillSendBuffer(std::multimap< int, std::unique_ptr< std::uint8_t[]> > &rankDataMap, std::map< int, std::vector< std::uint8_t > > &rankDataMapSorted, std::size_t serialSize)
Definition relocation.h:387
void forParticlesInSuperParticleSystem(SuperParticleSystem< T, PARTICLETYPE > &sParticleSystem, F f)
void unifyPositions(Particle< T, PARTICLETYPE > &particle1, Particle< T, PARTICLETYPE > &particle2, const PhysR< T, PARTICLETYPE::d > &cellMin, const PhysR< T, PARTICLETYPE::d > &cellMax, F getSetupPeriodicity, std::array< PhysR< T, PARTICLETYPE::d >, 2 > &positions, T deltaX)
void accountForPeriodicParticleBoundary(SuperParticleSystem< T, PARTICLETYPE > &sParticleSystem, contact::ContactContainer< T, PARTICLECONTACTTYPE, WALLCONTACTTYPE > &contactContainer, const SuperGeometry< T, PARTICLETYPE::d > &sGeometry, F getSetupPeriodicity)
update positions in contact to account for periodic boundaries
void receiveContactTreatmentResults(SuperParticleSystem< T, PARTICLETYPE > &sParticleSystem, MPI_Comm contactTreatmentComm, singleton::MpiNonBlockingHelper &mpiNbHelper)
void cleanContacts(std::vector< CONTACTTYPE > &contacts)
void resetResponsibleRank(CONTACTTYPE &contact)
bool particleContactConsistsOfIDs(PARTICLECONTACTTYPE &particleContact, const std::array< size_t, 2 > &ids)
bool isResponsibleRankSet(CONTACTTYPE &contact)
void receiveContact(ParticleContactArbitraryFromOverlapVolume< T, PARTICLETYPE::d, CONVEX > &newcontact, SuperParticleSystem< T, PARTICLETYPE > &sParticleSystem, std::vector< CONTACTTYPE > &contacts, int rankOrig)
void communicateContactTreatmentResults(SuperParticleSystem< T, PARTICLETYPE > &particleSystem, std::multimap< int, std::unique_ptr< std::uint8_t[]> > &dataMap, MPI_Comm contactTreatmentComm)
PhysR< T, PARTICLETYPE::d > evalContactPosition(Particle< T, PARTICLETYPE > &particle, const PhysR< T, PARTICLETYPE::d > &particlePos, const PhysR< T, PARTICLETYPE::d > &contactPos, const PhysR< T, PARTICLETYPE::d > &cellMin, const PhysR< T, PARTICLETYPE::d > &cellMax, F getSetupPeriodicity, T deltaX)
void communicateParallelContacts(std::vector< CONTACTTYPE > &contacts, SuperParticleSystem< T, PARTICLETYPE > &sParticleSystem, const T deltaX, MPI_Comm contactDetectionComm, const Vector< bool, PARTICLETYPE::d > &periodicity)
T evalCircumRadius(T const contactDetectionDistance, T const circumRadius, T const epsilon)
void communicateContactsParallel(ContactContainer< T, PARTICLECONTACTTYPE, WALLCONTACTTYPE > &contactContainer, SuperParticleSystem< T, PARTICLETYPE > &sParticleSystem, const T deltaX, MPI_Comm particleContactDetectionComm, MPI_Comm wallContactDetectionComm, const Vector< bool, PARTICLETYPE::d > &periodicity)
void communicatePostContactTreatmentContacts(std::vector< CONTACTTYPE > &contacts, SuperParticleSystem< T, PARTICLETYPE > &sParticleSystem, const T deltaX, MPI_Comm postContactTreatmentComm, const Vector< bool, PARTICLETYPE::d > &periodicity)
void extendContactTreatmentResultsDataMap(const std::size_t &globalParticleID, Vector< T, D > &force, Vector< T, utilities::dimensions::convert< D >::rotation > &torque, const int destRank, std::multimap< int, std::unique_ptr< std::uint8_t[]> > &dataMap)
auto getContactIterator(std::vector< CONTACTTYPE > &contacts, const std::function< bool(const CONTACTTYPE &)> condition)
PhysR< T, PARTICLETYPE::d > unifyPosition(Particle< T, PARTICLETYPE > &particle, const PhysR< T, PARTICLETYPE::d > &cellMin, const PhysR< T, PARTICLETYPE::d > &cellMax, F getSetupPeriodicity, T deltaX)
std::unordered_set< int > evalDestRanksForDetectionCommunication(ParticleContactArbitraryFromOverlapVolume< T, PARTICLETYPE::d, CONVEX > &contact, SuperParticleSystem< T, PARTICLETYPE > &sParticleSystem, const T deltaX, const Vector< bool, PARTICLETYPE::d > &periodicity)
evaluate responsible ranks for particle-particle contact
std::unordered_set< int > evalDestRanksForPostContactTreatmentCommunication(ParticleContactArbitraryFromOverlapVolume< T, PARTICLETYPE::d, CONVEX > &contact, SuperParticleSystem< T, PARTICLETYPE > &sParticleSystem, const T deltaX, const Vector< bool, PARTICLETYPE::d > &periodicity)
evaluate ranks that touch both particles of a particle-particle contact
void communicateContacts(ContactContainer< T, PARTICLECONTACTTYPE, WALLCONTACTTYPE > &contactContainer)
MpiManager & mpi()
Top level namespace for all of OpenLB.
std::vector< PARTICLECONTACTTYPE > particleContacts
resizeable vector containing all particle-particle contacts
void cleanContacts()
Clean contacts - remove "empty" contacts.
std::vector< WALLCONTACTTYPE > wallContacts
resizeable vector containg all particle-wall contacts
void combineContacts()
Combine contacts with same ids.
An object holding data for a contact which is described analog to Nassauer and Kuna (2013)
constexpr const std::array< std::size_t, 2 > & getIDs() const
Read access to particle IDs.
constexpr const int & getResponsibleRank() const
Read access to the responsible rank.
constexpr void resetMinMax()
Reset min and max to default values.
constexpr void setResponsibleRank(const int &rank)
Set processor that is responsible for contact treatment.
constexpr void setResponsibleRank(const int &rank)
Set processor that is responsible for contact treatment.
constexpr const std::size_t & getParticleID() const
Read access to particle ID.
constexpr unsigned getWallID() const
Read access to wall matreial.
constexpr const int & getResponsibleRank() const
Read access to the responsible rank.
Converts dimensions by deriving from given cartesian dimension D.