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::PatchInteractionModel<CloudType>::patchData ( typename CloudType::parcelType& p, const polyPatch& pp, const scalar trackFraction, const tetIndices& tetIs, vector& nw, vector& Up ) const { const fvMesh& mesh = this->owner().mesh(); const volVectorField& Ufield = mesh.objectRegistry::lookupObject<volVectorField>(UName_); label patchI = pp.index(); label patchFaceI = pp.whichFace(p.face()); vector n = tetIs.faceTri(mesh).normal(); n /= mag(n); vector U = Ufield.boundaryField()[patchI][patchFaceI]; // Unless the face is rotating, the required normal is n; nw = n; if (!mesh.moving()) { // Only wall patches may have a non-zero wall velocity from // the velocity field when the mesh is not moving. if (isA<wallPolyPatch>(pp)) { Up = U; } else { Up = vector::zero; } } else { vector U00 = Ufield.oldTime().boundaryField()[patchI][patchFaceI]; vector n00 = tetIs.oldFaceTri(mesh).normal(); // Difference in normal over timestep vector dn = vector::zero; if (mag(n00) > SMALL) { // If the old normal is zero (for example in layer // addition) then use the current normal, meaning that the // motion can only be translational, and dn remains zero, // otherwise, calculate dn: n00 /= mag(n00); dn = n - n00; } // Total fraction thought the timestep of the motion, // including stepFraction before the current tracking step // and the current trackFraction // i.e. // let s = stepFraction, t = trackFraction // Motion of x in time: // |-----------------|---------|---------| // x00 x0 xi x // // where xi is the correct value of x at the required // tracking instant. // // x0 = x00 + s*(x - x00) = s*x + (1 - s)*x00 // // i.e. the motion covered by previous tracking portions // within this timestep, and // // xi = x0 + t*(x - x0) // = t*x + (1 - t)*x0 // = t*x + (1 - t)*(s*x + (1 - s)*x00) // = (s + t - s*t)*x + (1 - (s + t - s*t))*x00 // // let m = (s + t - s*t) // // xi = m*x + (1 - m)*x00 = x00 + m*(x - x00); // // In the same form as before. scalar m = p.stepFraction() + trackFraction - (p.stepFraction()*trackFraction); // When the mesh is moving, the velocity field on wall patches // will contain the velocity associated with the motion of the // mesh, in which case it is interpolated in time using m. // For other patches the face velocity will need to be // reconstructed from the face centre motion. const vector& Cf = mesh.faceCentres()[p.face()]; vector Cf00 = mesh.faces()[p.face()].centre(mesh.oldPoints()); if (isA<wallPolyPatch>(pp)) { Up = U00 + m*(U - U00); } else { Up = (Cf - Cf00)/this->owner().time().deltaTValue(); } if (mag(dn) > SMALL) { // Rotational motion, nw requires interpolation and a // rotational velocity around face centre correction to Up // is required. nw = n00 + m*dn; // Cf at tracking instant vector Cfi = Cf00 + m*(Cf - Cf00); // Normal vector cross product vector omega = (n00 ^ n); scalar magOmega = mag(omega); // magOmega = sin(angle between unit normals) // Normalise omega vector by magOmega, then multiply by // angle/dt to give the correct angular velocity vector. omega *= Foam::asin(magOmega) /(magOmega*this->owner().time().deltaTValue()); // Project position onto face and calculate this position // relative to the face centre. vector facePos = p.position() - ((p.position() - Cfi) & nw)*nw - Cfi; Up += (omega ^ facePos); } // No further action is required if the motion is // translational only, nw and Up have already been set. } }
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; } } }
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; }