OpenLB 1.7
Loading...
Searching...
No Matches
particleContactForceFunctions.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 contact force calculation.
26*/
27
28#ifndef PARTICLE_CONTACT_FORCE_FUNCTIONS_H
29#define PARTICLE_CONTACT_FORCE_FUNCTIONS_H
30
31#include "core/vector.h"
33#include "particles/particles.h"
34
35namespace olb {
36
37namespace particles {
38
39namespace defaults {
40template <typename T, unsigned D>
42 [](const PhysR<T, D>& contactPosition, const Vector<T, D>& contactNormal,
43 const Vector<T, D>& normalForce, const Vector<T, D>& tangentialForce,
44 const std::array<std::size_t, 2>& ids, T overlapVolume, T indentation,
45 bool isParticleWallContact) {};
46}
47
48namespace contact {
49
50template <typename T, unsigned D, typename CONTACTTYPE>
51constexpr T
52evalCurrentDampingFactor(CONTACTTYPE& contact, const T coefficientOfRestitution,
53 const Vector<T, D>& initialRelativeNormalVelocity)
54{
55 if (contact.getDampingFactor() < 0) {
56 contact.setDampingFactorFromInitialVelocity(
57 coefficientOfRestitution, norm(initialRelativeNormalVelocity));
58 }
59 return contact.getDampingFactor();
60}
61
63template <typename T, typename PARTICLETYPE>
72
73template <typename T, typename PARTICLETYPE>
80
81template <typename T, unsigned D>
82constexpr Vector<T, D>
84 const Vector<T, D>& relVel)
85{
86 return Vector<T, D>((relVel * contactNormal) * contactNormal);
87}
88
89template <typename T, typename F>
90constexpr void forEachPosition(Vector<T, 3> startPos, Vector<T, 3> endPos, F f)
91{
92 Vector<T, 3> pos;
93 for (pos[0] = startPos[0]; pos[0] <= endPos[0]; ++pos[0]) {
94 for (pos[1] = startPos[1]; pos[1] <= endPos[1]; ++pos[1]) {
95 for (pos[2] = startPos[2]; pos[2] <= endPos[2]; ++pos[2]) {
96 f(pos);
97 }
98 }
99 }
100}
101
102template <typename T, typename F>
103constexpr void forEachPosition(Vector<T, 2> startPos, Vector<T, 2> endPos, F f)
104{
105 Vector<T, 2> pos;
106 for (pos[0] = startPos[0]; pos[0] <= endPos[0]; ++pos[0]) {
107 for (pos[1] = startPos[1]; pos[1] <= endPos[1]; ++pos[1]) {
108 f(pos);
109 }
110 }
111}
112
113template <typename T, typename F>
115 Vector<T, 3> endPos, F f)
116{
117 bool breakLoops = false;
118 Vector<T, 3> pos;
119 for (pos[0] = startPos[0]; pos[0] <= endPos[0]; ++pos[0]) {
120 for (pos[1] = startPos[1]; pos[1] <= endPos[1]; ++pos[1]) {
121 for (pos[2] = startPos[2]; pos[2] <= endPos[2]; ++pos[2]) {
122 f(pos, breakLoops);
123 if (breakLoops) {
124 return;
125 }
126 }
127 }
128 }
129}
130
131template <typename T, typename F>
133 Vector<T, 2> endPos, F f)
134{
135 bool breakLoops = false;
136 Vector<T, 2> pos;
137 for (pos[0] = startPos[0]; pos[0] <= endPos[0]; ++pos[0]) {
138 for (pos[1] = startPos[1]; pos[1] <= endPos[1]; ++pos[1]) {
139 f(pos, breakLoops);
140 if (breakLoops) {
141 return;
142 }
143 }
144 }
145}
146template <typename T, unsigned D>
147constexpr Vector<T, D>
149 const T physDeltaX,
150 const unsigned contactBoxResolutionPerDirection)
151{
152 // Adaptiv resolution of found contact
153 const Vector<T, D> boxSize = max - min;
154 Vector<T, D> contactPhysDeltaX = boxSize;
155 std::array<bool, D> fallbackUsed {[]() {
156 static_assert(D == 2 || D == 3, "Only D=2 and D=3 are supported");
157 if constexpr (D == 3) {
158 return std::array<bool, D> {false, false, false};
159 }
160 else {
161 return std::array<bool, D> {false, false};
162 }
163 }()};
164
165 for (unsigned iD = 0; iD < D; ++iD) {
166 contactPhysDeltaX[iD] /= contactBoxResolutionPerDirection;
167 if (util::nearZero(contactPhysDeltaX[iD])) {
168 contactPhysDeltaX[iD] = physDeltaX / contactBoxResolutionPerDirection;
169 fallbackUsed[iD] = true;
170 }
171 }
172
173 // If fallback was used, check if in another dimension the deltaX is known.
174 // If yes, then use the smallest known deltaX. Otherwise, keep the fallback.
175 // This is possible because any dimenison for which we cannot find a proper deltaX has probably even smaller lenghts than the others.
176 for (unsigned iD = 0; iD < D; ++iD) {
177 if (fallbackUsed[iD]) {
178 for (unsigned iD2 = 0; iD2 < D; ++iD2) {
179 if (iD != iD2) {
180 contactPhysDeltaX[iD] =
181 util::min(contactPhysDeltaX[iD], contactPhysDeltaX[iD2]);
182 }
183 }
184 }
185 }
186
187 return contactPhysDeltaX;
188}
189
190template <typename T, typename PARTICLETYPE, typename PARTICLECONTACTTYPE,
191 typename WALLCONTACTTYPE, bool CONVEX>
194{
195
196 communicateContacts(contactContainer);
197
198 if constexpr (!CONVEX) {
199 OstreamManager clout(std::cout, "forceCalcPrep");
200 // TODO: Split contacts if the points are not connected
201 // How to remember properties then? Store old min and max (maybe with a little extra) and then check for to which contact a position belongs?
202 clout << "WARNING: Concave particles aren't supported." << std::endl;
203 }
204}
205
207template <typename T, unsigned D>
208Vector<T, D> calcElasticAndViscousForce(T effectiveE, T k, T overlapVolume,
209 T indentation, T dampingFactor,
210 const Vector<T, D>& relVelNormal,
211 const Vector<T, D>& contactNormal)
212{
213 // Sum is necessary to get the sign of the damping since velocity and normal have the exact same/opposite direction
214 T sum = relVelNormal[0] * contactNormal[0];
215 for (unsigned iD = 1; iD < D; ++iD) {
216 sum += relVelNormal[iD] * contactNormal[iD];
217 }
218
219 const T forceMagnitude =
220 effectiveE * k * util::sqrt(overlapVolume * indentation) *
221 (1 + dampingFactor * util::sign(-sum) * norm(relVelNormal));
222 return util::max(forceMagnitude, T {0}) * contactNormal;
223}
224
226template <typename T, unsigned D>
227T evalApproxSurface(const Vector<T, D>& normalizedNormal,
228 const PhysR<T, D>& contactPhysDeltaX)
229{
230 T approxSurface;
231 if constexpr (D == 3) {
232 approxSurface =
233 contactPhysDeltaX[1] * contactPhysDeltaX[2] *
234 /* util::abs(normalizedNormal[0]); // * normalizedNormal[0]; */
235 normalizedNormal[0] * normalizedNormal[0];
236 approxSurface +=
237 contactPhysDeltaX[0] * contactPhysDeltaX[2] *
238 /* util::abs(normalizedNormal[1]); // * normalizedNormal[1]; */
239 normalizedNormal[1] * normalizedNormal[1];
240 approxSurface +=
241 contactPhysDeltaX[0] * contactPhysDeltaX[1] *
242 /* util::abs(normalizedNormal[2]); // * normalizedNormal[2]; */
243 normalizedNormal[2] * normalizedNormal[2];
244 }
245 else {
246 approxSurface =
247 contactPhysDeltaX[0] *
248 /* util::abs(normalizedNormal[1]); // * normalizedNormal[1]; */
249 normalizedNormal[1] * normalizedNormal[1];
250 approxSurface +=
251 contactPhysDeltaX[1] *
252 /* util::abs(normalizedNormal[0]); // * normalizedNormal[0]; */
253 normalizedNormal[0] * normalizedNormal[0];
254 }
255 return approxSurface;
256 /* return approxSurface / norm(contactPhysDeltaX); */
257}
258
259template <typename T, unsigned D>
261 const Vector<T, D>& contactNormal, const T coefficientStaticFriction,
262 const T coefficientKineticFriction, const T staticKineticTransitionVelocity,
263 const T k, const Vector<T, D>& relVel, const Vector<T, D>& normalForce)
264{
265 // Only regard if static friction is > 0, because we obtain nan otherwise
266 if (coefficientStaticFriction > T {0.}) {
267 const Vector<T, D> relVelNormal =
268 evalRelativeNormalVelocity(contactNormal, relVel);
269 const Vector<T, D> relVelTangential(relVel - relVelNormal);
270 T const magnitudeTangentialVelocity = norm(relVelTangential);
271 if (magnitudeTangentialVelocity != T {0}) {
272 T const velocityQuotient =
273 magnitudeTangentialVelocity / staticKineticTransitionVelocity;
274
275 T magnitudeTangentialForce =
276 2 * coefficientStaticFriction *
277 (1 - T {0.09} * util::pow(coefficientKineticFriction /
278 coefficientStaticFriction,
279 4));
280 magnitudeTangentialForce -= coefficientKineticFriction;
281 magnitudeTangentialForce *= velocityQuotient * velocityQuotient /
282 (util::pow(velocityQuotient, 4) + 1);
283 magnitudeTangentialForce += coefficientKineticFriction;
284 magnitudeTangentialForce -= coefficientKineticFriction /
285 (velocityQuotient * velocityQuotient + 1);
286 magnitudeTangentialForce *= norm(normalForce);
287
288 return -magnitudeTangentialForce * relVelTangential /
289 magnitudeTangentialVelocity;
290 }
291 }
292
293 return Vector<T, D>(T {0.});
294}
295
296template <typename T, unsigned D>
299 const T indentation, const T overlapVolume,
300 const T effectiveE, const T dampingFactor,
301 const T k, const Vector<T, D>& relVel)
302{
303 const Vector<T, D> relVelNormal =
304 evalRelativeNormalVelocity(contactNormal, relVel);
305 return calcElasticAndViscousForce(effectiveE, k, overlapVolume, indentation,
306 dampingFactor, relVelNormal, contactNormal);
307}
308
309template <typename T, typename PARTICLETYPE>
311 std::multimap<int, std::unique_ptr<std::uint8_t[]>>& dataMap,
313 XParticleSystem<T, PARTICLETYPE>& particleSystem,
314 const Vector<T, PARTICLETYPE::d>& contactPoint,
315 const Vector<T, PARTICLETYPE::d>& normalForce,
316 const Vector<T, PARTICLETYPE::d>& tangentialForce)
317{
318 using namespace descriptors;
319 constexpr unsigned D = PARTICLETYPE::d;
320 constexpr unsigned Drot = utilities::dimensions::convert<D>::rotation;
321 Vector<T, D> contactForce = normalForce + tangentialForce;
322 const PhysR<T, D> position = particle.template getField<GENERAL, POSITION>();
323 const PhysR<T, D> contactPointDistance = contactPoint - position;
324 Vector<T, Drot> torqueFromContact(
326 contactForce, contactPointDistance));
327
329 bool applyNow = true;
330
331 if constexpr (access::providesParallelization<PARTICLETYPE>()) {
332 auto& loadBalancer = particleSystem.getSuperStructure().getLoadBalancer();
333 const std::size_t globalParticleIC =
334 particle.template getField<PARALLELIZATION, IC>();
335 int responsibleRank = loadBalancer.rank(globalParticleIC);
336 if (responsibleRank != singleton::mpi().getRank()) {
337 applyNow = false;
338 const std::size_t globalParticleID =
339 particle.template getField<PARALLELIZATION, ID>();
340 extendContactTreatmentResultsDataMap(globalParticleID, contactForce,
341 torqueFromContact, responsibleRank,
342 dataMap);
343 }
344 }
345
346 if (applyNow) {
347 if constexpr (access::providesParallelization<PARTICLETYPE>()) {
348 // Apply force only to the main particle
349 const std::size_t globalParticleID =
350 particle.template getField<PARALLELIZATION, ID>();
352 T, PARTICLETYPE, conditions::valid_particles>(
353 particleSystem,
354 [&](Particle<T, PARTICLETYPE>& particle,
355 ParticleSystem<T, PARTICLETYPE>& particleSystem, int globiC) {
356 const int globalParticleIC =
357 particle.template getField<PARALLELIZATION, IC>();
358 const std::size_t currGlobalParticleID =
359 particle.template getField<PARALLELIZATION, ID>();
360 if (currGlobalParticleID == globalParticleID &&
361 globiC == globalParticleIC) {
362 // Applying contact force on the particle
363 const Vector<T, D> force =
364 particle.template getField<FORCING, FORCE>();
365 particle.template setField<FORCING, FORCE>(force +
366 contactForce);
367
368 // Torque calculation and application
369 const Vector<T, Drot> torque(
370 particle.template getField<FORCING, TORQUE>());
371 particle.template setField<FORCING, TORQUE>(
373 torque + torqueFromContact));
374#ifdef VERBOSE_CONTACT_COMMUNICATION
375 std::cout << "Rank " << singleton::mpi().getRank()
376 << " procsses data of particle " << globalParticleID
377 << " (contactForce = " << contactForce + force
378 << "; torque = " << torqueFromContact + torque << ")"
379 << std::endl;
380#endif
381 }
382 });
383 }
384 else {
385 // Applying contact force on the particle
386 const Vector<T, D> force = particle.template getField<FORCING, FORCE>();
387 particle.template setField<FORCING, FORCE>(force + contactForce);
388
389 // Torque calculation and application
390 const Vector<T, Drot> torque(
391 particle.template getField<FORCING, TORQUE>());
392 particle.template setField<FORCING, TORQUE>(
394 torque + torqueFromContact));
395 }
396 }
397 }
398}
399
400template <
401 typename T, typename PARTICLETYPE, typename CONTACTPROPERTIES,
402 typename F = decltype(defaults::processContactForce<T, PARTICLETYPE::d>)>
404 std::multimap<int, std::unique_ptr<std::uint8_t[]>>& dataMap,
406 XParticleSystem<T, PARTICLETYPE>& particleSystem,
407 const PhysR<T, PARTICLETYPE::d>& contactPoint,
408 const Vector<T, PARTICLETYPE::d>& contactNormal, const T indentation,
409 const T overlapVolume, const Vector<T, PARTICLETYPE::d>& relVel,
410 const T dampingFactor, const CONTACTPROPERTIES& contactProperties, T k,
411 const std::size_t& particleAID, const std::size_t& particleBID,
412 F processContactForce = defaults::processContactForce<T, PARTICLETYPE::d>)
413{
414 using namespace descriptors;
415 constexpr unsigned D = PARTICLETYPE::d;
416
417 const unsigned materialA =
418 particleA.template getField<MECHPROPERTIES, MATERIAL>();
419 const unsigned materialB =
420 particleB.template getField<MECHPROPERTIES, MATERIAL>();
421
422 // Calculation of contact force
424 contactNormal, indentation, overlapVolume,
425 contactProperties.getEffectiveYoungsModulus(materialA, materialB),
426 dampingFactor, k, relVel);
428 contactNormal,
429 contactProperties.getStaticFrictionCoefficient(materialA, materialB),
430 contactProperties.getKineticFrictionCoefficient(materialA, materialB),
431 contactProperties.getStaticFrictionCoefficient(materialA, materialB), k,
432 relVel, normalForce);
433
434 processContactForce(contactPoint, contactNormal, normalForce, tangentialForce,
435 std::array<std::size_t, 2>({particleAID, particleBID}),
436 overlapVolume, indentation, false);
437
438 // Applying forces on the particle
439 applyForceOnParticle(dataMap, particleA, particleSystem, contactPoint,
440 normalForce, tangentialForce);
441 applyForceOnParticle(dataMap, particleB, particleSystem, contactPoint,
442 -1 * normalForce, -1 * tangentialForce);
443}
444
445template <
446 typename T, typename PARTICLETYPE, typename CONTACTPROPERTIES,
447 typename F = decltype(defaults::processContactForce<T, PARTICLETYPE::d>)>
449 std::multimap<int, std::unique_ptr<std::uint8_t[]>>& dataMap,
451 XParticleSystem<T, PARTICLETYPE>& particleSystem,
453 const PhysR<T, PARTICLETYPE::d>& contactPoint,
454 const Vector<T, PARTICLETYPE::d>& contactNormal, const T indentation,
455 const T overlapVolume, const Vector<T, PARTICLETYPE::d>& relVel,
456 const T dampingFactor, const CONTACTPROPERTIES& contactProperties, T k,
457 const std::size_t& particleID, const std::size_t& wallID,
458 F processContactForce = defaults::processContactForce<T, PARTICLETYPE::d>)
459{
460 using namespace descriptors;
461 constexpr unsigned D = PARTICLETYPE::d;
462
463 const unsigned particleMaterial =
464 particle.template getField<MECHPROPERTIES, MATERIAL>();
465 const unsigned wallMaterial = solidBoundary.getContactMaterial();
466
467 // Calculation of contact force
469 contactNormal, indentation, overlapVolume,
470 contactProperties.getEffectiveYoungsModulus(particleMaterial,
471 wallMaterial),
472 dampingFactor, k, relVel);
474 contactNormal,
475 contactProperties.getStaticFrictionCoefficient(particleMaterial,
476 wallMaterial),
477 contactProperties.getKineticFrictionCoefficient(particleMaterial,
478 wallMaterial),
479 contactProperties.getStaticKineticTransitionVelocity(particleMaterial,
480 wallMaterial),
481 k, relVel, normalForce);
482
483 processContactForce(contactPoint, contactNormal, normalForce, tangentialForce,
484 std::array<std::size_t, 2>({particleID, wallID}),
485 overlapVolume, indentation, true);
486
487 // Applying contact forces on the particle
488 applyForceOnParticle(dataMap, particle, particleSystem, contactPoint,
489 normalForce, tangentialForce);
490}
491
492template <typename T, unsigned D>
494 const PhysR<T, D>& max, const PhysR<T, D>& min,
495 const PhysR<T, D>& contactPhysDeltaX)
496{
497 for (unsigned iD = 0; iD < D; ++iD) {
498 startPos[iD] = util::floor(min[iD] / contactPhysDeltaX[iD]);
499 endPos[iD] = util::ceil(max[iD] / contactPhysDeltaX[iD]);
500 }
501}
502
503template <typename T, unsigned D>
505 const Vector<T, D>& max, T overlapVolume)
506{
507 const T a = util::log(2. / 3.) / util::log(2.);
508 const T m = 2. / util::sqrt(M_PI) * util::pow(4. / M_PI, -a);
509
510 Vector<T, D> length = max - min;
511 T boundingBoxVolume = 1;
512 for (unsigned iD = 0; iD < D; ++iD) {
513 boundingBoxVolume *= length[iD];
514 }
515
516 const T k =
517 m * util::pow(util::max(boundingBoxVolume / overlapVolume, T {1}), a);
518 return k;
519}
520
521template <typename T>
522constexpr T evalForceViaVolumeCoefficient(const T contactVolume,
523 const T contactSurface,
524 const T indentationDepth)
525{
526 constexpr T a = 0.6118229;
527 constexpr T b = 1.900093;
528 constexpr T c = 0.651237;
529
530 const T x = indentationDepth * contactSurface / contactVolume;
531 const T k = a + b * util::exp(-c * x);
532 return k;
533}
534
535template <typename T, unsigned D, typename F1, typename F2, typename F3,
536 typename F4, typename F5>
537void processCell(const PhysR<T, D>& pos, const PhysR<T, D>& contactPhysDeltaX,
538 PhysR<T, D>& center, Vector<T, D>& contactNormal,
539 unsigned& volumeCellCount, T& surfaceCellCount, F1 isInside,
540 F2 isOnSurface, F3 calcNormalA, F4 calcNormalB,
541 F5 updateMinMax)
542{
543 // outside for normal calculation
544 if (!isInside(pos)) {
545 bool onSurfaceA = false, onSurfaceB = false;
546 // calc normals
547 Vector<T, D> normalA, normalB;
548 // TODO: Improve mesh size
549 T const meshSize = 1e-8 * util::average(contactPhysDeltaX);
550 normalA = calcNormalA(pos, meshSize);
551 normalB = calcNormalB(pos, meshSize);
552 if (!util::nearZero(norm(normalA))) {
553 normalA = normalize(normalA);
554 }
555 if (!util::nearZero(norm(normalB))) {
556 normalB = normalize(normalB);
557 }
558
559 if (isOnSurface(pos, contactPhysDeltaX, onSurfaceA, onSurfaceB, normalA,
560 normalB)) {
561 T approxSurface;
562 if (!util::nearZero(norm(normalA)) && onSurfaceA) {
563 approxSurface = evalApproxSurface(normalA, contactPhysDeltaX);
564 surfaceCellCount += approxSurface;
565 contactNormal += normalA * approxSurface;
566 }
567 if (!util::nearZero(norm(normalB)) && onSurfaceB) {
568 approxSurface = evalApproxSurface(normalB, contactPhysDeltaX);
569 surfaceCellCount += approxSurface;
570 contactNormal -= normalB * approxSurface;
571 }
572 }
573 }
574 // inside volume calculation
575 else {
576 ++volumeCellCount;
577
578 for (unsigned iD = 0; iD < D; ++iD) {
579 center[iD] += pos[iD];
580 }
581 // Updating min and max for next sub-timestep
582 updateMinMax(pos);
583 }
584}
585
586template <typename T, unsigned D, typename F1, typename F2, typename F3,
587 typename F4>
589 T physDeltaX,
590 unsigned contactBoxResolutionPerDirection,
591 F1 resetContactBox, F2 processCellWrapped,
592 F3 calculateIndentation, F4 applyForce)
593{
594 Vector<T, D> contactPhysDeltaX =
595 evalContactDeltaX(min, max, physDeltaX, contactBoxResolutionPerDirection);
596
597 // direction of force
598 Vector<T, D> contactNormal(T(0.));
599 // contact point
600 Vector<T, D> center(T(0.));
601 // number of cells in contact volume
602 unsigned volumeCellCount = 0;
603 // number of cells in contact surface
604 T surfaceCellCount = 0.;
605 // volume of cell describing the contact
606 T infinitesimalVolume = contactPhysDeltaX[0] * contactPhysDeltaX[1];
607 if constexpr (D == 3) {
608 infinitesimalVolume *= contactPhysDeltaX[2];
609 }
610
611 Vector<long, D> startPos;
612 Vector<long, D> endPos;
613 evalStartAndEndPos(startPos, endPos, max, min, contactPhysDeltaX);
614 // Increase the box 2*deltaX in each direction
615 startPos -= Vector<long, D>(1);
616 endPos += Vector<long, D>(1);
617
618 resetContactBox();
619
620 forEachPosition(startPos, endPos, [&](const Vector<long, D>& pos) {
621 Vector<T, D> physPos;
622 for (unsigned iD = 0; iD < D; ++iD) {
623 physPos[iD] = pos[iD] * contactPhysDeltaX[iD];
624 }
625 processCellWrapped(contactNormal, center, volumeCellCount, surfaceCellCount,
626 physPos, contactPhysDeltaX);
627 });
628
629 if (!util::nearZero(norm(contactNormal))) {
630 if (volumeCellCount > 0 && surfaceCellCount > 0) {
631 // finishing center calculation
632 center /= volumeCellCount;
633 // finishing normal calculation
634 contactNormal = normalize(contactNormal);
635 // calculate precision depending on the size of the overlap
636 T precision {0};
637 for (unsigned iD = 0; iD < D; ++iD) {
638 precision += util::pow(contactNormal[iD] * contactPhysDeltaX[iD], 2);
639 }
640 precision = T {1e-2} * util::sqrt(precision / D);
641 const T indentation =
642 calculateIndentation(center, contactNormal, precision);
643 // currently, the normal points outside the particle (because we use SDF), but the force acts in the opposite way
644 contactNormal *= -1;
645 // Avoid indentation < 0
646 if (indentation > 0) {
647 applyForce(center, contactNormal, util::max(indentation, T {0}),
648 volumeCellCount * infinitesimalVolume);
649 }
650 }
651 }
652#ifdef OLB_DEBUG
653 else {
654 OstreamManager clout(std::cout, "contactProcessing");
655 clout << "WARNING: The contact normal's norm is zero. Therefore, the "
656 "detected contact is ignored."
657 << std::endl;
658 }
659#endif
660}
661
662template <typename T, unsigned D, typename F1>
664 const Vector<long, D>& endPos,
665 const Vector<T, D>& contactPhysDeltaX,
666 Vector<long, D>& currPos, F1 isInsideContact)
667{
668 bool pointFound = false;
669
670 forEachPositionWithBreak(startPos, endPos,
671 [&](const Vector<long, D>& pos, bool& breakLoops) {
672 PhysR<T, D> physPos;
673 for (unsigned iD = 0; iD < D; ++iD) {
674 physPos[iD] = contactPhysDeltaX[iD] * pos[iD];
675 }
676 if (isInsideContact(physPos)) {
677 currPos = pos;
678 pointFound = true;
679 breakLoops = true;
680 }
681 });
682
683 return pointFound;
684}
685
686template <typename T, unsigned D, typename F1, typename F2>
688 const Vector<long, D>& endPos,
689 const Vector<T, D>& contactPhysDeltaX,
690 const Vector<long, D>& currPos,
691 const Vector<short, D>& dirIn, F1 isInsideContact,
692 F2 updateMinMax)
693{
694 PhysR<T, D> currPhysPos;
695 for (unsigned iD = 0; iD < D; ++iD) {
696 currPhysPos[iD] = contactPhysDeltaX[iD] * currPos[iD];
697 }
698 bool isInside = isInsideContact(currPhysPos);
699 bool isInsideBB = true;
700 for (unsigned iD = 0; iD < D; ++iD) {
701 if (currPos[iD] < startPos[iD] || currPos[iD] > endPos[iD]) {
702 isInsideBB = false;
703 break;
704 }
705 }
706
707 // if point is inside, then we check the neighboring points too, otherwise they cannot be inside for convex shapes
708 if (isInside || isInsideBB) {
709 if (isInside) {
710 updateMinMax(currPhysPos);
711 }
712
715 [&](const Vector<unsigned short, D>& dirOut) {
716 if (norm(dirOut) >= 1) {
717 Vector<short, D> signedDirOut;
718 for (unsigned iD = 0; iD < D; ++iD) {
719 signedDirOut[iD] = dirOut[iD] * dirIn[iD];
720 }
721 const Vector<long, D> nextPos = signedDirOut + currPos;
723 startPos, endPos, contactPhysDeltaX, nextPos,
724 signedDirOut, isInsideContact, updateMinMax);
725 }
726 });
727 }
728}
729
730template <typename T, unsigned D, typename F1, typename F2, typename F3>
731void correctBoundingBox(const PhysR<T, D>& min, const PhysR<T, D>& max,
732 const T physDeltaX,
733 const unsigned contactBoxResolutionPerDirection,
734 F1 isInsideContact, F2 resetContactBox, F3 updateMinMax)
735{
736 const Vector<T, D> contactPhysDeltaX =
737 evalContactDeltaX(min, max, physDeltaX, contactBoxResolutionPerDirection);
738 Vector<long, D> startPos;
739 Vector<long, D> endPos;
740 evalStartAndEndPos(startPos, endPos, max, min, contactPhysDeltaX);
741 resetContactBox();
742
743 Vector<long, D> origin;
744 const bool originFound = evalStartingPoint(
745 startPos, endPos, contactPhysDeltaX, origin, isInsideContact);
746
747 if (originFound) {
748 PhysR<T, D> physOrigin;
749 for (unsigned iD = 0; iD < D; ++iD) {
750 physOrigin[iD] = contactPhysDeltaX[iD] * origin[iD];
751 }
752 updateMinMax(physOrigin);
753
754 for (unsigned i = 0; i < utilities::dimensions::convert<D>::neighborsCount;
755 ++i) {
756 const Vector<short, D> direction =
758 evalBoundingBoxRecursive(startPos, endPos, contactPhysDeltaX,
759 origin + direction, direction, isInsideContact,
761 }
762 }
763}
764
765template <typename T, unsigned D, typename F1, typename F2, typename F3>
767 T physDeltaX,
768 unsigned contactBoxResolutionPerDirection,
769 F1 signedDistanceToIntersection,
770 F2 resetContactBox, F3 updateMinMax)
771{
772 Vector<T, D> contactPhysDeltaX =
773 evalContactDeltaX(min, max, physDeltaX, contactBoxResolutionPerDirection);
774
775 for (unsigned iD = 0; iD < D; ++iD) {
776 if (util::nearZero(min[iD] - max[iD])) {
777 min[iD] -= contactPhysDeltaX[iD];
778 /* T {0.5} * contactBoxResolutionPerDirection * contactPhysDeltaX[iD]; */
779 max[iD] += contactPhysDeltaX[iD];
780 /* T {0.5} * contactBoxResolutionPerDirection * contactPhysDeltaX[iD]; */
781 }
782 }
783
784 Vector<long, D> startPos;
785 Vector<long, D> endPos;
786 evalStartAndEndPos(startPos, endPos, max, min, contactPhysDeltaX);
787 resetContactBox();
788
789 forEachPosition(startPos, endPos, [&](const Vector<long, D>& pos) {
790 bool isOnContactSurface = false;
791 for (unsigned iD = 0; iD < D; ++iD) {
792 if (pos[iD] == startPos[iD] || pos[iD] == endPos[iD]) {
793 isOnContactSurface = true;
794 break;
795 }
796 }
797
798 if (isOnContactSurface) {
799 // Determine physical position
800 PhysR<T, D> physPos;
801 for (unsigned iD = 0; iD < D; ++iD) {
802 physPos[iD] = pos[iD] * contactPhysDeltaX[iD];
803 }
804
805 for (unsigned i = 0;
806 i < utilities::dimensions::convert<D>::neighborsCount; ++i) {
807 // do not regard direction if the neighbor is also on the surface
808 bool regardDirection = true;
809 //const Vector<long,D> neighborPos = pos + direction;
810 const Vector<long, D> neighborPos =
811 pos + Vector<long, D>(
813 for (unsigned iD = 0; iD < D; ++iD) {
814 if (neighborPos[iD] <= startPos[iD] ||
815 neighborPos[iD] >= endPos[iD]) {
816 regardDirection = false;
817 break;
818 }
819 }
820
821 if (regardDirection) {
822 const PhysR<T, D> normalizedDirection =
824 D>::template normalizedNeighborDirections<T>[i]);
825 T distance;
826
827 // TODO: the sdf from intersections is not exact - replace with another option
828 // Only change min & max if boundary was found
829 /* if (util::distance( */
830 /* distance, physPos, direction, physDeltaX * T {1e-2}, */
831 /* [&](const Vector<T, D>& pos) { */
832 /* return signedDistanceToIntersection(pos); */
833 /* }, */
834 /* [&](const Vector<T, D>& pos) { */
835 /* return signedDistanceToIntersection(pos) < physDeltaX; */
836 /* })) { */
837 if (util::distance<T, D, false>(
838 distance, physPos, normalizedDirection, physDeltaX * T {1e-2},
839 [&](const Vector<T, D>& pos) {
840 return signedDistanceToIntersection(pos);
841 },
842 physDeltaX)) {
843 // Determine position of boundary in given direction
844 const PhysR<T, D> posOnBoundary =
845 physPos + distance * normalizedDirection;
846
847 // Attempt to update min and max with found position on contact boundary
848 updateMinMax(posOnBoundary);
849 }
850 }
851 }
852 }
853 });
854}
855
856template <typename T, typename PARTICLETYPE, typename PARTICLECONTACTTYPE,
857 typename WALLCONTACTTYPE, unsigned BBCORRECTIONMETHOD = 0,
858 bool CONVEX = true, bool useSDF = true>
860
861template <typename T, typename PARTICLETYPE, typename WALLCONTACTTYPE,
862 unsigned BBCORRECTIONMETHOD, bool CONVEX, bool useSDF>
864 T, PARTICLETYPE,
865 ParticleContactArbitraryFromOverlapVolume<T, PARTICLETYPE::d, CONVEX>,
866 WALLCONTACTTYPE, BBCORRECTIONMETHOD, CONVEX, useSDF> {
867 static void resetContainer(
869 T,
871 WALLCONTACTTYPE>& contactContainer)
872 {
873 for (auto& contact : contactContainer.particleContacts) {
874 contact.resetMinMax();
875 }
876 }
877
878 static void
880 CONVEX>& contact,
881 Particle<T, PARTICLETYPE>& particleA,
882 Particle<T, PARTICLETYPE>& particleB,
883 Vector<T, PARTICLETYPE::d>& contactNormal,
884 Vector<T, PARTICLETYPE::d>& center, unsigned& volumeCellCount,
885 T& surfaceCellCount, const Vector<T, PARTICLETYPE::d>& pos,
886 const Vector<T, PARTICLETYPE::d>& contactPhysDeltaX)
887 {
888 constexpr unsigned D = PARTICLETYPE::d;
889
890 // Check if pos is inside contact
891 const std::function<bool(const PhysR<T, D>&)> isInside =
892 [&](const PhysR<T, D>& pos) {
893 bool const isInsideParticleA =
896 bool const isInsideParticleB =
899 return isInsideParticleA && isInsideParticleB;
900 };
901
902 // Check if pos is on surface
903 const std::function<bool(const PhysR<T, D>&, const PhysR<T, D>&, bool&,
904 bool&, const Vector<T, D>&, const Vector<T, D>&)>
905 isOnSurface = [&](const PhysR<T, D>& pos,
906 const PhysR<T, D>& contactPhysDeltaX,
907 bool& onSurfaceA, bool& onSurfaceB,
908 const Vector<T, D>& normalA,
909 const Vector<T, D>& normalB) {
910 Vector<T, D> neighbor;
911 for (unsigned iD = 0; iD < D; ++iD) {
912 neighbor[iD] = -normalA[iD] * contactPhysDeltaX[iD];
913 }
915 particleA, pos + neighbor) <=
917 for (unsigned iD = 0; iD < D; ++iD) {
918 neighbor[iD] = -normalB[iD] * contactPhysDeltaX[iD];
919 }
921 particleB, pos + neighbor) <=
923 return onSurfaceA || onSurfaceB;
924 };
925
926 // Calculate normal to particle surface
927 const std::function<Vector<T, D>(const PhysR<T, D>&, const T)> calcNormalA =
928 [&](const PhysR<T, D>& pos, const T meshSize) {
929 // also with an enlarged particle, the normal should still point the same way, since we apply an equal layer to the whole surface
931 meshSize);
932 };
933 // Calculate normal to indicator surface
934 const std::function<Vector<T, D>(const PhysR<T, D>&, const T)> calcNormalB =
935 [&](const PhysR<T, D>& pos, const T meshSize) {
936 // also with an enlarged particle, the normal should still point the same way, since we apply an equal layer to the whole surface
938 meshSize);
939 };
940 // Wrapper for update min and max
941 const std::function<void(const PhysR<T, D>&)> updateMinMax =
942 [&contact](const PhysR<T, D>& pos) {
943 contact.updateMinMax(pos);
944 };
945
946 particles::contact::processCell(pos, contactPhysDeltaX, center,
947 contactNormal, volumeCellCount,
948 surfaceCellCount, isInside, isOnSurface,
949 calcNormalA, calcNormalB, updateMinMax);
950 }
951
952 template <
953 typename CONTACTPROPERTIES,
954 typename F = decltype(defaults::processContactForce<T, PARTICLETYPE::d>)>
955 static void apply(
956 std::multimap<int, std::unique_ptr<std::uint8_t[]>>& dataMap,
957 XParticleSystem<T, PARTICLETYPE>& particleSystem,
959 contact,
960 const CONTACTPROPERTIES& contactProperties, const T physDeltaX,
961 const unsigned contactBoxResolutionPerDirection, const T k,
962 F processContactForce = defaults::processContactForce<T, PARTICLETYPE::d>)
963 {
964 using namespace descriptors;
965 constexpr unsigned D = PARTICLETYPE::d;
966
967 // indentation depth
968 T indentationDepth = T(0.);
969
970 if (!contact.isEmpty()) {
971 OstreamManager clout(std::cout, "forceApplication");
972 // particles in contact
973 auto particleA = particleSystem.get(contact.getIDs()[0]);
974 auto particleB = particleSystem.get(contact.getIDs()[1]);
975
976 // function to reset min & max of bounding box
977 const std::function<void()> resetMinMax = [&contact]() {
978 contact.resetMinMax();
979 };
980 // wrapper for cell processing to calculate contact point and normal
981 const std::function<void(Vector<T, D>&, Vector<T, D>&, unsigned&, T&,
982 const Vector<T, D>&, const Vector<T, D>&)>
983 processCellWrapped =
984 [&](Vector<T, D>& contactNormal, Vector<T, D>& center,
985 unsigned& volumeCellCount, T& surfaceCellCount,
986 const Vector<T, D>& physPos,
987 const Vector<T, D>& contactPhysDeltaX) {
988 processCell(contact, particleA, particleB, contactNormal,
989 center, volumeCellCount, surfaceCellCount, physPos,
990 contactPhysDeltaX);
991 };
992 // function to calculate indendation depth
993 const std::function<T(const Vector<T, D>&, const Vector<T, D>&, const T)>
994 calculateIndentation = [&](const Vector<T, D>& center,
995 const Vector<T, D>& contactNormal,
996 const T distancePrecision) {
997 const PhysR<T, D> originA =
998 center -
999 contactNormal *
1001 const PhysR<T, D> originB =
1002 center +
1003 contactNormal *
1005 T indentation =
1007 particleA, originA, contactNormal, distancePrecision) +
1009 particleB, originB, -1 * contactNormal, distancePrecision);
1010 return indentation;
1011 };
1012 // function to apply force on particles
1013 const std::function<void(const Vector<T, D>&, const Vector<T, D>&, T, T)>
1014 applyForce = [&](const Vector<T, D>& center,
1015 const Vector<T, D>& contactNormal, T indentation,
1016 T overlapVolume) {
1017 const unsigned materialA =
1018 particleA.template getField<MECHPROPERTIES, MATERIAL>();
1019 const unsigned materialB =
1020 particleB.template getField<MECHPROPERTIES, MATERIAL>();
1021
1022 indentationDepth = indentation;
1023 const Vector<T, D> relVel =
1024 evalRelativeVelocity(particleA, particleB, center);
1025 const T dampingFactor = evalCurrentDampingFactor(
1026 contact,
1027 contactProperties.getCoefficientOfRestitution(materialA,
1028 materialB),
1029 evalRelativeNormalVelocity(contactNormal, relVel));
1030
1032 dataMap, particleA, particleB, particleSystem, center,
1033 contactNormal, indentation, overlapVolume, relVel,
1034 dampingFactor, contactProperties, k, contact.getIDs()[0],
1035 contact.getIDs()[1], processContactForce);
1036 };
1037
1038 processContactViaVolume(contact.getMin(), contact.getMax(), physDeltaX,
1039 contactBoxResolutionPerDirection, resetMinMax,
1040 processCellWrapped, calculateIndentation,
1041 applyForce);
1042 }
1043 }
1044
1046 XParticleSystem<T, PARTICLETYPE>& particleSystem,
1048 contact,
1049 const T physDeltaX, const unsigned contactBoxResolutionPerDirection)
1050 {
1051 using namespace descriptors;
1052 constexpr unsigned D = PARTICLETYPE::d;
1053
1054 auto particleA = particleSystem.get(contact.getIDs()[0]);
1055 auto particleB = particleSystem.get(contact.getIDs()[1]);
1056
1058 contact.getMin(), contact.getMax(), physDeltaX,
1059 contactBoxResolutionPerDirection,
1060 [&](const Vector<T, D>& pos) {
1061 return sdf::intersection(
1062 sdf::rounding(
1063 particles::resolved::signedDistanceToParticle(particleA, pos),
1064 particles::access::getEnlargementForContact(particleA)),
1065 sdf::rounding(
1066 particles::resolved::signedDistanceToParticle(particleB, pos),
1067 particles::access::getEnlargementForContact(particleB)));
1068 },
1069 [&contact]() {
1070 contact.resetMinMax();
1071 },
1072 [&contact](const PhysR<T, D>& pos) {
1073 contact.updateMinMax(pos);
1074 });
1075 }
1076
1078 XParticleSystem<T, PARTICLETYPE>& particleSystem,
1079 std::vector<SolidBoundary<T, PARTICLETYPE::d>>& solidBoundaries,
1081 contact,
1082 const T physDeltaX, const unsigned contactBoxResolutionPerDirection)
1083 {
1084 using namespace descriptors;
1085 constexpr unsigned D = PARTICLETYPE::d;
1086
1087 Vector<T, D> contactPhysDeltaX =
1088 evalContactDeltaX(contact.getMin(), contact.getMax(), physDeltaX,
1089 contactBoxResolutionPerDirection);
1090 contact.increaseMinMax(T {0.5} * util::sqrt(D) * contactPhysDeltaX);
1091 correctBoundingBoxNewContact(particleSystem, contact, physDeltaX,
1092 contactBoxResolutionPerDirection);
1093 }
1094
1096 XParticleSystem<T, PARTICLETYPE>& particleSystem,
1098 contact,
1099 T physDeltaX, unsigned contactBoxResolutionPerDirection)
1100 {
1101 using namespace descriptors;
1102 constexpr unsigned D = PARTICLETYPE::d;
1103
1104 auto particleA = particleSystem.get(contact.getIDs()[0]);
1105 auto particleB = particleSystem.get(contact.getIDs()[1]);
1106
1107 const std::function<bool(const PhysR<T, D>&)> isInsideContact =
1108 [&](const Vector<T, D>& pos) {
1109 return particles::resolved::isInsideParticle(particleA, pos) &&
1111 };
1112 const std::function<void()> resetMinMax = [&contact]() {
1113 contact.resetMinMax();
1114 };
1115 const std::function<void(const PhysR<T, D>&)> updateMinMax =
1116 [&contact](const PhysR<T, D>& pos) {
1117 contact.updateMinMax(pos);
1118 };
1119
1121 contact.getMin(), contact.getMax(), physDeltaX,
1122 contactBoxResolutionPerDirection, isInsideContact, resetMinMax,
1123 updateMinMax);
1124 }
1125
1126 template <
1127 typename CONTACTPROPERTIES,
1128 typename F1 = decltype(defaults::periodicity<PARTICLETYPE::d>),
1129 typename F2 = decltype(defaults::processContactForce<T, PARTICLETYPE::d>)>
1130 static void apply(
1131 XParticleSystem<T, PARTICLETYPE>& particleSystem,
1133 T,
1135 WALLCONTACTTYPE>& contactContainer,
1136 const CONTACTPROPERTIES& contactProperties,
1137 const SuperGeometry<T, PARTICLETYPE::d>& sGeometry,
1138 std::multimap<int, std::unique_ptr<std::uint8_t[]>>& dataMap,
1139 const unsigned contactBoxResolutionPerDirection = 8,
1140 const T k = T {4. / (3 * util::sqrt(M_PI))},
1141 F1 getSetupPeriodicity = defaults::periodicity<PARTICLETYPE::d>,
1142 F2 processContactForce =
1143 defaults::processContactForce<T, PARTICLETYPE::d>)
1144 {
1145 using namespace descriptors;
1146 const T physDeltaX =
1147 sGeometry.getCuboidGeometry().getMotherCuboid().getDeltaR();
1148
1149 for (auto& particleContact : contactContainer.particleContacts) {
1150 if (!particleContact.isEmpty()) {
1151 bool isResponsible = true;
1152
1153 if constexpr (access::providesParallelization<PARTICLETYPE>()) {
1154 isResponsible = singleton::mpi().getRank() ==
1155 particleContact.getResponsibleRank();
1156 }
1157
1158 if (isResponsible) {
1159 // Check if contact should be computed
1160 bool computeContact = true;
1161 if constexpr (access::providesComputeContact<PARTICLETYPE>()) {
1162 auto particleA = particleSystem.get(particleContact.getIDs()[0]);
1163 auto particleB = particleSystem.get(particleContact.getIDs()[1]);
1164 computeContact =
1165 access::isContactComputationEnabled(particleA, particleB);
1166 }
1167
1168 if (computeContact) {
1169 std::array<PhysR<T, PARTICLETYPE::d>, 2> originalPos;
1170 if constexpr (isPeriodic(getSetupPeriodicity())) {
1171 for (unsigned i = 0; i < 2; ++i) {
1172 auto particle = particleSystem.get(particleContact.getIDs()[i]);
1173 originalPos[i] =
1174 particle.template getField<GENERAL, POSITION>();
1175 particle.template setField<GENERAL, POSITION>(
1176 particleContact.getParticlePositions()[i]);
1177 }
1178 }
1179
1180 if (particleContact.isNew()) {
1181 correctBoundingBoxNewContact(particleSystem, particleContact,
1182 physDeltaX,
1183 contactBoxResolutionPerDirection);
1184 particleContact.isNew(particleContact.isEmpty());
1185 }
1186 else {
1187 if constexpr (BBCORRECTIONMETHOD == 1) {
1188 correctBoundingBoxExistingContact(
1189 particleSystem, particleContact, physDeltaX,
1190 contactBoxResolutionPerDirection);
1191 }
1192 else {
1193 correctBoundingBoxNewContact(particleSystem, particleContact,
1194 physDeltaX,
1195 contactBoxResolutionPerDirection);
1196 }
1197 }
1198 apply(dataMap, particleSystem, particleContact, contactProperties,
1199 physDeltaX, contactBoxResolutionPerDirection, k,
1200 processContactForce);
1201
1202 if constexpr (isPeriodic(getSetupPeriodicity())) {
1203 for (unsigned i = 0; i < 2; ++i) {
1204 auto particle = particleSystem.get(particleContact.getIDs()[i]);
1205 particle.template setField<GENERAL, POSITION>(originalPos[i]);
1206 }
1207 }
1208 particleContact.setParticlePositionUpdated(false);
1209 }
1210 else {
1211 // if contact should be ignored, reset min and max so that it counts
1212 // as empty and can be deleted during the next cleanContact() call.
1213 particleContact.resetMinMax();
1214 }
1215 }
1216 }
1217 }
1218 }
1219};
1220
1221template <typename T, typename PARTICLETYPE, typename PARTICLECONTACTTYPE,
1222 typename WALLCONTACTTYPE, unsigned BBCORRECTIONMETHOD = 0,
1223 bool CONVEX = true, bool useSDF = true>
1225
1226template <typename T, typename PARTICLETYPE, typename PARTICLECONTACTTYPE,
1227 unsigned BBCORRECTIONMETHOD, bool CONVEX, bool useSDF>
1229 T, PARTICLETYPE, PARTICLECONTACTTYPE,
1230 WallContactArbitraryFromOverlapVolume<T, PARTICLETYPE::d, CONVEX>,
1231 BBCORRECTIONMETHOD, CONVEX, useSDF> {
1232 static void resetContainer(
1234 T, PARTICLECONTACTTYPE,
1236 contactContainer)
1237 {
1238 for (auto& contact : contactContainer.wallContacts) {
1239 contact.resetMinMax();
1240 }
1241 }
1242
1243 static void
1245 contact,
1246 Particle<T, PARTICLETYPE>& particle,
1247 SolidBoundary<T, PARTICLETYPE::d>& solidBoundary,
1248 Vector<T, PARTICLETYPE::d>& contactNormal,
1249 Vector<T, PARTICLETYPE::d>& center, unsigned& volumeCellCount,
1250 T& surfaceCellCount, const Vector<T, PARTICLETYPE::d>& pos,
1251 const Vector<T, PARTICLETYPE::d>& contactPhysDeltaX)
1252 {
1253 /* OstreamManager clout(std::cout, "cellProcessingForContactForce"); */
1254 constexpr unsigned D = PARTICLETYPE::d;
1255
1256 // Check if pos is inside contact
1257 const std::function<bool(const PhysR<T, D>&)> isInside =
1258 [&](const PhysR<T, D>& pos) {
1259 bool const isInsideIndicator =
1260 solidBoundary.getIndicator()->signedDistance(pos.data()) <=
1261 solidBoundary.getEnlargementForContact();
1262 bool const isInsideParticle =
1265 return isInsideParticle && isInsideIndicator;
1266 };
1267
1268 // Check if pos is on surface
1269 const std::function<bool(const PhysR<T, D>&, const PhysR<T, D>&, bool&,
1270 bool&, const Vector<T, D>&, const Vector<T, D>&)>
1271 isOnSurface = [&](const PhysR<T, D>& pos,
1272 const PhysR<T, D>& contactPhysDeltaX,
1273 bool& onSurfaceA, bool& onSurfaceB,
1274 const Vector<T, D>& normalA,
1275 const Vector<T, D>& normalB) {
1276 Vector<T, D> neighbor;
1277 for (unsigned iD = 0; iD < D; ++iD) {
1278 neighbor[iD] = -normalA[iD] * contactPhysDeltaX[iD];
1279 }
1281 particle, pos + neighbor) <=
1283 for (unsigned iD = 0; iD < D; ++iD) {
1284 neighbor[iD] = -normalB[iD] * contactPhysDeltaX[iD];
1285 }
1286 onSurfaceB = solidBoundary.getIndicator()->signedDistance(
1287 (pos + neighbor).data()) <=
1288 solidBoundary.getEnlargementForContact();
1289 return onSurfaceA || onSurfaceB;
1290 };
1291
1292 // Calculate normal to particle surface
1293 const std::function<Vector<T, D>(const PhysR<T, D>&, const T)> calcNormalA =
1294 [&](const PhysR<T, D>& pos, const T meshSize) {
1295 // also with an enlarged particle, the normal should still point the same way, since we apply an equal layer to the whole surface
1297 meshSize);
1298 };
1299 // Calculate normal to indicator surface
1300 const std::function<Vector<T, D>(const PhysR<T, D>&, const T)> calcNormalB =
1301 [&](const PhysR<T, D>& pos, const T meshSize) {
1302 return solidBoundary.getIndicator()->surfaceNormal(pos, meshSize);
1303 };
1304 // Wrapper for update of min and max
1305 const std::function<void(const PhysR<T, D>&)> updateMinMax =
1306 [&contact](const PhysR<T, D>& pos) {
1307 contact.updateMinMax(pos);
1308 };
1309
1310 particles::contact::processCell(pos, contactPhysDeltaX, center,
1311 contactNormal, volumeCellCount,
1312 surfaceCellCount, isInside, isOnSurface,
1313 calcNormalA, calcNormalB, updateMinMax);
1314 }
1315
1316 template <
1317 typename CONTACTPROPERTIES,
1318 typename F = decltype(defaults::processContactForce<T, PARTICLETYPE::d>)>
1319 static void apply(
1320 std::multimap<int, std::unique_ptr<std::uint8_t[]>>& dataMap,
1321 XParticleSystem<T, PARTICLETYPE>& particleSystem,
1322 std::vector<SolidBoundary<T, PARTICLETYPE::d>>& solidBoundaries,
1324 contact,
1325 const CONTACTPROPERTIES& contactProperties, const T physDeltaX,
1326 const unsigned contactBoxResolutionPerDirection, const T k,
1327 F processContactForce = defaults::processContactForce<T, PARTICLETYPE::d>)
1328 {
1329 using namespace descriptors;
1330 constexpr unsigned D = PARTICLETYPE::d;
1331
1332 // indentation depth
1333 T indentationDepth = T(0.);
1334
1335 if (!contact.isEmpty()) {
1336 // particle and wall in contact
1337 auto particle = particleSystem.get(contact.getParticleID());
1338 SolidBoundary<T, D>& solidBoundary = solidBoundaries[contact.getWallID()];
1339
1340 // function to reset min & max of bounding box
1341 const std::function<void()> resetMinMax = [&contact]() {
1342 contact.resetMinMax();
1343 };
1344 // wrapper for cell processing to calculate contact point and normal
1345 const std::function<void(Vector<T, D>&, Vector<T, D>&, unsigned&, T&,
1346 const Vector<T, D>&, const Vector<T, D>&)>
1347 processCellWrapped =
1348 [&](Vector<T, D>& contactNormal, Vector<T, D>& center,
1349 unsigned& volumeCellCount, T& surfaceCellCount,
1350 const Vector<T, D>& physPos,
1351 const Vector<T, D>& contactPhysDeltaX) {
1352 processCell(contact, particle, solidBoundary, contactNormal,
1353 center, volumeCellCount, surfaceCellCount, physPos,
1354 contactPhysDeltaX);
1355 };
1356 // function to calculate indendation depth
1357 const std::function<T(const Vector<T, D>&, const Vector<T, D>&, const T)>
1358 calculateIndentation = [&](const Vector<T, D>& center,
1359 const Vector<T, D>& contactNormal,
1360 const T distancePrecision) {
1361 T indentation =
1363 center) +
1365 if (!util::nearZero(indentation)) {
1366 const PhysR<T, D> origin =
1367 center -
1368 contactNormal *
1371 particle, origin, contactNormal, distancePrecision);
1372 }
1373 T distToWall;
1374 if (solidBoundary.getIndicator()->distance(distToWall, center,
1375 distancePrecision,
1376 -1 * contactNormal)) {
1377 indentation +=
1378 distToWall + solidBoundary.getEnlargementForContact();
1379 }
1380#ifdef OLB_DEBUG
1381 else if (!util::nearZero(
1382 solidBoundary.getIndicator()->signedDistance(
1383 center))) {
1384 OstreamManager clout(std::cout, "forceApplication");
1385 clout << "WARNING: No distance to wall determined." << std::endl;
1386 }
1387#endif
1388 return indentation;
1389 };
1390 // function to apply force on particles
1391 const std::function<void(const Vector<T, D>&, const Vector<T, D>&, T, T)>
1392 applyForce = [&](const Vector<T, D>& center,
1393 const Vector<T, D>& contactNormal, T indentation,
1394 T overlapVolume) {
1395 const unsigned particleMaterial =
1396 particle.template getField<MECHPROPERTIES, MATERIAL>();
1397 const unsigned wallMaterial = solidBoundary.getContactMaterial();
1398
1399 indentationDepth = indentation;
1400 const Vector<T, D> relVel = evalRelativeVelocity(particle, center);
1401 const T dampingFactor = evalCurrentDampingFactor(
1402 contact,
1403 contactProperties.getCoefficientOfRestitution(particleMaterial,
1404 wallMaterial),
1405 evalRelativeNormalVelocity(contactNormal, relVel));
1406
1408 dataMap, particle, particleSystem, solidBoundary, center,
1409 contactNormal, indentation, overlapVolume, relVel,
1410 dampingFactor, contactProperties, k, contact.getParticleID(),
1411 contact.getWallID(), processContactForce);
1412 };
1413
1414 processContactViaVolume(contact.getMin(), contact.getMax(), physDeltaX,
1415 contactBoxResolutionPerDirection, resetMinMax,
1416 processCellWrapped, calculateIndentation,
1417 applyForce);
1418 }
1419 }
1420
1422 XParticleSystem<T, PARTICLETYPE>& particleSystem,
1423 std::vector<SolidBoundary<T, PARTICLETYPE::d>>& solidBoundaries,
1425 contact,
1426 const T physDeltaX, const unsigned contactBoxResolutionPerDirection)
1427 {
1428 using namespace descriptors;
1429 constexpr unsigned D = PARTICLETYPE::d;
1430
1431 Particle<T, PARTICLETYPE> particle =
1432 particleSystem.get(contact.getParticleID());
1433 SolidBoundary<T, D>& solidBoundary = solidBoundaries[contact.getWallID()];
1434
1435 // TODO: Is there a better way to increase the particle size than sdf::rounding?
1437 contact.getMin(), contact.getMax(), physDeltaX,
1438 contactBoxResolutionPerDirection,
1439 [&](const PhysR<T, D>& pos) {
1440 return sdf::intersection(
1441 sdf::rounding(
1442 particles::resolved::signedDistanceToParticle(particle, pos),
1443 particles::access::getEnlargementForContact(particle)),
1444 sdf::rounding(solidBoundary.getIndicator()->signedDistance(pos),
1445 solidBoundary.getEnlargementForContact()));
1446 },
1447 [&contact]() {
1448 contact.resetMinMax();
1449 },
1450 [&contact](const PhysR<T, D>& pos) {
1451 contact.updateMinMax(pos);
1452 });
1453 }
1454
1456 XParticleSystem<T, PARTICLETYPE>& particleSystem,
1457 std::vector<SolidBoundary<T, PARTICLETYPE::d>>& solidBoundaries,
1459 contact,
1460 const T physDeltaX, const unsigned contactBoxResolutionPerDirection)
1461 {
1462 using namespace descriptors;
1463 constexpr unsigned D = PARTICLETYPE::d;
1464
1465 Vector<T, D> contactPhysDeltaX =
1466 evalContactDeltaX(contact.getMin(), contact.getMax(), physDeltaX,
1467 contactBoxResolutionPerDirection);
1468 contact.increaseMinMax(T {0.5} * util::sqrt(D) * contactPhysDeltaX);
1469 correctBoundingBoxNewContact(particleSystem, solidBoundaries, contact,
1470 physDeltaX, contactBoxResolutionPerDirection);
1471 }
1472
1474 XParticleSystem<T, PARTICLETYPE>& particleSystem,
1475 std::vector<SolidBoundary<T, PARTICLETYPE::d>>& solidBoundaries,
1477 contact,
1478 const T physDeltaX, const unsigned contactBoxResolutionPerDirection)
1479 {
1480 using namespace descriptors;
1481 constexpr unsigned D = PARTICLETYPE::d;
1482
1483 auto particle = particleSystem.get(contact.getParticleID());
1484 SolidBoundary<T, D>& solidBoundary = solidBoundaries[contact.getWallID()];
1485
1486 const std::function<bool(const PhysR<T, D>&)> isInsideContact =
1487 [&](const Vector<T, D>& pos) {
1488 bool isInsideWall;
1489 solidBoundary.getIndicator()->operator()(&isInsideWall, pos.data());
1490 return particles::resolved::isInsideParticle(particle, pos) &&
1491 isInsideWall;
1492 };
1493 const std::function<void()> resetMinMax = [&contact]() {
1494 contact.resetMinMax();
1495 };
1496 const std::function<void(const PhysR<T, D>&)> updateMinMax =
1497 [&contact](const PhysR<T, D>& pos) {
1498 contact.updateMinMax(pos);
1499 };
1500
1502 contact.getMin(), contact.getMax(), physDeltaX,
1503 contactBoxResolutionPerDirection, isInsideContact, resetMinMax,
1504 updateMinMax);
1505 }
1506
1507 template <
1508 typename CONTACTPROPERTIES,
1509 typename F1 = decltype(defaults::periodicity<PARTICLETYPE::d>),
1510 typename F2 = decltype(defaults::processContactForce<T, PARTICLETYPE::d>)>
1511 static void
1513 std::vector<SolidBoundary<T, PARTICLETYPE::d>>& solidBoundaries,
1515 T, PARTICLECONTACTTYPE,
1517 contactContainer,
1518 const CONTACTPROPERTIES& contactProperties,
1519 const SuperGeometry<T, PARTICLETYPE::d>& sGeometry,
1520 std::multimap<int, std::unique_ptr<std::uint8_t[]>>& dataMap,
1521 const unsigned contactBoxResolutionPerDirection = 8,
1522 const T k = T {4. / (3 * util::sqrt(M_PI))},
1523 F1 getSetupPeriodicity = defaults::periodicity<PARTICLETYPE::d>,
1524 F2 processContactForce =
1525 defaults::processContactForce<T, PARTICLETYPE::d>)
1526 {
1527 using namespace descriptors;
1528 const T physDeltaX =
1529 sGeometry.getCuboidGeometry().getMotherCuboid().getDeltaR();
1530
1531 for (auto& wallContact : contactContainer.wallContacts) {
1532 if (!wallContact.isEmpty()) {
1533 bool isResponsible = true;
1534
1535 if constexpr (access::providesParallelization<PARTICLETYPE>()) {
1536 isResponsible =
1537 singleton::mpi().getRank() == wallContact.getResponsibleRank();
1538 }
1539
1540 if (isResponsible) {
1541 // Check if contact should be computed
1542 bool computeContact = true;
1543 if constexpr (access::providesComputeContact<PARTICLETYPE>()) {
1544 auto particle = particleSystem.get(wallContact.getParticleID());
1545 computeContact = access::isContactComputationEnabled(particle);
1546 }
1547
1548 if (computeContact) {
1549 PhysR<T, PARTICLETYPE::d> originalPos;
1550 if constexpr (isPeriodic(getSetupPeriodicity())) {
1551 auto particle = particleSystem.get(wallContact.getParticleID());
1552 originalPos = particle.template getField<GENERAL, POSITION>();
1553 particle.template setField<GENERAL, POSITION>(
1554 wallContact.getParticlePosition());
1555 }
1556
1557 if (wallContact.isNew()) {
1558 correctBoundingBoxNewContact(particleSystem, solidBoundaries,
1559 wallContact, physDeltaX,
1560 contactBoxResolutionPerDirection);
1561 wallContact.isNew(wallContact.isEmpty());
1562 }
1563 else {
1564 if constexpr (BBCORRECTIONMETHOD == 1) {
1565 correctBoundingBoxExistingContact(
1566 particleSystem, solidBoundaries, wallContact, physDeltaX,
1567 contactBoxResolutionPerDirection);
1568 }
1569 else {
1570 correctBoundingBox(particleSystem, solidBoundaries, wallContact,
1571 physDeltaX,
1572 contactBoxResolutionPerDirection);
1573 }
1574 }
1575 apply(dataMap, particleSystem, solidBoundaries, wallContact,
1576 contactProperties, physDeltaX,
1577 contactBoxResolutionPerDirection, k, processContactForce);
1578
1579 if constexpr (isPeriodic(getSetupPeriodicity())) {
1580 particleSystem.get(wallContact.getParticleID())
1581 .template setField<GENERAL, POSITION>(originalPos);
1582 // TODO: Set that unfied position is not set anymore
1583 }
1584 wallContact.setParticlePositionUpdated(false);
1585 }
1586 else {
1587 // if contact should be ignored, reset min and max so that it counts
1588 // as empty and can be deleted during the next cleanContact() call.
1589 wallContact.resetMinMax();
1590 }
1591 }
1592 }
1593 }
1594 }
1595};
1596
1597/*
1598 * Combined contact processing
1599 * Adds an option to use different bounding box correction methods for existing contacts:
1600 * 0: Less fast, but more accurate option (this option is always used for new contacts)
1601 * 1: Faster, but inaccurate option (this option needs tests)
1602 */
1603template <
1604 typename T, typename PARTICLETYPE, typename PARTICLECONTACTTYPE,
1605 typename WALLCONTACTTYPE, typename CONTACTPROPERTIES,
1606 unsigned BBCORRECTIONMETHOD = 0,
1607 typename F1 = decltype(defaults::periodicity<PARTICLETYPE::d>),
1608 typename F2 = decltype(defaults::processContactForce<T, PARTICLETYPE::d>)>
1611 std::vector<SolidBoundary<T, PARTICLETYPE::d>>& solidBoundaries,
1613 const CONTACTPROPERTIES& contactProperties,
1614 const SuperGeometry<T, PARTICLETYPE::d>& sGeometry,
1615#ifdef PARALLEL_MODE_MPI
1616 MPI_Comm contactTreatmentComm,
1617#endif
1618 const unsigned contactBoxResolutionPerDirection = 8,
1619 const T k = static_cast<T>(4. / (3 * util::sqrt(M_PI))),
1620 F1 getSetupPeriodicity = defaults::periodicity<PARTICLETYPE::d>,
1621 F2 processContactForce = defaults::processContactForce<T, PARTICLETYPE::d>)
1622
1623{
1624 std::multimap<int, std::unique_ptr<std::uint8_t[]>> dataMap;
1625
1626 particle_particle<T, PARTICLETYPE, PARTICLECONTACTTYPE, WALLCONTACTTYPE,
1627 BBCORRECTIONMETHOD>::apply(particleSystem, contactContainer,
1628 contactProperties, sGeometry,
1629 dataMap,
1630 contactBoxResolutionPerDirection,
1631 k, getSetupPeriodicity,
1632 processContactForce);
1633
1634 particle_wall<T, PARTICLETYPE, PARTICLECONTACTTYPE, WALLCONTACTTYPE,
1635 BBCORRECTIONMETHOD>::apply(particleSystem, solidBoundaries,
1636 contactContainer, contactProperties,
1637 sGeometry, dataMap,
1638 contactBoxResolutionPerDirection, k,
1639 getSetupPeriodicity,
1640 processContactForce);
1641 if constexpr (access::providesParallelization<PARTICLETYPE>()) {
1642 communicateContactTreatmentResults<T, PARTICLETYPE>(particleSystem, dataMap
1643#ifdef PARALLEL_MODE_MPI
1644 ,
1645 contactTreatmentComm
1646#endif
1647 );
1648 }
1649}
1650
1651template <
1652 typename T, typename PARTICLETYPE, typename PARTICLECONTACTTYPE,
1653 typename WALLCONTACTTYPE, typename CONTACTPROPERTIES,
1654 unsigned BBCORRECTIONMETHOD = 0,
1655 typename F1 = decltype(defaults::periodicity<PARTICLETYPE::d>),
1656 typename F2 = decltype(defaults::processContactForce<T, PARTICLETYPE::d>)>
1658 ParticleSystem<T, PARTICLETYPE>& particleSystem,
1659 std::vector<SolidBoundary<T, PARTICLETYPE::d>>& solidBoundaries,
1661 const CONTACTPROPERTIES& contactProperties,
1662 const SuperGeometry<T, PARTICLETYPE::d>& sGeometry,
1663 const unsigned contactBoxResolutionPerDirection = 8,
1664 const T k = static_cast<T>(4. / (3 * util::sqrt(M_PI))),
1665 F1 getSetupPeriodicity = defaults::periodicity<PARTICLETYPE::d>,
1666 F2 processContactForce = defaults::processContactForce<T, PARTICLETYPE::d>)
1667
1668{
1669 std::multimap<int, std::unique_ptr<std::uint8_t[]>> dataMap;
1670
1671 particle_particle<T, PARTICLETYPE, PARTICLECONTACTTYPE, WALLCONTACTTYPE,
1672 BBCORRECTIONMETHOD>::apply(particleSystem, contactContainer,
1673 contactProperties, sGeometry,
1674 dataMap,
1675 contactBoxResolutionPerDirection,
1676 k, getSetupPeriodicity,
1677 processContactForce);
1678
1679 particle_wall<T, PARTICLETYPE, PARTICLECONTACTTYPE, WALLCONTACTTYPE,
1680 BBCORRECTIONMETHOD>::apply(particleSystem, solidBoundaries,
1681 contactContainer, contactProperties,
1682 sGeometry, dataMap,
1683 contactBoxResolutionPerDirection, k,
1684 getSetupPeriodicity,
1685 processContactForce);
1686}
1687
1688} //namespace contact
1689
1690} //namespace particles
1691
1692} //namespace olb
1693
1694#endif
#define M_PI
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.
Plain old scalar vector.
Definition vector.h:47
constexpr const T * data() const any_platform
Definition vector.h:161
int getRank() const
Returns the process ID.
bool isContactComputationEnabled(Particle< T, PARTICLETYPE > &particle)
Check if contact should be regarded (specification for a single particle)
T getEnlargementForContact(Particle< T, PARTICLETYPE > particle)
void forParticlesInSuperParticleSystem(SuperParticleSystem< T, PARTICLETYPE > &sParticleSystem, F f)
void processContacts(SuperParticleSystem< T, PARTICLETYPE > &particleSystem, std::vector< SolidBoundary< T, PARTICLETYPE::d > > &solidBoundaries, ContactContainer< T, PARTICLECONTACTTYPE, WALLCONTACTTYPE > &contactContainer, const CONTACTPROPERTIES &contactProperties, const SuperGeometry< T, PARTICLETYPE::d > &sGeometry, MPI_Comm contactTreatmentComm, const unsigned contactBoxResolutionPerDirection=8, const T k=static_cast< T >(4./(3 *util::sqrt(M_PI))), F1 getSetupPeriodicity=defaults::periodicity< PARTICLETYPE::d >, F2 processContactForce=defaults::processContactForce< T, PARTICLETYPE::d >)
void evalStartAndEndPos(Vector< long, D > &startPos, Vector< long, D > &endPos, const PhysR< T, D > &max, const PhysR< T, D > &min, const PhysR< T, D > &contactPhysDeltaX)
T evalForceViaVolumeCoefficient(const Vector< T, D > &min, const Vector< T, D > &max, T overlapVolume)
constexpr T evalCurrentDampingFactor(CONTACTTYPE &contact, const T coefficientOfRestitution, const Vector< T, D > &initialRelativeNormalVelocity)
constexpr Vector< T, D > evalContactDeltaX(const Vector< T, D > &min, const Vector< T, D > &max, const T physDeltaX, const unsigned contactBoxResolutionPerDirection)
void processCell(const PhysR< T, D > &pos, const PhysR< T, D > &contactPhysDeltaX, PhysR< T, D > &center, Vector< T, D > &contactNormal, unsigned &volumeCellCount, T &surfaceCellCount, F1 isInside, F2 isOnSurface, F3 calcNormalA, F4 calcNormalB, F5 updateMinMax)
void prepareForceCalculation(ContactContainer< T, PARTICLECONTACTTYPE, WALLCONTACTTYPE > &contactContainer)
constexpr Vector< T, PARTICLETYPE::d > evalRelativeVelocity(Particle< T, PARTICLETYPE > &particleA, Particle< T, PARTICLETYPE > &particleB, const Vector< T, PARTICLETYPE::d > &position)
Returns the relative velocity of two particles at a defined position.
void correctBoundingBoxNewContact(PhysR< T, D > min, PhysR< T, D > max, T physDeltaX, unsigned contactBoxResolutionPerDirection, F1 signedDistanceToIntersection, F2 resetContactBox, F3 updateMinMax)
void correctBoundingBox(const PhysR< T, D > &min, const PhysR< T, D > &max, const T physDeltaX, const unsigned contactBoxResolutionPerDirection, F1 isInsideContact, F2 resetContactBox, F3 updateMinMax)
Vector< T, D > calcElasticAndViscousForce(T effectiveE, T k, T overlapVolume, T indentation, T dampingFactor, const Vector< T, D > &relVelNormal, const Vector< T, D > &contactNormal)
Elastic and viscous force from overlap volume according to Nassauer & Kuna (2013)
T evalApproxSurface(const Vector< T, D > &normalizedNormal, const PhysR< T, D > &contactPhysDeltaX)
Takes a normalized normal and the voxel sizes and approximates the corresponding surface.
constexpr void forEachPosition(Vector< T, 3 > startPos, Vector< T, 3 > endPos, F f)
void updateMinMax(PhysR< T, D > &min, PhysR< T, D > &max, const PhysR< T, D > &pos)
Vector< T, D > getNormalForceFromOverlapVolume(const Vector< T, D > &contactNormal, const T indentation, const T overlapVolume, const T effectiveE, const T dampingFactor, const T k, const Vector< T, D > &relVel)
void applyForceFromOverlapVolume(std::multimap< int, std::unique_ptr< std::uint8_t[]> > &dataMap, Particle< T, PARTICLETYPE > &particleA, Particle< T, PARTICLETYPE > &particleB, XParticleSystem< T, PARTICLETYPE > &particleSystem, const PhysR< T, PARTICLETYPE::d > &contactPoint, const Vector< T, PARTICLETYPE::d > &contactNormal, const T indentation, const T overlapVolume, const Vector< T, PARTICLETYPE::d > &relVel, const T dampingFactor, const CONTACTPROPERTIES &contactProperties, T k, const std::size_t &particleAID, const std::size_t &particleBID, F processContactForce=defaults::processContactForce< T, PARTICLETYPE::d >)
bool evalStartingPoint(const Vector< long, D > &startPos, const Vector< long, D > &endPos, const Vector< T, D > &contactPhysDeltaX, Vector< long, D > &currPos, F1 isInsideContact)
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)
constexpr Vector< T, D > evalRelativeNormalVelocity(const Vector< T, D > &contactNormal, const Vector< T, D > &relVel)
void applyForceOnParticle(std::multimap< int, std::unique_ptr< std::uint8_t[]> > &dataMap, Particle< T, PARTICLETYPE > &particle, XParticleSystem< T, PARTICLETYPE > &particleSystem, const Vector< T, PARTICLETYPE::d > &contactPoint, const Vector< T, PARTICLETYPE::d > &normalForce, const Vector< T, PARTICLETYPE::d > &tangentialForce)
constexpr void forEachPositionWithBreak(Vector< T, 3 > startPos, Vector< T, 3 > endPos, F f)
void processContactViaVolume(const Vector< T, D > &min, const Vector< T, D > &max, T physDeltaX, unsigned contactBoxResolutionPerDirection, F1 resetContactBox, F2 processCellWrapped, F3 calculateIndentation, F4 applyForce)
Vector< T, D > getTangentialForceFromOverlapVolume(const Vector< T, D > &contactNormal, const T coefficientStaticFriction, const T coefficientKineticFriction, const T staticKineticTransitionVelocity, const T k, const Vector< T, D > &relVel, const Vector< T, D > &normalForce)
void evalBoundingBoxRecursive(const Vector< long, D > &startPos, const Vector< long, D > &endPos, const Vector< T, D > &contactPhysDeltaX, const Vector< long, D > &currPos, const Vector< short, D > &dirIn, F1 isInsideContact, F2 updateMinMax)
void communicateContacts(ContactContainer< T, PARTICLECONTACTTYPE, WALLCONTACTTYPE > &contactContainer)
constexpr Vector< T, PARTICLETYPE::d > calculateLocalVelocity(Particle< T, PARTICLETYPE > &particle, const PhysR< T, PARTICLETYPE::d > &input)
const S distanceToParticle(Particle< T, PARTICLETYPE > &particle, const Vector< S, PARTICLETYPE::d > &origin, const Vector< S, PARTICLETYPE::d > &direction, S precision, S pitch)
Vector< S, PARTICLETYPE::d > normalOnParticleSurface(Particle< T, PARTICLETYPE > &particle, const Vector< S, PARTICLETYPE::d > &pos, const T meshSize)
bool isInsideParticle(Particle< T, PARTICLETYPE > &particle, const PhysR< S, PARTICLETYPE::d > &input)
const S signedDistanceToParticle(Particle< T, PARTICLETYPE > &particle, const PhysR< S, PARTICLETYPE::d > &input)
std::conditional_t< PARTICLETYPE::template providesNested< descriptors::PARALLELIZATION >(), SuperParticleSystem< T, PARTICLETYPE >, ParticleSystem< T, PARTICLETYPE > > XParticleSystem
constexpr bool isPeriodic(const Vector< bool, D > &periodic)
MpiManager & mpi()
cpu::simd::Pack< T > sqrt(cpu::simd::Pack< T > value)
Definition pack.h:100
ADf< T, DIM > ceil(const ADf< T, DIM > &a)
Definition aDiff.h:900
ADf< T, DIM > floor(const ADf< T, DIM > &a)
Definition aDiff.h:869
cpu::simd::Pack< T > min(cpu::simd::Pack< T > rhs, cpu::simd::Pack< T > lhs)
Definition pack.h:124
ADf< T, DIM > log(const ADf< T, DIM > &a)
Definition aDiff.h:475
T average(const Vector< T, Size > &a)
computes the average of all elements
cpu::simd::Pack< T > max(cpu::simd::Pack< T > rhs, cpu::simd::Pack< T > lhs)
Definition pack.h:130
cpu::simd::Pack< T > pow(cpu::simd::Pack< T > base, cpu::simd::Pack< T > exp)
Definition pack.h:112
bool nearZero(const ADf< T, DIM > &a)
Definition aDiff.h:1087
int sign(T val)
Definition util.h:54
ADf< T, DIM > exp(const ADf< T, DIM > &a)
Definition aDiff.h:455
Top level namespace for all of OpenLB.
std::enable_if_t< std::is_arithmetic< T >::type::value, T > abs(T x)
Definition util.h:396
constexpr T norm(const ScalarVector< T, D, IMPL > &a)
Euclidean vector norm.
constexpr Vector< T, D > normalize(const ScalarVector< T, D, IMPL > &a, T scale=T{1})
Definition vector.h:245
constexpr unsigned getContactMaterial() const
Definition wall.hh:54
constexpr T getEnlargementForContact() const
Definition wall.hh:60
IndicatorF< T, D > * getIndicator()
Definition wall.hh:42
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 void updateMinMax(const PhysR< T, D > &positionInsideTheContact)
Update min and max with given position inside the contact.
constexpr const PhysR< T, D > & getMax() const
Read access to max.
constexpr bool isEmpty() const
Returns if contact holds data.
constexpr const PhysR< T, D > & getMin() const
Read access to min.
constexpr void resetMinMax()
Reset min and max to default values.
constexpr const std::size_t & getParticleID() const
Read access to particle ID.
constexpr const PhysR< T, D > & getMin() const
Read access to min.
constexpr void resetMinMax()
Reset min and max to default values.
constexpr unsigned getWallID() const
Read access to wall matreial.
constexpr bool isEmpty() const
Returns if contact holds data.
constexpr const PhysR< T, D > & getMax() const
Read access to max.
constexpr void increaseMinMax(const Vector< T, D > &increaseBy)
Increase bounding box size.
constexpr void updateMinMax(const PhysR< T, D > &positionInsideTheContact)
Update min and max with given position inside the contact.
static void apply(XParticleSystem< T, PARTICLETYPE > &particleSystem, ContactContainer< T, ParticleContactArbitraryFromOverlapVolume< T, PARTICLETYPE::d, CONVEX >, WALLCONTACTTYPE > &contactContainer, const CONTACTPROPERTIES &contactProperties, const SuperGeometry< T, PARTICLETYPE::d > &sGeometry, std::multimap< int, std::unique_ptr< std::uint8_t[]> > &dataMap, const unsigned contactBoxResolutionPerDirection=8, const T k=T {4./(3 *util::sqrt(M_PI))}, F1 getSetupPeriodicity=defaults::periodicity< PARTICLETYPE::d >, F2 processContactForce=defaults::processContactForce< T, PARTICLETYPE::d >)
static void correctBoundingBoxExistingContact(XParticleSystem< T, PARTICLETYPE > &particleSystem, ParticleContactArbitraryFromOverlapVolume< T, PARTICLETYPE::d, CONVEX > &contact, T physDeltaX, unsigned contactBoxResolutionPerDirection)
static void correctBoundingBoxNewContact(XParticleSystem< T, PARTICLETYPE > &particleSystem, ParticleContactArbitraryFromOverlapVolume< T, PARTICLETYPE::d, CONVEX > &contact, const T physDeltaX, const unsigned contactBoxResolutionPerDirection)
static void correctBoundingBox(XParticleSystem< T, PARTICLETYPE > &particleSystem, std::vector< SolidBoundary< T, PARTICLETYPE::d > > &solidBoundaries, WallContactArbitraryFromOverlapVolume< T, PARTICLETYPE::d, CONVEX > &contact, const T physDeltaX, const unsigned contactBoxResolutionPerDirection)
static void apply(std::multimap< int, std::unique_ptr< std::uint8_t[]> > &dataMap, XParticleSystem< T, PARTICLETYPE > &particleSystem, ParticleContactArbitraryFromOverlapVolume< T, PARTICLETYPE::d, CONVEX > &contact, const CONTACTPROPERTIES &contactProperties, const T physDeltaX, const unsigned contactBoxResolutionPerDirection, const T k, F processContactForce=defaults::processContactForce< T, PARTICLETYPE::d >)
static void resetContainer(ContactContainer< T, ParticleContactArbitraryFromOverlapVolume< T, PARTICLETYPE::d, CONVEX >, WALLCONTACTTYPE > &contactContainer)
static void processCell(ParticleContactArbitraryFromOverlapVolume< T, PARTICLETYPE::d, CONVEX > &contact, Particle< T, PARTICLETYPE > &particleA, Particle< T, PARTICLETYPE > &particleB, Vector< T, PARTICLETYPE::d > &contactNormal, Vector< T, PARTICLETYPE::d > &center, unsigned &volumeCellCount, T &surfaceCellCount, const Vector< T, PARTICLETYPE::d > &pos, const Vector< T, PARTICLETYPE::d > &contactPhysDeltaX)
static void correctBoundingBoxExistingContact(XParticleSystem< T, PARTICLETYPE > &particleSystem, std::vector< SolidBoundary< T, PARTICLETYPE::d > > &solidBoundaries, WallContactArbitraryFromOverlapVolume< T, PARTICLETYPE::d, CONVEX > &contact, const T physDeltaX, const unsigned contactBoxResolutionPerDirection)
static void resetContainer(ContactContainer< T, PARTICLECONTACTTYPE, WallContactArbitraryFromOverlapVolume< T, PARTICLETYPE::d, CONVEX > > &contactContainer)
static void processCell(WallContactArbitraryFromOverlapVolume< T, PARTICLETYPE::d, CONVEX > &contact, Particle< T, PARTICLETYPE > &particle, SolidBoundary< T, PARTICLETYPE::d > &solidBoundary, Vector< T, PARTICLETYPE::d > &contactNormal, Vector< T, PARTICLETYPE::d > &center, unsigned &volumeCellCount, T &surfaceCellCount, const Vector< T, PARTICLETYPE::d > &pos, const Vector< T, PARTICLETYPE::d > &contactPhysDeltaX)
static void correctBoundingBox(XParticleSystem< T, PARTICLETYPE > &particleSystem, std::vector< SolidBoundary< T, PARTICLETYPE::d > > &solidBoundaries, WallContactArbitraryFromOverlapVolume< T, PARTICLETYPE::d, CONVEX > &contact, const T physDeltaX, const unsigned contactBoxResolutionPerDirection)
static void apply(std::multimap< int, std::unique_ptr< std::uint8_t[]> > &dataMap, XParticleSystem< T, PARTICLETYPE > &particleSystem, std::vector< SolidBoundary< T, PARTICLETYPE::d > > &solidBoundaries, WallContactArbitraryFromOverlapVolume< T, PARTICLETYPE::d, CONVEX > &contact, const CONTACTPROPERTIES &contactProperties, const T physDeltaX, const unsigned contactBoxResolutionPerDirection, const T k, F processContactForce=defaults::processContactForce< T, PARTICLETYPE::d >)
static void apply(XParticleSystem< T, PARTICLETYPE > &particleSystem, std::vector< SolidBoundary< T, PARTICLETYPE::d > > &solidBoundaries, ContactContainer< T, PARTICLECONTACTTYPE, WallContactArbitraryFromOverlapVolume< T, PARTICLETYPE::d, CONVEX > > &contactContainer, const CONTACTPROPERTIES &contactProperties, const SuperGeometry< T, PARTICLETYPE::d > &sGeometry, std::multimap< int, std::unique_ptr< std::uint8_t[]> > &dataMap, const unsigned contactBoxResolutionPerDirection=8, const T k=T {4./(3 *util::sqrt(M_PI))}, F1 getSetupPeriodicity=defaults::periodicity< PARTICLETYPE::d >, F2 processContactForce=defaults::processContactForce< T, PARTICLETYPE::d >)
static void correctBoundingBoxNewContact(XParticleSystem< T, PARTICLETYPE > &particleSystem, std::vector< SolidBoundary< T, PARTICLETYPE::d > > &solidBoundaries, WallContactArbitraryFromOverlapVolume< T, PARTICLETYPE::d, CONVEX > &contact, const T physDeltaX, const unsigned contactBoxResolutionPerDirection)
Converts dimensions by deriving from given cartesian dimension D.
efficient implementation of a vector class