41 const auto nan = std::numeric_limits<MFloat>::quiet_NaN();
42 for(
MInt i = 0; i < nDim; i++) {
44 m_oldFluidVel[i] = nan;
51 m_angularVel[i] = nan;
52 m_oldAngularVel[i] = nan;
53 m_angularAccel[i] = nan;
54 m_oldAngularAccel[i] = nan;
56 for(
MInt i = 0; i < 4; i++) {
57 m_quaternion[i] = nan;
58 m_oldQuaternion[i] = nan;
70 hasCollided() =
false;
71 hadWallColl() =
false;
72 for(
MInt i = 0; i < nDim; i++) {
73 ASSERT(!isnan(m_fluidVel[i]),
"");
74 m_oldFluidVel[i] = m_fluidVel[i];
76 ASSERT(m_fluidDensity > 0,
"");
77 m_oldFluidDensity = m_fluidDensity;
87 mTerm(1, AT_,
"coupling of invalid particle?");
90 if(!s_backPtr->m_heatCoupling && !s_backPtr->m_evaporation) {
91 m_redistWeight.clear();
92 interpolateVelocityAndDensity(m_cellId, m_position.data(), m_fluidVel.data(), m_fluidDensity, m_redistWeight);
96 for(
MInt i = 0; i < nDim; i++) {
97 ASSERT(!isnan(m_oldFluidVel[i]),
"");
99 ASSERT(m_oldFluidDensity > 0,
"");
103 for(
MInt i = 0; i < nDim; i++) {
104 if(std::isnan(m_accel[i])) {
105 cerr <<
"Invalid motion equation! " << m_partId <<
" " << m_position[0] <<
" " << m_position[1] <<
" "
106 << m_position[2] << endl;
110 if(std::isnan(m_velocity[i])) {
111 cerr <<
"Invalid motion equation2! " << m_partId <<
" " << m_position[0] <<
" " << m_position[1] <<
" "
112 << m_position[2] <<
" " << m_densityRatio <<
" " << hadWallColl() << endl;
119 if(s_backPtr->m_momentumCoupling) momentumCoupling();
120 if(s_backPtr->m_heatCoupling) heatCoupling();
121 if(s_backPtr->m_massCoupling) massCoupling();
130 mTerm(1, AT_,
"Mass coupling not implemented for ellipsoidal particles");
139 const MFloat relT = (s_backPtr->m_timeStep - m_creationTime) / s_backPtr->m_timeStep;
141 const MFloat mass = particleMass();
143 array<MFloat, nDim> force{};
145 for(
MInt i = 0; i < nDim; i++) {
147 const MFloat invDensityRatio = (abs(m_densityRatio) <= MFloatEps) ? 1.0 : 1.0 / m_densityRatio;
148 force[i] = mass * (m_accel[i] - ((1.0 - invDensityRatio) * s_Frm[i])) * relT;
152 const MFloat work = std::inner_product(force.begin(), force.end(), m_oldVel.begin(), 0.0);
155 if(!s_backPtr->m_couplingRedist) {
156 for(
MInt i = 0; i < nDim; i++) {
157 s_backPtr->a_momentumFlux(m_cellId, i) += (force[i]);
159 s_backPtr->a_workFlux(m_cellId) += work;
162 ASSERT(m_redistWeight.size() == m_neighborList.size(),
163 "Invalid " + to_string(m_redistWeight.size()) +
"!=" + to_string(m_neighborList.size()));
164 for(
MInt n = 0; n < (signed)m_neighborList.size(); n++) {
166 for(
MInt i = 0; i < nDim; i++) {
167 s_backPtr->a_momentumFlux(m_neighborList[n], i) += m_redistWeight.at(n) * (force[i]);
169 s_backPtr->a_workFlux(m_neighborList[n]) += m_redistWeight.at(n) * work;
180 mTerm(1, AT_,
"Heat coupling not tested for ellipsoidal particles");
186 const MFloat dt = s_backPtr->m_timeStep - m_creationTime;
187 ASSERT(dt > 0,
"Timestep < 0");
190 m_oldPos = m_position;
191 m_oldVel = m_velocity;
192 m_oldAccel = m_accel;
193 m_oldAngularVel = m_angularVel;
194 m_oldAngularAccel = m_angularAccel;
195 m_oldQuaternion = m_quaternion;
196 m_oldCellId = m_cellId;
199 for(
MInt i = 0; i < nDim; i++) {
200 m_accel[i] = s_Frm[i];
201 m_oldAccel[i] = s_Frm[i];
205 for(
MInt i = 0; i < nDim; i++) {
206 ASSERT(!isnan(m_oldFluidVel[i]),
"");
208 ASSERT(m_oldFluidDensity > 0,
"");
212 const MFloat invDensityRatio =
213 F1 / (
static_cast<MFloat>(ceil(s_backPtr->m_material->density() / m_oldFluidDensity * 1E9)) / 1E9);
215 m_densityRatio = 1 / invDensityRatio;
218 array<MFloat, nDim> predictedPos{};
219 array<MFloat, nDim> predictedVel{};
220 array<MFloat, nDim> predictedAngularVel{};
221 array<MFloat, nDim + 1> predictedQuaternion{};
227 MFloat w = m_oldQuaternion[0];
228 MFloat x = m_oldQuaternion[1];
230 MFloat z = m_oldQuaternion[3];
231 for(
MInt i = 0; i < 3; i++) {
232 q0[i] = m_oldAngularVel[i];
236 for(
MInt i = 0; i < nDim; i++) {
237 predictedVel[i] = m_oldVel[i] + dt * m_oldAccel[i];
238 predictedPos[i] = m_oldPos[i] + dt * (m_oldVel[i]) + F1B2 *
POW2(dt) * (m_oldAccel[i]);
240 predictedQuaternion[0] = w + F1B2 * dt * (-x * q0[0] -
y * q0[1] - z * q0[2]);
241 predictedQuaternion[1] = x + F1B2 * dt * (w * q0[0] - z * q0[1] +
y * q0[2]);
242 predictedQuaternion[2] =
y + F1B2 * dt * (z * q0[0] + w * q0[1] - x * q0[2]);
243 predictedQuaternion[3] = z + F1B2 * dt * (-
y * q0[0] + x * q0[1] + w * q0[2]);
244 for(
MInt i = 0; i < 3; i++) {
245 predictedAngularVel[i] = m_oldAngularVel[i] + dt * m_oldAngularAccel[i];
250 const MInt predictedCellId = s_backPtr->grid().findContainingLeafCell(&predictedPos[0], m_cellId);
252 ASSERT(m_cellId > -1,
"Invalid cell!");
253 ASSERT(predictedCellId > -1,
"Invalid cell!");
254 ASSERT(s_backPtr->c_isLeafCell(predictedCellId),
"Invalid cell!");
256 vector<MFloat> predictedWeights(0);
257 array<MFloat, nDim> fluidVel{};
258 array<MFloat, nDim * nDim> fluidVelGradient{};
259 array<MFloat, nDim * nDim> fluidVelGradientHat{};
260 std::fill(fluidVel.begin(), fluidVel.end(), std::numeric_limits<MFloat>::quiet_NaN());
261 std::fill(fluidVelGradient.begin(), fluidVelGradient.end(), std::numeric_limits<MFloat>::quiet_NaN());
262 std::fill(fluidVelGradientHat.begin(), fluidVelGradientHat.end(), std::numeric_limits<MFloat>::quiet_NaN());
263 interpolateVelocityAndVelocitySlopes(predictedCellId, predictedPos.data(), fluidVel.data(), fluidVelGradient.data(),
269 for(
MInt i = 0; i < nDim; i++) {
273 m_fluidVelMag = sqrt(std::inner_product(fluidVel.begin(), fluidVel.end(), fluidVel.begin(), F0));
275 const MFloat T = s_backPtr->a_fluidTemperature(m_cellId);
277 MFloat fluidViscosity = s_backPtr->m_material->dynViscosityFun(T);
280 if(s_backPtr->m_dragModelType == 0) {
282 for(
MInt i = 0; i < nDim; i++) {
283 m_velocity[i] = predictedVel[i];
284 m_position[i] = predictedPos[i];
285 m_angularVel[i] = predictedAngularVel[i];
286 m_quaternion[i] = predictedQuaternion[i];
287 m_accel[i] = m_oldAccel[i];
288 m_angularAccel[i] = m_oldAngularAccel[i];
291 switch(s_backPtr->m_motionEquationType) {
294 for(
MInt i = 0; i < nDim; i++) {
295 m_position[i] = m_oldPos[i] + F1B2 * dt * (fluidVel[i] + m_oldVel[i]) * s_lengthFactor;
296 m_velocity[i] = fluidVel[i];
297 m_accel[i] = (m_velocity[i] - m_oldVel[i]) / dt;
298 m_angularVel[i] = F0;
299 m_angularAccel[i] = F0;
306 const MFloat relVel = magRelVel(&fluidVel[0], &predictedVel[0]);
308 const MFloat Rep = particleRe(relVel, m_oldFluidDensity, fluidViscosity) * s_Re;
310 const MFloat CD = dragFactor(Rep) / s_Re * fParticleRelTime(fluidViscosity);
312 for(
MInt i = 0; i < nDim; i++) {
313 m_accel[i] = CD * (fluidVel[i] - predictedVel[i]) + (1.0 - invDensityRatio) * s_Frm[i];
314 m_velocity[i] = m_oldVel[i] + dt * F1B2 * (m_accel[i] + m_oldAccel[i]);
315 m_position[i] = m_oldPos[i] + dt * F1B2 * (m_velocity[i] + m_oldVel[i]) * s_lengthFactor;
321 const MFloat fdtB2 = F1B2 * dt;
333 const MFloat beta = m_aspectRatio;
335 const MFloat fTauP = fParticleRelTime(fluidViscosity) / s_Re;
337 const MFloat linAccFac = (8.0 / 3.0) * pow(beta, F2B3) * fTauP;
338 const MFloat angAccFac = (40.0 / 9.0) * pow(beta, F2B3) * fTauP;
341 const MInt maxit = 100;
346 for(
MInt i = 0; i < 3; i++) {
347 tq0[i] = m_oldAngularAccel[i];
348 q0[i] = m_oldAngularVel[i];
354 for(
MInt i = 0; i < 3; i++) {
355 MInt id0 = (i + 1) % 3;
356 MInt id1 = (id0 + 1) % 3;
357 strain[i] = F1B2 * (fluidVelGradientHat[3 * id1 + id0] + fluidVelGradientHat[3 * id0 + id1]);
358 vort[i] = F1B2 * (fluidVelGradientHat[3 * id1 + id0] - fluidVelGradientHat[3 * id0 + id1]);
361 W(0, 0) = F1 + fdtB2 * angAccFac / (m_shapeParams[1] + beta2 * m_shapeParams[3]);
362 W(1, 1) = F1 + fdtB2 * angAccFac / (m_shapeParams[1] + beta2 * m_shapeParams[3]);
363 W(2, 2) = F1 + fdtB2 * angAccFac / (F2 * m_shapeParams[1]);
368 while(delta > 1e-10 && it < maxit) {
369 W(0, 1) = -fdtB2 * q[2] * (beta2 - F1) / (beta2 + F1);
370 W(0, 2) = -fdtB2 * q[1] * (beta2 - F1) / (beta2 + F1);
371 W(1, 0) = -fdtB2 * q[2] * (F1 - beta2) / (beta2 + F1);
372 W(1, 2) = -fdtB2 * q[0] * (F1 - beta2) / (beta2 + F1);
377 tq[0] = q[1] * q[2] * (beta2 - F1) / (beta2 + F1)
378 + angAccFac * (((F1 - beta2) / (F1 + beta2)) * strain[0] + vort[0] - q[0])
379 / (m_shapeParams[1] + beta2 * m_shapeParams[3]);
380 tq[1] = q[0] * q[2] * (F1 - beta2) / (beta2 + F1)
381 + angAccFac * (((beta2 - F1) / (F1 + beta2)) * strain[1] + vort[1] - q[1])
382 / (m_shapeParams[1] + beta2 * m_shapeParams[3]);
383 tq[2] = angAccFac * (vort[2] - q[2]) / (F2 * m_shapeParams[1]);
385 for(
MInt i = 0; i < 3; i++)
386 rhs[i] = q[i] - q0[i] - fdtB2 * (tq0[i] + tq[i]);
389 for(
MInt i = 0; i < 3; i++) {
391 for(
MInt j = 0; j < 3; j++) {
392 q[i] -= iW(i, j) * rhs(j);
394 delta =
mMax(delta, fabs(q[i] - qq));
399 if(it >= maxit || it <= 0) {
400 cerr <<
"Newton iterations did not converge " << m_partId << endl;
403 for(
MInt i = 0; i < 3; i++) {
404 m_angularVel[i] = q[i];
405 m_angularAccel[i] = tq[i];
409 w = m_oldQuaternion[0];
410 x = m_oldQuaternion[1];
411 y = m_oldQuaternion[2];
412 z = m_oldQuaternion[3];
431 for(
MInt i = 0; i < 4; i++) {
432 for(
MInt j = 0; j < 4; j++) {
433 W(i, j) = -F1B2 * fdtB2 * W(i, j);
438 rhs(0) = w + F1B2 * fdtB2 * (-x * q0[0] -
y * q0[1] - z * q0[2]);
439 rhs(1) = x + F1B2 * fdtB2 * (w * q0[0] - z * q0[1] +
y * q0[2]);
440 rhs(2) =
y + F1B2 * fdtB2 * (z * q0[0] + w * q0[1] - x * q0[2]);
441 rhs(3) = z + F1B2 * fdtB2 * (-
y * q0[0] + x * q0[1] + w * q0[2]);
445 for(
MInt i = 0; i < 4; i++) {
446 m_quaternion[i] = F0;
447 for(
MInt j = 0; j < 4; j++) {
448 m_quaternion[i] += iW(i, j) * rhs(j);
453 for(
MInt i = 0; i < 4; i++)
454 abs +=
POW2(m_quaternion[i]);
455 for(
MInt i = 0; i < 4; i++)
456 m_quaternion[i] /= sqrt(abs);
460 K(0, 0) = F1 / (m_shapeParams[0] /
POW2(m_semiMinorAxis) + m_shapeParams[1]);
462 K(2, 2) = F1 / (m_shapeParams[0] /
POW2(m_semiMinorAxis) + beta2 * m_shapeParams[3]);
465 for(
MInt i = 0; i < nDim; i++) {
466 vrel[i] = fluidVel[i] - predictedVel[i];
473 for(
MInt i = 0; i < nDim; i++) {
474 m_accel[i] = linAccFac * vrel[i] + (1.0 - invDensityRatio) * s_Frm[i];
475 m_velocity[i] = m_oldVel[i] + dt * F1B2 * (m_accel[i] + m_oldAccel[i]);
476 m_position[i] = m_oldPos[i] + dt * F1B2 * (m_velocity[i] + m_oldVel[i]) * s_lengthFactor
477 + F1B4 *
POW2(dt) * (m_accel[i] + m_oldAccel[i]);
483 const MFloat fdtB2 = F1B2 * dt;
495 const MFloat beta = m_aspectRatio;
497 const MFloat eqRadius = F1B2 * m_eqDiameter;
498 const MFloat fTauP = fParticleRelTime(fluidViscosity) / s_Re;
499 const MFloat magRelVeloc = magRelVel(&fluidVel[0], &predictedVel[0]);
500 const MFloat Rep = particleRe(magRelVeloc, m_oldFluidDensity, fluidViscosity) * s_Re;
502 const MFloat angAccFac = (40.0 / 9.0) * pow(beta, F2B3) * fTauP;
503 const MFloat pitchingTorqueFac = (15.0 / 8.0) * invDensityRatio * pow(beta, F2B3) /
POW2(eqRadius);
505 48.0 * fluidViscosity / (s_Re * s_backPtr->m_material->density() *
POW2(m_eqDiameter)) * pow(beta, F2B3);
510 for(
MInt i = 0; i < nDim; i++) {
511 vrel[i] = fluidVel[i] - predictedVel[i];
518 const MFloat magRelVelHat = sqrt(
POW2(vrelhat[0]) +
POW2(vrelhat[1]) +
POW2(vrelhat[2]));
520 const auto nan = std::numeric_limits<MFloat>::quiet_NaN();
521 MFloat inclinationAngle = nan;
522 std::array<MFloat, 3> accHat = {nan, nan, nan};
523 std::array<MFloat, 3> accDragHat = {nan, nan, nan};
524 std::array<MFloat, 3> accLiftHat = {nan, nan, nan};
525 std::array<MFloat, 3> dirDHat = {nan, nan, nan};
526 std::array<MFloat, 3> dirTHat = {nan, nan, nan};
527 std::array<MFloat, 3> dirLHat = {nan, nan, nan};
528 std::array<MFloat, 3> dirDCrossZ = {nan, nan, nan};
529 std::array<MFloat, 3> dirZHat{nan, nan, nan};
532 for(
MInt i = 0; i < nDim; i++) {
533 i == m_particleMajorAxis ? dirZHat[i] = F1 : dirZHat[i] = F0;
536 for(
MInt i = 0; i < nDim; i++) {
537 dirDHat[i] = vrelhat[i] / magRelVelHat;
541 const MFloat dotProduct = std::inner_product(dirDHat.begin(), dirDHat.end(), dirZHat.begin(), F0);
542 inclinationAngle = acos(fabs(dotProduct));
543 MInt sgn = (dotProduct > 0 ? 1 : -1);
544 for(
MInt i = 0; i < nDim; i++) {
545 dirTHat[i] = dirDCrossZ[i] / sqrt(
POW2(dirDCrossZ[0]) +
POW2(dirDCrossZ[1]) +
POW2(dirDCrossZ[2])) * sgn;
551 const MInt maxit = 100;
555 W(0, 0) = F1 + fdtB2 * angAccFac / (m_shapeParams[1] + beta2 * m_shapeParams[3]);
556 W(1, 1) = F1 + fdtB2 * angAccFac / (m_shapeParams[1] + beta2 * m_shapeParams[3]);
557 W(2, 2) = F1 + fdtB2 * angAccFac / (F2 * m_shapeParams[1]);
562 for(
MInt i = 0; i < 3; i++) {
563 tq0[i] = m_oldAngularAccel[i];
564 q0[i] = m_oldAngularVel[i];
571 if(s_backPtr->m_torqueModelType > 0 && Rep > 1e-8) {
572 MFloat CT = torqueFactor(Rep, beta, inclinationAngle);
573 for(
MInt i = 0; i < 3; i++) {
574 pitch[i] = pitchingTorqueFac *
POW2(magRelVelHat) * CT / momI[i] * dirTHat[i];
575 if(pitchingTorqueFac <= F0 || CT <= F0 || momI[i] <= F0)
576 cout <<
"Calc pitching torque " << pitchingTorqueFac <<
" " << CT <<
" " << momI[i] << endl;
581 for(
MInt i = 0; i < 3; i++) {
582 MInt id0 = (i + 1) % 3;
583 MInt id1 = (id0 + 1) % 3;
584 strain[i] = F1B2 * (fluidVelGradientHat[3 * id1 + id0] + fluidVelGradientHat[3 * id0 + id1]);
585 vort[i] = F1B2 * (fluidVelGradientHat[3 * id1 + id0] - fluidVelGradientHat[3 * id0 + id1]);
589 while(delta > 1e-10 && it < maxit) {
590 W(0, 1) = -fdtB2 * q[2] * (beta2 - F1) / (beta2 + F1);
591 W(0, 2) = -fdtB2 * q[1] * (beta2 - F1) / (beta2 + F1);
592 W(1, 0) = -fdtB2 * q[2] * (F1 - beta2) / (beta2 + F1);
593 W(1, 2) = -fdtB2 * q[0] * (F1 - beta2) / (beta2 + F1);
597 tq[0] = q[1] * q[2] * (beta2 - F1) / (beta2 + F1)
598 + angAccFac * (((F1 - beta2) / (F1 + beta2)) * strain[0] + vort[0] - q[0])
599 / (m_shapeParams[1] + beta2 * m_shapeParams[3])
601 tq[1] = q[0] * q[2] * (F1 - beta2) / (beta2 + F1)
602 + angAccFac * (((beta2 - F1) / (F1 + beta2)) * strain[1] + vort[1] - q[1])
603 / (m_shapeParams[1] + beta2 * m_shapeParams[3])
605 tq[2] = angAccFac * (vort[2] - q[2]) / (F2 * m_shapeParams[1]) + pitch[2];
607 for(
MInt i = 0; i < 3; i++)
608 rhs[i] = q[i] - q0[i] - fdtB2 * (tq0[i] + tq[i]);
611 for(
MInt i = 0; i < 3; i++) {
613 for(
MInt j = 0; j < 3; j++) {
614 q[i] -= iW(i, j) * rhs(j);
616 delta =
mMax(delta, fabs(q[i] - qq));
621 if(it >= maxit || it <= 0) {
622 cerr <<
"Newton iterations did not converge " << m_partId << endl;
625 for(
MInt i = 0; i < 3; i++) {
626 m_angularVel[i] = q[i];
627 m_angularAccel[i] = tq[i];
631 w = m_oldQuaternion[0];
632 x = m_oldQuaternion[1];
633 y = m_oldQuaternion[2];
634 z = m_oldQuaternion[3];
653 for(
MInt i = 0; i < 4; i++) {
654 for(
MInt j = 0; j < 4; j++) {
655 W(i, j) = -F1B2 * fdtB2 * W(i, j);
660 rhs(0) = w + F1B2 * fdtB2 * (-x * q0[0] -
y * q0[1] - z * q0[2]);
661 rhs(1) = x + F1B2 * fdtB2 * (w * q0[0] - z * q0[1] +
y * q0[2]);
662 rhs(2) =
y + F1B2 * fdtB2 * (z * q0[0] + w * q0[1] - x * q0[2]);
663 rhs(3) = z + F1B2 * fdtB2 * (-
y * q0[0] + x * q0[1] + w * q0[2]);
667 for(
MInt i = 0; i < 4; i++) {
668 m_quaternion[i] = F0;
669 for(
MInt j = 0; j < 4; j++) {
670 m_quaternion[i] += iW(i, j) * rhs(j);
675 for(
MInt i = 0; i < 4; i++)
676 abs +=
POW2(m_quaternion[i]);
677 for(
MInt i = 0; i < 4; i++)
678 m_quaternion[i] /= sqrt(abs);
682 K(0, 0) = F1 / (m_shapeParams[0] /
POW2(m_semiMinorAxis) + m_shapeParams[1]);
684 K(2, 2) = F1 / (m_shapeParams[0] /
POW2(m_semiMinorAxis) + beta2 * m_shapeParams[3]);
689 MFloat facCL = liftFactor(Rep, beta, inclinationAngle, K(0, 0), K(2, 2));
690 MFloat facCD = dragFactor(Rep, beta, inclinationAngle, K(0, 0), K(2, 2));
691 for(
MInt i = 0; i < nDim; i++) {
692 accLiftHat[i] = addFacCd * facCL * magRelVelHat * dirLHat[i];
693 accDragHat[i] = addFacCd * facCD * magRelVelHat * dirDHat[i];
694 accHat[i] = accDragHat[i] + accLiftHat[i];
698 for(
MInt i = 0; i < nDim; i++) {
699 m_accel[i] = accDrag[i] + (1.0 - invDensityRatio) * s_Frm[i];
700 m_velocity[i] = m_oldVel[i] + dt * F1B2 * (m_accel[i] + m_oldAccel[i]);
701 m_position[i] = m_oldPos[i] + dt * F1B2 * (m_velocity[i] + m_oldVel[i]) * s_lengthFactor
702 + F1B4 *
POW2(dt) * (m_accel[i] + m_oldAccel[i]);
707 mTerm(1, AT_,
"Unknown particle corrector equation type!");
713 if(m_partId == debugPartId) {
714 cerr <<
"AT " << m_velocity[0] <<
" " << m_velocity[1] <<
" " << m_velocity[2] << endl;
719 checkCellChange(&m_oldPos[0]);
722 for(
MInt i = 0; i < nDim; i++) {
723 if(std::isnan(m_accel[i])) {
724 cerr <<
"Nan acc. for " << s_backPtr->domainId() <<
" " << m_partId <<
" " << m_position[0] <<
" "
725 << m_position[1] <<
" " << m_position[nDim - 1] <<
" " << s_backPtr->a_isValidCell(m_cellId) <<
" "
726 << m_oldPos[0] <<
" " << m_oldPos[1] <<
" " << m_oldPos[nDim - 1] <<
" " << m_creationTime <<
" "
727 << fluidVel[0] <<
" " << fluidVel[1] <<
" " << fluidVel[nDim - 1] << fluidDensity <<
" " << T <<
" "
728 << fluidViscosity <<
" " << m_oldVel[0] <<
" " << m_oldVel[1] <<
" " << m_oldVel[nDim - 1] << endl;
744 switch(s_backPtr->m_dragModelType) {
754 result = 1.0 + 0.15 * pow(partRe, 0.687);
758 std::array<MFloat, 8> dCorrs{-0.007, 1.0, 1.17, -0.07, 0.047, 1.14, 0.7, -0.008};
761 fd0 = 1.0 + 0.15 * pow(partRe, 0.687)
762 + dCorrs[0] * pow(std::log(beta), dCorrs[1]) * pow(partRe, dCorrs[2] + dCorrs[3] * std::log(beta));
763 fd90 = 1.0 + 0.15 * pow(partRe, 0.687)
764 + dCorrs[4] * pow(log(beta), dCorrs[5]) * pow(partRe, dCorrs[6] + dCorrs[7] * log(beta));
768 result = fd0K2 + (f90K0 - fd0K2) *
POW2(sin(inclinationAngle));
772 mTerm(1, AT_,
"Unknown particle drag method");
787 switch(s_backPtr->m_liftModelType) {
797 std::array<MFloat, 6> lCorrs{0.01, 0.86, 1.77, 0.34, 0.88, -0.05};
800 if(partRe > 1) flShift += lCorrs[0] * pow(log(beta), lCorrs[1]) * pow(log(partRe), lCorrs[2]);
801 MFloat psi = F1B2 * M_PI * pow(inclinationAngle / M_PI * F2, flShift);
802 flMax = F1 + lCorrs[3] * pow(partRe, lCorrs[4] + lCorrs[5]);
803 MFloat ClMax = F1B2 * flMax * (K0 - K2);
804 MFloat Cl = F2 * sin(psi) * cos(psi) * ClMax;
809 mTerm(1, AT_,
"Unknown particle drag method");
823 switch(s_backPtr->m_torqueModelType) {
833 std::array<MFloat, 7> tCorrs{0.931, 0.675, 0.162, 0.657, 2.77, 0.178, 0.177};
834 MFloat CTMax = tCorrs[0] * pow(log(beta), tCorrs[1]) / pow(partRe, tCorrs[2])
835 + tCorrs[3] * pow(log(beta), tCorrs[4]) / pow(partRe, tCorrs[5] + tCorrs[6] * log(beta));
836 MFloat CT = F2 * sin(inclinationAngle) * cos(inclinationAngle) * CTMax;
841 mTerm(1, AT_,
"Unknown particle torque method");
855 MFloat beta = m_aspectRatio;
856 MFloat beta2 = beta * beta;
857 if(fabs(beta - F1) < 1e-14) {
859 m_shapeParams[0] = F2 *
POW2(m_semiMinorAxis);
860 m_shapeParams[1] = F2B3;
861 m_shapeParams[2] = F2B3;
862 m_shapeParams[3] = F2B3;
863 }
else if(beta > F1) {
865 MFloat kappa = log((beta - sqrt(beta2 - F1)) / (beta + sqrt(beta2 - F1)));
866 m_shapeParams[0] = -beta *
POW2(m_semiMinorAxis) * kappa / sqrt(beta2 - F1);
867 m_shapeParams[1] = beta2 / (beta2 - F1) + beta * kappa / (F2 * pow(beta2 - F1, F3B2));
868 m_shapeParams[2] = beta2 / (beta2 - F1) + beta * kappa / (F2 * pow(beta2 - F1, F3B2));
869 m_shapeParams[3] = -F2 / (beta2 - F1) - beta * kappa / (pow(beta2 - F1, F3B2));
870 }
else if(beta < F1) {
872 MFloat kappa = F2 * atan2(beta, sqrt(F1 - beta2));
873 m_shapeParams[0] = beta *
POW2(m_semiMinorAxis) * (PI - kappa) / sqrt(F1 - beta2);
874 m_shapeParams[1] = -beta * (kappa - PI + F2 * beta * sqrt(F1 - beta2)) / (F2 * pow(F1 - beta2, F3B2));
875 m_shapeParams[1] = -beta * (kappa - PI + F2 * beta * sqrt(F1 - beta2)) / (F2 * pow(F1 - beta2, F3B2));
876 m_shapeParams[1] = (beta * kappa - beta * PI + F2 * sqrt(F1 - beta2)) / (pow(F1 - beta2, F3B2));
879 mTerm(1,
"Generalize here");
886 mTerm(1, AT_,
"Not yet implemented");
897 std::array<MFloat, nDim> majorAxisParticleFixed{};
898 for(
MInt i = 0; i < nDim; i++) {
899 i == m_particleMajorAxis ? majorAxisParticleFixed[i] = maxParticleRadius() : majorAxisParticleFixed[i] = F0;
MFloat liftFactor(const MFloat partRe, const MFloat beta, const MFloat inclinationAngle, const MFloat K0, const MFloat K2)
Calculates the lift factor for current particle with given Re number, aspect ratio,...
LPTEllipsoidal()
constructor of ellipsoidal particles
MFloat torqueFactor(const MFloat partRe, const MFloat beta, const MFloat inclinationAngle)
Calculates the torque factor for current particle with given Re number, aspect ratio and inclination ...
void calculateMajorAxisOrientation(MFloat *majorAxis)
Calculate the orientation of the major axis in the world coordinate system.
void motionEquation() override
void heatCoupling()
add the heat flux from the particle to the cell/all surrounding cells
void initShapeParams()
Calcultes shape parameters for current particle (see Siewert, 2014)
void coupling() override
single particle coupling terms
void momentumCoupling()
add the momentum flux from the particle to the cell/all surrounding cells
void advanceParticle() override
advance to new timeStep
void energyEquation() override
MFloat dragFactor(const MFloat partRe, const MFloat beta=1, const MFloat inclinationAngle=0, const MFloat K0=0, const MFloat K2=0)
Calculates the drag factor for current particle with given Re number, aspect ratio and inclination an...
void massCoupling()
add the mass flux from the particle to the cell/all surrounding cells
This class is a ScratchSpace.
void fill(T val)
fill the scratch with a given value
void mTerm(const MInt errorCode, const MString &location, const MString &message)
constexpr Real POW2(const Real x)
constexpr T mMax(const T &x, const T &y)
void cross(const T *const u, const T *const v, T *const c)
void matrixVectorProduct(MFloat *c, MFloatScratchSpace &A, MFloat *b)
c=A*b
void invert(MFloat *A, const MInt m, const MInt n)
void computeRotationMatrix(MFloatScratchSpace &R, MFloat *q)
rotation matrix co-rotating(~inertial) frame -> body-fixed frame
void matrixVectorProductTranspose(MFloat *c, MFloatScratchSpace &A, MFloat *b)
c=A^t*b