void Foam::PairSpringSliderDashpot<CloudType>::evaluatePair ( typename CloudType::parcelType& pA, typename CloudType::parcelType& pB ) const { vector r_AB = (pA.position() - pB.position()); scalar dAEff = pA.d(); if (useEquivalentSize_) { dAEff *= cbrt(pA.nParticle()*volumeFactor_); } scalar dBEff = pB.d(); if (useEquivalentSize_) { dBEff *= cbrt(pB.nParticle()*volumeFactor_); } scalar r_AB_mag = mag(r_AB); scalar normalOverlapMag = 0.5*(dAEff + dBEff) - r_AB_mag; if (normalOverlapMag > 0) { //Particles in collision vector rHat_AB = r_AB/(r_AB_mag + VSMALL); vector U_AB = pA.U() - pB.U(); // Effective radius scalar R = 0.5*dAEff*dBEff/(dAEff + dBEff); // Effective mass scalar M = pA.mass()*pB.mass()/(pA.mass() + pB.mass()); scalar kN = (4.0/3.0)*sqrt(R)*Estar_; scalar etaN = alpha_*sqrt(M*kN)*pow025(normalOverlapMag); // Normal force vector fN_AB = rHat_AB *(kN*pow(normalOverlapMag, b_) - etaN*(U_AB & rHat_AB)); // Cohesion force if (cohesion_) { fN_AB += -cohesionEnergyDensity_ *overlapArea(dAEff/2.0, dBEff/2.0, r_AB_mag) *rHat_AB; } pA.f() += fN_AB; pB.f() += -fN_AB; vector USlip_AB = U_AB - (U_AB & rHat_AB)*rHat_AB + (pA.omega() ^ (dAEff/2*-rHat_AB)) - (pB.omega() ^ (dBEff/2*rHat_AB)); scalar deltaT = this->owner().mesh().time().deltaTValue(); vector& tangentialOverlap_AB = pA.collisionRecords().matchPairRecord ( pB.origProc(), pB.origId() ).collisionData(); vector& tangentialOverlap_BA = pB.collisionRecords().matchPairRecord ( pA.origProc(), pA.origId() ).collisionData(); vector deltaTangentialOverlap_AB = USlip_AB*deltaT; tangentialOverlap_AB += deltaTangentialOverlap_AB; tangentialOverlap_BA += -deltaTangentialOverlap_AB; scalar tangentialOverlapMag = mag(tangentialOverlap_AB); if (tangentialOverlapMag > VSMALL) { scalar kT = 8.0*sqrt(R*normalOverlapMag)*Gstar_; scalar etaT = etaN; // Tangential force vector fT_AB; if (kT*tangentialOverlapMag > mu_*mag(fN_AB)) { // Tangential force greater than sliding friction, // particle slips fT_AB = -mu_*mag(fN_AB)*USlip_AB/mag(USlip_AB); tangentialOverlap_AB = vector::zero; tangentialOverlap_BA = vector::zero; } else { fT_AB = -kT*tangentialOverlapMag *tangentialOverlap_AB/tangentialOverlapMag - etaT*USlip_AB; } pA.f() += fT_AB; pB.f() += -fT_AB; pA.torque() += (dAEff/2*-rHat_AB) ^ fT_AB; pB.torque() += (dBEff/2*rHat_AB) ^ -fT_AB; } } }
void Foam::MaxwellianThermal<CloudType>::correct ( typename CloudType::parcelType& p, const wallPolyPatch& wpp ) { vector& U = p.U(); scalar& Ei = p.Ei(); label typeId = p.typeId(); label wppIndex = wpp.index(); label wppLocalFace = wpp.whichFace(p.face()); vector nw = p.normal(); nw /= mag(nw); // Normal velocity magnitude scalar U_dot_nw = U & nw; // Wall tangential velocity (flow direction) vector Ut = U - U_dot_nw*nw; CloudType& cloud(this->owner()); Random& rndGen(cloud.rndGen()); while (mag(Ut) < SMALL) { // If the incident velocity is parallel to the face normal, no // tangential direction can be chosen. Add a perturbation to the // incoming velocity and recalculate. U = vector ( U.x()*(0.8 + 0.2*rndGen.scalar01()), U.y()*(0.8 + 0.2*rndGen.scalar01()), U.z()*(0.8 + 0.2*rndGen.scalar01()) ); U_dot_nw = U & nw; Ut = U - U_dot_nw*nw; } // Wall tangential unit vector vector tw1 = Ut/mag(Ut); // Other tangential unit vector vector tw2 = nw^tw1; scalar T = cloud.boundaryT().boundaryField()[wppIndex][wppLocalFace]; scalar mass = cloud.constProps(typeId).mass(); scalar iDof = cloud.constProps(typeId).internalDegreesOfFreedom(); U = sqrt(physicoChemical::k.value()*T/mass) *( rndGen.GaussNormal()*tw1 + rndGen.GaussNormal()*tw2 - sqrt(-2.0*log(max(1 - rndGen.scalar01(), VSMALL)))*nw ); U += cloud.boundaryU().boundaryField()[wppIndex][wppLocalFace]; Ei = cloud.equipartitionInternalEnergy(T, iDof); }
void Foam::MixedDiffuseSpecular<CloudType>::correct ( typename CloudType::parcelType& p ) { vector& U = p.U(); scalar& Ei = p.Ei(); label typeId = p.typeId(); const label wppIndex = p.patch(); const polyPatch& wpp = p.mesh().boundaryMesh()[wppIndex]; label wppLocalFace = wpp.whichFace(p.face()); const vector nw = p.normal(); // Normal velocity magnitude scalar U_dot_nw = U & nw; CloudType& cloud(this->owner()); Random& rndGen(cloud.rndGen()); if (diffuseFraction_ > rndGen.scalar01()) { // Diffuse reflection // Wall tangential velocity (flow direction) vector Ut = U - U_dot_nw*nw; while (mag(Ut) < small) { // If the incident velocity is parallel to the face normal, no // tangential direction can be chosen. Add a perturbation to the // incoming velocity and recalculate. U = vector ( U.x()*(0.8 + 0.2*rndGen.scalar01()), U.y()*(0.8 + 0.2*rndGen.scalar01()), U.z()*(0.8 + 0.2*rndGen.scalar01()) ); U_dot_nw = U & nw; Ut = U - U_dot_nw*nw; } // Wall tangential unit vector vector tw1 = Ut/mag(Ut); // Other tangential unit vector vector tw2 = nw^tw1; scalar T = cloud.boundaryT().boundaryField()[wppIndex][wppLocalFace]; scalar mass = cloud.constProps(typeId).mass(); direction iDof = cloud.constProps(typeId).internalDegreesOfFreedom(); U = sqrt(physicoChemical::k.value()*T/mass) *( rndGen.scalarNormal()*tw1 + rndGen.scalarNormal()*tw2 - sqrt(-2.0*log(max(1 - rndGen.scalar01(), vSmall)))*nw ); U += cloud.boundaryU().boundaryField()[wppIndex][wppLocalFace]; Ei = cloud.equipartitionInternalEnergy(T, iDof); } else { // Specular reflection if (U_dot_nw > 0.0) { U -= 2.0*U_dot_nw*nw; } } }
void Foam::WallLocalSpringSliderDashpot<CloudType>::evaluateWall ( typename CloudType::parcelType& p, const point& site, const WallSiteData<vector>& data, scalar pREff ) const { // wall patch index label wPI = patchMap_[data.patchIndex()]; // data for this patch scalar Estar = Estar_[wPI]; scalar Gstar = Gstar_[wPI]; scalar alpha = alpha_[wPI]; scalar b = b_[wPI]; scalar mu = mu_[wPI]; vector r_PW = p.position() - site; vector U_PW = p.U() - data.wallData(); scalar normalOverlapMag = max(pREff - mag(r_PW), 0.0); vector rHat_PW = r_PW/(mag(r_PW) + VSMALL); scalar kN = (4.0/3.0)*sqrt(pREff)*Estar; scalar etaN = alpha*sqrt(p.mass()*kN)*pow025(normalOverlapMag); vector fN_PW = rHat_PW *(kN*pow(normalOverlapMag, b) - etaN*(U_PW & rHat_PW)); p.f() += fN_PW; vector USlip_PW = U_PW - (U_PW & rHat_PW)*rHat_PW + (p.omega() ^ (pREff*-rHat_PW)); scalar deltaT = this->owner().mesh().time().deltaTValue(); vector& tangentialOverlap_PW = p.collisionRecords().matchWallRecord(-r_PW, pREff).collisionData(); tangentialOverlap_PW += USlip_PW*deltaT; scalar tangentialOverlapMag = mag(tangentialOverlap_PW); if (tangentialOverlapMag > VSMALL) { scalar kT = 8.0*sqrt(pREff*normalOverlapMag)*Gstar; scalar etaT = etaN; // Tangential force vector fT_PW; if (kT*tangentialOverlapMag > mu*mag(fN_PW)) { // Tangential force greater than sliding friction, // particle slips fT_PW = -mu*mag(fN_PW)*USlip_PW/mag(USlip_PW); tangentialOverlap_PW = vector::zero; } else { fT_PW = -kT*tangentialOverlapMag *tangentialOverlap_PW/tangentialOverlapMag - etaT*USlip_PW; } p.f() += fT_PW; p.torque() += (pREff*-rHat_PW) ^ fT_PW; } }
void Foam::LarsenBorgnakkeVariableHardSphere<CloudType>::collide ( typename CloudType::parcelType& pP, typename CloudType::parcelType& pQ ) { CloudType& cloud(this->owner()); label typeIdP = pP.typeId(); label typeIdQ = pQ.typeId(); vector& UP = pP.U(); vector& UQ = pQ.U(); scalar& EiP = pP.Ei(); scalar& EiQ = pQ.Ei(); Random& rndGen(cloud.rndGen()); scalar inverseCollisionNumber = 1/relaxationCollisionNumber_; // Larsen Borgnakke internal energy redistribution part. Using the serial // application of the LB method, as per the INELRS subroutine in Bird's // DSMC0R.FOR scalar preCollisionEiP = EiP; scalar preCollisionEiQ = EiQ; direction iDofP = cloud.constProps(typeIdP).internalDegreesOfFreedom(); direction iDofQ = cloud.constProps(typeIdQ).internalDegreesOfFreedom(); scalar omegaPQ = 0.5 *( cloud.constProps(typeIdP).omega() + cloud.constProps(typeIdQ).omega() ); scalar mP = cloud.constProps(typeIdP).mass(); scalar mQ = cloud.constProps(typeIdQ).mass(); scalar mR = mP*mQ/(mP + mQ); vector Ucm = (mP*UP + mQ*UQ)/(mP + mQ); scalar cRsqr = magSqr(UP - UQ); scalar availableEnergy = 0.5*mR*cRsqr; scalar ChiB = 2.5 - omegaPQ; if (iDofP > 0) { if (inverseCollisionNumber > rndGen.scalar01()) { availableEnergy += preCollisionEiP; if (iDofP == 2) { scalar energyRatio = 1.0 - pow(rndGen.scalar01(), (1.0/ChiB)); EiP = energyRatio*availableEnergy; } else { scalar ChiA = 0.5*iDofP; EiP = energyRatio(ChiA, ChiB)*availableEnergy; } availableEnergy -= EiP; } } if (iDofQ > 0) { if (inverseCollisionNumber > rndGen.scalar01()) { availableEnergy += preCollisionEiQ; if (iDofQ == 2) { scalar energyRatio = 1.0 - pow(rndGen.scalar01(), (1.0/ChiB)); EiQ = energyRatio*availableEnergy; } else { scalar ChiA = 0.5*iDofQ; EiQ = energyRatio(ChiA, ChiB)*availableEnergy; } availableEnergy -= EiQ; } } // Rescale the translational energy scalar cR = sqrt(2.0*availableEnergy/mR); // Variable Hard Sphere collision part scalar cosTheta = 2.0*rndGen.scalar01() - 1.0; scalar sinTheta = sqrt(1.0 - cosTheta*cosTheta); scalar phi = twoPi*rndGen.scalar01(); vector postCollisionRelU = cR *vector ( cosTheta, sinTheta*cos(phi), sinTheta*sin(phi) ); UP = Ucm + postCollisionRelU*mQ/(mP + mQ); UQ = Ucm - postCollisionRelU*mP/(mP + mQ); }
bool Foam::StandardWallInteraction<CloudType>::correct ( typename CloudType::parcelType& p, const polyPatch& pp, bool& keepParticle, const scalar trackFraction, const tetIndices& tetIs ) { vector& U = p.U(); bool& active = p.active(); if (isA<wallPolyPatch>(pp)) { switch (interactionType_) { case PatchInteractionModel<CloudType>::itEscape: { scalar dm = p.mass()*p.nParticle(); keepParticle = false; active = false; U = vector::zero; nEscape_++; massEscape_ += dm; break; } case PatchInteractionModel<CloudType>::itStick: { keepParticle = true; active = false; U = vector::zero; nStick_++; break; } case PatchInteractionModel<CloudType>::itRebound: { keepParticle = true; active = true; vector nw; vector Up; this->owner().patchData(p, pp, trackFraction, tetIs, nw, Up); // Calculate motion relative to patch velocity U -= Up; scalar Un = U & nw; vector Ut = U - Un*nw; if (Un > 0) { U -= (1.0 + e_)*Un*nw; } U -= mu_*Ut; // Return velocity to global space U += Up; break; } default: { FatalErrorIn ( "bool StandardWallInteraction<CloudType>::correct" "(" "typename CloudType::parcelType&, " "const polyPatch&, " "bool& keepParticle, " "const scalar, " "const tetIndices&" ") const" ) << "Unknown interaction type " << this->interactionTypeToWord(interactionType_) << "(" << interactionType_ << ")" << endl << abort(FatalError); } } return true; } return false; }
bool Foam::LocalInteraction<CloudType>::correct ( typename CloudType::parcelType& p, const polyPatch& pp, bool& keepParticle, const scalar trackFraction, const tetIndices& tetIs ) { label patchI = patchData_.applyToPatch(pp.index()); if (patchI >= 0) { vector& U = p.U(); bool& active = p.active(); typename PatchInteractionModel<CloudType>::interactionType it = this->wordToInteractionType ( patchData_[patchI].interactionTypeName() ); switch (it) { case PatchInteractionModel<CloudType>::itEscape: { scalar dm = p.mass()*p.nParticle(); keepParticle = false; active = false; U = vector::zero; nEscape_[patchI]++; massEscape_[patchI] += dm; if (writeFields_) { label pI = pp.index(); label fI = pp.whichFace(p.face()); massEscape().boundaryField()[pI][fI] += dm; } break; } case PatchInteractionModel<CloudType>::itStick: { scalar dm = p.mass()*p.nParticle(); keepParticle = true; active = false; U = vector::zero; nStick_[patchI]++; massStick_[patchI] += dm; if (writeFields_) { label pI = pp.index(); label fI = pp.whichFace(p.face()); massStick().boundaryField()[pI][fI] += dm; } break; } case PatchInteractionModel<CloudType>::itRebound: { keepParticle = true; active = true; vector nw; vector Up; this->owner().patchData(p, pp, trackFraction, tetIs, nw, Up); // Calculate motion relative to patch velocity U -= Up; scalar Un = U & nw; vector Ut = U - Un*nw; if (Un > 0) { U -= (1.0 + patchData_[patchI].e())*Un*nw; } U -= patchData_[patchI].mu()*Ut; // Return velocity to global space U += Up; break; } default: { FatalErrorIn ( "bool LocalInteraction<CloudType>::correct" "(" "typename CloudType::parcelType&, " "const polyPatch&, " "bool&, " "const scalar, " "const tetIndices&" ") const" ) << "Unknown interaction type " << patchData_[patchI].interactionTypeName() << "(" << it << ") for patch " << patchData_[patchI].patchName() << ". Valid selections are:" << this->interactionTypeNames_ << endl << abort(FatalError); } } return true; } return false; }