/*---------------------------------------------------------------------------*\ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2011-2017 OpenFOAM Foundation Copyright (C) 2019-2022 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. OpenFOAM is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. OpenFOAM is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenFOAM. If not, see . \*---------------------------------------------------------------------------*/ #include "PairCollision.H" #include "PairModel.H" #include "WallModel.H" // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // template Foam::scalar Foam::PairCollision::cosPhiMinFlatWall = 1 - SMALL; template Foam::scalar Foam::PairCollision::flatWallDuplicateExclusion = sqrt(3*SMALL); // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // template void Foam::PairCollision::preInteraction() { // Set accumulated quantities to zero for (typename CloudType::parcelType& p : this->owner()) { p.f() = Zero; p.torque() = Zero; } } template void Foam::PairCollision::parcelInteraction() { PstreamBuffers pBufs; label startOfRequests = Pstream::nRequests(); il_.sendReferredData(this->owner().cellOccupancy(), pBufs); realRealInteraction(); il_.receiveReferredData(pBufs, startOfRequests); realReferredInteraction(); } template void Foam::PairCollision::realRealInteraction() { // Direct interaction list (dil) const labelListList& dil = il_.dil(); typename CloudType::parcelType* pA_ptr = nullptr; typename CloudType::parcelType* pB_ptr = nullptr; List>& cellOccupancy = this->owner().cellOccupancy(); forAll(dil, realCelli) { // Loop over all Parcels in cell A (a) forAll(cellOccupancy[realCelli], a) { pA_ptr = cellOccupancy[realCelli][a]; forAll(dil[realCelli], interactingCells) { List cellBParcels = cellOccupancy[dil[realCelli][interactingCells]]; // Loop over all Parcels in cell B (b) forAll(cellBParcels, b) { pB_ptr = cellBParcels[b]; evaluatePair(*pA_ptr, *pB_ptr); } } // Loop over the other Parcels in cell A (aO) forAll(cellOccupancy[realCelli], aO) { pB_ptr = cellOccupancy[realCelli][aO]; // Do not double-evaluate, compare pointers, arbitrary // order if (pB_ptr > pA_ptr) { evaluatePair(*pA_ptr, *pB_ptr); } } } } } template void Foam::PairCollision::realReferredInteraction() { // Referred interaction list (ril) const labelListList& ril = il_.ril(); List>& referredParticles = il_.referredParticles(); List>& cellOccupancy = this->owner().cellOccupancy(); // Loop over all referred cells forAll(ril, refCelli) { IDLList& refCellRefParticles = referredParticles[refCelli]; const labelList& realCells = ril[refCelli]; // Loop over all referred parcels in the referred cell for ( typename CloudType::parcelType& referredParcel : refCellRefParticles ) { // Loop over all real cells in that the referred cell is // to supply interactions to forAll(realCells, realCelli) { List realCellParcels = cellOccupancy[realCells[realCelli]]; forAll(realCellParcels, realParcelI) { evaluatePair ( *realCellParcels[realParcelI], referredParcel ); } } } } } template void Foam::PairCollision::wallInteraction() { const polyMesh& mesh = this->owner().mesh(); const labelListList& dil = il_.dil(); const labelListList& directWallFaces = il_.dwfil(); const labelList& patchID = mesh.boundaryMesh().patchID(); const volVectorField& U = mesh.lookupObject(il_.UName()); List>& cellOccupancy = this->owner().cellOccupancy(); // Storage for the wall interaction sites DynamicList flatSitePoints; DynamicList flatSiteExclusionDistancesSqr; DynamicList> flatSiteData; DynamicList otherSitePoints; DynamicList otherSiteDistances; DynamicList> otherSiteData; DynamicList sharpSitePoints; DynamicList sharpSiteExclusionDistancesSqr; DynamicList> sharpSiteData; forAll(dil, realCelli) { // The real wall faces in range of this real cell const labelList& realWallFaces = directWallFaces[realCelli]; // Loop over all Parcels in cell forAll(cellOccupancy[realCelli], cellParticleI) { flatSitePoints.clear(); flatSiteExclusionDistancesSqr.clear(); flatSiteData.clear(); otherSitePoints.clear(); otherSiteDistances.clear(); otherSiteData.clear(); sharpSitePoints.clear(); sharpSiteExclusionDistancesSqr.clear(); sharpSiteData.clear(); typename CloudType::parcelType& p = *cellOccupancy[realCelli][cellParticleI]; const point pos(p.position()); scalar r = wallModel_->pREff(p); // real wallFace interactions forAll(realWallFaces, realWallFacei) { label realFacei = realWallFaces[realWallFacei]; pointHit nearest = mesh.faces()[realFacei].nearestPoint ( pos, mesh.points() ); if (nearest.distance() < r) { const vector normal = normalised(mesh.faceAreas()[realFacei]); const vector& nearPt = nearest.point(); const vector pW = normalised(nearPt - pos); const scalar normalAlignment = normal & pW; // Find the patchIndex and wallData for WallSiteData object label patchi = patchID[realFacei - mesh.nInternalFaces()]; label patchFacei = realFacei - mesh.boundaryMesh()[patchi].start(); WallSiteData wSD ( patchi, U.boundaryField()[patchi][patchFacei] ); if (normalAlignment > cosPhiMinFlatWall) { // Guard against a flat interaction being // present on the boundary of two or more // faces, which would create duplicate contact // points. Duplicates are discarded. if ( !duplicatePointInList ( flatSitePoints, nearPt, sqr(r*flatWallDuplicateExclusion) ) ) { flatSitePoints.append(nearPt); flatSiteExclusionDistancesSqr.append ( sqr(r) - sqr(nearest.distance()) ); flatSiteData.append(wSD); } } else { otherSitePoints.append(nearPt); otherSiteDistances.append(nearest.distance()); otherSiteData.append(wSD); } } } // referred wallFace interactions // The labels of referred wall faces in range of this real cell const labelList& cellRefWallFaces = il_.rwfilInverse()[realCelli]; forAll(cellRefWallFaces, rWFI) { label refWallFacei = cellRefWallFaces[rWFI]; const referredWallFace& rwf = il_.referredWallFaces()[refWallFacei]; const pointField& pts = rwf.points(); pointHit nearest = rwf.nearestPoint(pos, pts); if (nearest.distance() < r) { const vector normal = rwf.unitNormal(pts); const vector& nearPt = nearest.point(); const vector pW = normalised(nearPt - pos); const scalar normalAlignment = normal & pW; // Find the patchIndex and wallData for WallSiteData object WallSiteData wSD ( rwf.patchIndex(), il_.referredWallData()[refWallFacei] ); if (normalAlignment > cosPhiMinFlatWall) { // Guard against a flat interaction being // present on the boundary of two or more // faces, which would create duplicate contact // points. Duplicates are discarded. if ( !duplicatePointInList ( flatSitePoints, nearPt, sqr(r*flatWallDuplicateExclusion) ) ) { flatSitePoints.append(nearPt); flatSiteExclusionDistancesSqr.append ( sqr(r) - sqr(nearest.distance()) ); flatSiteData.append(wSD); } } else { otherSitePoints.append(nearPt); otherSiteDistances.append(nearest.distance()); otherSiteData.append(wSD); } } } // All flat interaction sites found, now classify the // other sites as being in range of a flat interaction, or // a sharp interaction, being aware of not duplicating the // sharp interaction sites. // The "other" sites need to evaluated in order of // ascending distance to their nearest point so that // grouping occurs around the closest in any group labelList sortedOtherSiteIndices ( sortedOrder(otherSiteDistances) ); for (const label orderedIndex : sortedOtherSiteIndices) { const point& otherPt = otherSitePoints[orderedIndex]; if ( !duplicatePointInList ( flatSitePoints, otherPt, flatSiteExclusionDistancesSqr ) ) { // Not in range of a flat interaction, must be a // sharp interaction. if ( !duplicatePointInList ( sharpSitePoints, otherPt, sharpSiteExclusionDistancesSqr ) ) { sharpSitePoints.append(otherPt); sharpSiteExclusionDistancesSqr.append ( sqr(r) - sqr(otherSiteDistances[orderedIndex]) ); sharpSiteData.append(otherSiteData[orderedIndex]); } } } evaluateWall ( p, flatSitePoints, flatSiteData, sharpSitePoints, sharpSiteData ); } } } template bool Foam::PairCollision::duplicatePointInList ( const UList& existingPoints, const point& pointToTest, scalar duplicateRangeSqr ) const { forAll(existingPoints, i) { if (magSqr(existingPoints[i] - pointToTest) < duplicateRangeSqr) { return true; } } return false; } template bool Foam::PairCollision::duplicatePointInList ( const UList& existingPoints, const point& pointToTest, const scalarList& duplicateRangeSqr ) const { forAll(existingPoints, i) { if (magSqr(existingPoints[i] - pointToTest) < duplicateRangeSqr[i]) { return true; } } return false; } template void Foam::PairCollision::postInteraction() { // Delete any collision records where no collision occurred this step for (typename CloudType::parcelType& p : this->owner()) { p.collisionRecords().update(); } } template void Foam::PairCollision::evaluatePair ( typename CloudType::parcelType& pA, typename CloudType::parcelType& pB ) const { pairModel_->evaluatePair(pA, pB); } template void Foam::PairCollision::evaluateWall ( typename CloudType::parcelType& p, const List& flatSitePoints, const List>& flatSiteData, const List& sharpSitePoints, const List>& sharpSiteData ) const { wallModel_->evaluateWall ( p, flatSitePoints, flatSiteData, sharpSitePoints, sharpSiteData ); } // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // template Foam::PairCollision::PairCollision ( const dictionary& dict, CloudType& owner ) : CollisionModel(dict, owner, typeName), pairModel_ ( PairModel::New ( this->coeffDict(), this->owner() ) ), wallModel_ ( WallModel::New ( this->coeffDict(), this->owner() ) ), il_ ( owner.mesh(), this->coeffDict().getScalar("maxInteractionDistance"), this->coeffDict().getOrDefault ( "writeReferredParticleCloud", false ), this->coeffDict().template getOrDefault("U", "U") ) {} template Foam::PairCollision::PairCollision ( const PairCollision& cm ) : CollisionModel(cm), pairModel_(nullptr), wallModel_(nullptr), il_(cm.owner().mesh()) { // Need to clone to PairModel and WallModel NotImplemented; } // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * // template Foam::PairCollision::~PairCollision() {} // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // template Foam::label Foam::PairCollision::nSubCycles() const { label nSubCycles = 1; if (pairModel_->controlsTimestep()) { label nPairSubCycles = returnReduce ( pairModel_->nSubCycles(), maxOp