32#ifndef PARTICLE_SURFACE_DETACHMENT_H
33#define PARTICLE_SURFACE_DETACHMENT_H
43namespace interaction {
50 -std::sin(angle-
M_PI)*radius );
51 return position + relPos;
58 T angle1DRad,
unsigned axis,
bool verbose=
false)
63 T radiusRot =
norm(relPosCOR);
67 CORnew2D[0] = CORnew[1];
68 CORnew2D[1] = CORnew[2];
70 CORnew2D[0] = CORnew[0];
71 CORnew2D[1] = CORnew[2];
73 CORnew2D[0] = CORnew[0];
74 CORnew2D[1] = CORnew[1];
76 std::cerr <<
"ERROR: Unknown axis " << axis <<
"!" << std::endl;
83 posEccentric[0] = position[0];
84 posEccentric[1] = posEccentric2D[0];
85 posEccentric[2] = posEccentric2D[1];
87 posEccentric[0] = posEccentric2D[0];
88 posEccentric[1] = position[1];
89 posEccentric[2] = posEccentric2D[1];
91 posEccentric[0] = posEccentric2D[0];
92 posEccentric[1] = posEccentric2D[1];
93 posEccentric[2] = position[2];
95 std::cerr <<
"ERROR: Unknown axis " << axis <<
"!" << std::endl;
100 clout <<
"---Eccentric Position ---" << std::endl;
101 clout <<
"Position= " << position << std::endl;
102 clout <<
"RelPosCOR= " << relPosCOR << std::endl;
103 clout <<
"CORnew= " << CORnew << std::endl;
104 clout <<
"radiusRot= " << radiusRot << std::endl;
105 clout <<
"angle1DRad (repr. rotation at initial position)= " << angle1DRad << std::endl;
106 clout <<
"posEccentric2D= " << posEccentric2D << std::endl;
107 clout <<
"-------------------------" << std::endl;
115 return -std::atan2(-extent[2],extent[0]);
124template<
typename T,
typename PARTICLETYPE>
129 using namespace descriptors;
130 using namespace access;
131 static_assert(PARTICLETYPE::template providesNested<SURFACE,COR_OFFSET>(),
132 "Field SURFACE:COR_OFFSET has to be provided");
136 auto rotationAxisNormal =
crossProduct( surfaceNormal, mainFlowDirection );
139 auto normalParticleSurface = getSurfaceNormal( particle );
141 normalParticleSurface, surfaceNormal );
143 auto extent = getCuboidSurfaceExtent( particle );
145 auto position = getPosition( particle );
146 auto CORoffset = particle.template getField<SURFACE,COR_OFFSET>();
148 decltype(position) posEccentric;
149 if constexpr (is3D<PARTICLETYPE>()){
153 T angleEffective1D = orientationRelative[rotationAxis]
154 + constAngleOffset * signedDirection;
157 CORoffset, angleEffective1D, rotationAxis );
159 std::cerr <<
"ERROR: 2D version not implemented yet!" << std::endl;
162 auto posCubeDiffEcc = posEccentric-position;
163 CORoffset -= posCubeDiffEcc;
165 particle.template setField<GENERAL,POSITION>( posEccentric );
166 particle.template setField<SURFACE,COR_OFFSET>( CORoffset );
169template<
typename T,
unsigned D>
171 unsigned short& axisFlow,
unsigned short& axisSurface,
unsigned short& axisRot )
176 if (axisSurface==1){ axisRot=2; }
177 else if (axisSurface==2){ axisRot=1; }
178 else { std::cerr <<
"Error: Axes not unique!" << std::endl; }
179 }
else if (axisFlow==1){
180 if (axisSurface==0){ axisRot=2; }
181 else if (axisSurface==2){ axisRot=0; }
182 else { std::cerr <<
"Error: Axes not unique!" << std::endl; }
184 if (axisSurface==0){ axisRot=1; }
185 else if (axisSurface==1){ axisRot=0; }
186 else { std::cerr <<
"Error: Axes not unique!" << std::endl; }
192template<
typename T,
typename PARTICLETYPE>
197 using namespace particles::access;
198 using namespace descriptors;
200 auto extent = getCuboidSurfaceExtent( particle );
205 const unsigned axisZunrotated = 2;
208 CORoffset[axisFlow] = .5*extent[axisFlow] * mainFlowDirection[axisFlow];
209 CORoffset[axisSurface] = -.5*extent[axisZunrotated] * surfaceNormal[axisSurface];
211 particle.template setField<SURFACE,COR_OFFSET>(CORoffset);
214template<
typename T,
typename PARTICLETYPE>
219 using namespace descriptors;
220 static_assert(PARTICLETYPE::template providesNested<DYNBEHAVIOUR,ACTIVE>(),
221 "Field DYNBEHAVIOUR:ACTIVE has to be provided");
222 static_assert(PARTICLETYPE::template providesNested<DYNBEHAVIOUR,DETACHING>(),
223 "Field DYNBEHAVIOUR:DETACHING has to be provided");
225 if constexpr (access::is3D<PARTICLETYPE>()){
228 std::cerr <<
"ERROR: 2D version not implemented yet!" << std::endl;
231 particle.template setField<DYNBEHAVIOUR,DETACHING>(
true);
232 particle.template setField<DYNBEHAVIOUR,ACTIVE>(
false );
234 if constexpr(access::providesComputeContact<PARTICLETYPE>()){
235 particle.template setField<DYNBEHAVIOUR,COMPUTE_CONTACT>(
false);
241template<
typename T,
typename PARTICLETYPE>
246 using namespace particles::access;
247 using namespace descriptors;
248 static_assert(PARTICLETYPE::template providesNested<DYNBEHAVIOUR,DETACHING>(),
249 "Field DYNBEHAVIOUR:DETACHING has to be provided");
250 static_assert(PARTICLETYPE::template providesNested<SURFACE,COR_OFFSET>(),
251 "Field SURFACE:COR_OFFSET has to be provided");
255 auto extent = getCuboidSurfaceExtent( particle );
256 const unsigned axisZunrotated = 2;
257 T offsetCentreWall = .5*extent[axisZunrotated];
259 auto CORoffset = getCORoffset( particle );
264 T surfaceDistance = -CORoffset[axisSurface]-offsetCentreWall;
265 T lever = CORoffset[axisFlow];
272 unsigned short rotAxis = 1;
276 T adhesionNormal = adhesion[0];
277 T adhesionTangential = adhesion[1];
281 T fullForceDist = 500e-10;
284 if (surfaceDistance <= fullForceDist){
287 if (surfaceDistance > 0){
290 T distFactor = surfaceDistance/fullForceDist;
293 T addhesionForceNormalContribution = -distFactor*adhesionNormal;
294 T adhesionTorqueContribution = addhesionForceNormalContribution*lever;
297 force[axisSurface] += addhesionForceNormalContribution * surfaceNormal[axisSurface];
298 torque[rotAxis] += adhesionTorqueContribution;
301 particle.template setField<FORCING,FORCE>( force );
302 particle.template setField<FORCING,TORQUE>( torque );
304#ifdef VERBOSE_ADHESION_FORCE
305 auto orient = getAngle(particle);
306 std::cout <<
"Adhesion:" << std::endl
307 <<
" - distance=" << surfaceDistance
308 <<
" (factor=" << (distFactor) <<
")"
309 <<
", angle=" << orient[1] << std::endl
310 <<
" - forceNormalContrib=" << addhesionForceNormalContribution
311 <<
", torqueContrib=" << adhesionTorqueContribution << std::endl
312 <<
" - resForce=" << force[axisSurface]
313 <<
", resTorque=" << torque[rotAxis] << std::endl
314 <<
" - resForce3D=" << force
315 <<
", resTorque3D=" << torque
324template<
typename T,
typename PARTICLETYPE>
328 using namespace particles::access;
329 using namespace descriptors;
330 static_assert(PARTICLETYPE::template providesNested<SURFACE,COR_OFFSET>(),
331 "Field SURFACE:COR_OFFSET has to be provided");
338 auto torque = getTorque(particle);
340 T torqueTilt = torque[axisRot]*-tiltAxis[axisRot];
342 auto CORoffset = getCORoffset( particle );
343 T lever = CORoffset[axisFlow];
345 T forceNormalRot = 0.;
346 if (lever!=0){ forceNormalRot = torqueTilt / lever; }
348 return forceNormalRot;
353template<
typename T,
typename PARTICLETYPE>
358 using namespace particles::access;
359 using namespace descriptors;
360 static_assert(PARTICLETYPE::template providesNested<DYNBEHAVIOUR,ACTIVE>(),
361 "Field DYNBEHAVIOUR:ACTIVE has to be provided");
362 static_assert(PARTICLETYPE::template providesNested<DYNBEHAVIOUR,DETACHING>(),
363 "Field DYNBEHAVIOUR:DETACHING has to be provided");
364 static_assert(PARTICLETYPE::template providesNested<SURFACE,COR_OFFSET>(),
365 "Field SURFACE:COR_OFFSET has to be provided");
370 Vector<T,2> forceAdhesion = getAdhesion( particle );
371 T forceAdhesionN = forceAdhesion[0];
375 if (forceNormalRot>forceAdhesionN){
377 particle.template setField<DYNBEHAVIOUR,ACTIVE>(
true );
386template<
typename T,
typename PARTICLETYPE>
389 T tiltThreshold = 0.3*
M_PI )
391 using namespace particles::access;
392 using namespace descriptors;
393 static_assert(PARTICLETYPE::template providesNested<DYNBEHAVIOUR,DETACHING>(),
394 "Field DYNBEHAVIOUR:DETACHING has to be provided");
395 static_assert(PARTICLETYPE::template providesNested<SURFACE,COR_OFFSET>(),
396 "Field SURFACE:COR_OFFSET has to be provided");
398 auto position = getPosition( particle );
399 auto radius = getRadius( particle );
401 auto normalParticleSurface = getSurfaceNormal( particle );
403 wall, position, normalParticleSurface, radius );
406 if ( tiltIntensity > tiltThreshold){
408 particle.template setField<DYNBEHAVIOUR,DETACHING>(
false );
410 if constexpr( providesComputeContact<PARTICLETYPE>() ){
411 particle.template setField<DYNBEHAVIOUR,COMPUTE_CONTACT>(
true );
425template<
typename T,
typename PARTICLETYPE,
typename DESCRIPTOR>
429 T forceAbsoluteThreshold, T kinEnergyThreshold,
430 T timeNoActiveThreshold, std::size_t iTinterval=1 )
432 using namespace descriptors;
435 T forceAbs =
norm(force);
438 if (forceAbs<forceAbsoluteThreshold){
440 if (kinEnergy<kinEnergyThreshold){
442 std::size_t counterDeactive =
443 particle.template getField<DYNBEHAVIOUR,COUNTER<ACTIVE>>();
445 counterDeactive+=iTinterval;
446 T noActiveTime = converter.
getPhysTime( counterDeactive );
448 if (noActiveTime < timeNoActiveThreshold){
450 particle.template setField<DYNBEHAVIOUR,COUNTER<ACTIVE>>(counterDeactive);
453 particle.template setField<DYNBEHAVIOUR,ACTIVE>(
false);
454 if constexpr( access::providesComputeContact<PARTICLETYPE>() ){
455 particle.template setField<DYNBEHAVIOUR,COMPUTE_CONTACT>(
false);
461 particle.template setField<DYNBEHAVIOUR,COUNTER<ACTIVE>>(0);
class for marking output with some text
Conversion between physical and lattice units, as well as discretization.
constexpr T getPhysTime(size_t latticeTime) const
conversion from lattice to physical time
Vector< T, PARTICLETYPE::d > getForce(Particle< T, PARTICLETYPE > particle)
Vector< T, D > getNormalOnClosestSurface(SolidBoundary< T, D > &solidBoundary, Vector< T, D > &position, T referenceLength)
Vector< T, utilities::dimensions::convert< D >::rotation > getRelativeSurfaceOrientation(SolidBoundary< T, D > &solidBoundary, Vector< T, D > &position, Vector< T, D > &normalToBeComparedTo, T referenceLength)
T calcKineticEnergy(Particle< T, PARTICLETYPE > &particle)
bool checkAdhesion(SolidBoundary< T, PARTICLETYPE::d > &wall, Vector< T, PARTICLETYPE::d > &mainFlowDirection, Particle< T, PARTICLETYPE > &particle)
Check adhesion and return true if still adhering.
Vector< T, 3 > eccentricPosition3D(Vector< T, 3 > &position, Vector< T, 3 > relPosCOR, T angle1DRad, unsigned axis, bool verbose=false)
void getDetachmentAxes(Vector< T, D > mainFlowDirection, Vector< T, D > surfaceNormal, unsigned short &axisFlow, unsigned short &axisSurface, unsigned short &axisRot)
void evaluateDetachmentState(SolidBoundary< T, PARTICLETYPE::d > &wall, Particle< T, PARTICLETYPE > &particle, T tiltThreshold=0.3 *M_PI)
void handleDetachment(SolidBoundary< T, PARTICLETYPE::d > &wall, Vector< T, PARTICLETYPE::d > &mainFlowDirection, Particle< T, PARTICLETYPE > &particle)
void initializeDetachment(SolidBoundary< T, PARTICLETYPE::d > &wall, Particle< T, PARTICLETYPE > &particle, Vector< T, PARTICLETYPE::d > &mainFlowDirection)
T getCuboid3DDiagonalAngle1D(Vector< T, 3 > &extent)
Vector< T, 2 > pos2DOnCyclicHull(Vector< T, 2 > position, T radius, T angle)
void applyAdhesionForce(SolidBoundary< T, PARTICLETYPE::d > &wall, Vector< T, PARTICLETYPE::d > &mainFlowDirection, Particle< T, PARTICLETYPE > &particle)
T getRotationInducedNormalForce(Particle< T, PARTICLETYPE > &particle, Vector< T, PARTICLETYPE::d > &surfaceNormal, Vector< T, PARTICLETYPE::d > &mainFlowDirection)
Calculation of rotation induced normal force.
bool checkParticleReDeposition(Particle< T, PARTICLETYPE > &particle, UnitConverter< T, DESCRIPTOR > const &converter, T forceAbsoluteThreshold, T kinEnergyThreshold, T timeNoActiveThreshold, std::size_t iTinterval=1)
Check particle re-deposition.
void setCORcuboid3Dflush(SolidBoundary< T, PARTICLETYPE::d > &wall, Vector< T, PARTICLETYPE::d > &mainFlowDirection, Particle< T, PARTICLETYPE > &particle)
T angleBetweenVectors(const Vector< T, 2 > &a, const Vector< T, 2 > &b)
Calculates angles between two 2D vectors.
T max_element(const Vector< T, Size > &a)
finds maximum element of all elements
T maxElementAbs(const Vector< T, Size > &a)
finds maximum element of all absolute elements
unsigned maxElementAbsPos(const Vector< T, Size > &a)
finds position of maximum element of all absolute elements
Top level namespace for all of OpenLB.
std::enable_if_t< std::is_arithmetic< T >::type::value, T > abs(T x)
constexpr T norm(const ScalarVector< T, D, IMPL > &a)
Euclidean vector norm.
auto crossProduct(const ScalarVector< T, D, IMPL > &a, const ScalarVector< T, D, IMPL_ > &b)