1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright (C) 2011 OpenFOAM Foundation
7 -------------------------------------------------------------------------------
9 This file is part of OpenFOAM.
11 OpenFOAM is free software: you can redistribute it and/or modify it
12 under the terms of the GNU General Public License as published by
13 the Free Software Foundation, either version 3 of the License, or
14 (at your option) any later version.
16 OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21 You should have received a copy of the GNU General Public License
22 along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
24 \*---------------------------------------------------------------------------*/
26 #include "IOPosition.H"
28 #include "cyclicPolyPatch.H"
29 #include "processorPolyPatch.H"
30 #include "symmetryPolyPatch.H"
31 #include "wallPolyPatch.H"
32 #include "wedgePolyPatch.H"
33 #include "meshTools.H"
35 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
37 template<class TrackData>
38 void Foam::particle::prepareForParallelTransfer
44 // Convert the face index to be local to the processor patch
45 faceI_ = patchFace(patchI, faceI_);
49 template<class TrackData>
50 void Foam::particle::correctAfterParallelTransfer
56 const coupledPolyPatch& ppp =
57 refCast<const coupledPolyPatch>(mesh_.boundaryMesh()[patchI]);
59 cellI_ = ppp.faceCells()[faceI_];
61 // Have patch transform the position
62 ppp.transformPosition(position_, faceI_);
64 // Transform the properties
69 ppp.forwardT().size() == 1
71 : ppp.forwardT()[faceI_]
73 transformProperties(T);
75 else if (ppp.separated())
79 (ppp.separation().size() == 1)
81 : ppp.separation()[faceI_]
83 transformProperties(-s);
86 tetFaceI_ = faceI_ + ppp.start();
88 // Faces either side of a coupled patch have matched base indices,
89 // tetPtI is specified relative to the base point, already and
90 // opposite circulation directions by design, so if the vertices
100 // where a is the base point of the face are matching , and we
101 // have fPtI = 1 on the source processor face, i.e. vertex b, then
102 // this because of the face circulation direction change, vertex c
103 // is the characterising point on the destination processor face,
104 // giving the destination fPtI as:
105 // fPtI_d = f.size() - 1 - fPtI_s = 6 - 1 - 1 = 4
106 // This relationship can be verified for other points and sizes of
109 tetPtI_ = mesh_.faces()[tetFaceI_].size() - 1 - tetPtI_;
111 // Reset the face index for the next tracking operation
112 if (stepFraction_ > (1.0 - SMALL))
119 faceI_ += ppp.start();
124 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
126 template<class CloudType>
127 void Foam::particle::readFields(CloudType& c)
134 IOobject procIO(c.fieldIOobject("origProcId", IOobject::MUST_READ));
136 if (procIO.headerOk())
138 IOField<label> origProcId(procIO);
139 c.checkFieldIOobject(c, origProcId);
140 IOField<label> origId(c.fieldIOobject("origId", IOobject::MUST_READ));
141 c.checkFieldIOobject(c, origId);
144 forAllIter(typename CloudType, c, iter)
146 particle& p = iter();
148 p.origProc_ = origProcId[i];
149 p.origId_ = origId[i];
156 template<class CloudType>
157 void Foam::particle::writeFields(const CloudType& c)
159 // Write the cloud position file
160 IOPosition<CloudType> ioP(c);
165 IOField<label> origProc
167 c.fieldIOobject("origProcId", IOobject::NO_READ),
170 IOField<label> origId(c.fieldIOobject("origId", IOobject::NO_READ), np);
173 forAllConstIter(typename CloudType, c, iter)
175 origProc[i] = iter().origProc_;
176 origId[i] = iter().origId_;
185 template<class TrackData>
186 Foam::label Foam::particle::track(const vector& endPosition, TrackData& td)
190 // Tracks to endPosition or stop on boundary
191 while (!onBoundary() && stepFraction_ < 1.0 - SMALL)
193 stepFraction_ += trackToFace(endPosition, td)*(1.0 - stepFraction_);
200 template<class TrackData>
201 Foam::scalar Foam::particle::trackToFace
203 const vector& endPosition,
207 typedef typename TrackData::cloudType cloudType;
208 typedef typename cloudType::particleType particleType;
210 cloudType& cloud = td.cloud();
213 const faceList& pFaces = mesh_.faces();
214 const pointField& pPts = mesh_.points();
215 const vectorField& pC = mesh_.cellCentres();
219 // Pout<< "Particle " << origId_ << " " << origProc_
220 // << " Tracking from " << position_
221 // << " to " << endPosition
224 // Pout<< "stepFraction " << stepFraction_ << nl
225 // << "cellI " << cellI_ << nl
226 // << "tetFaceI " << tetFaceI_ << nl
227 // << "tetPtI " << tetPtI_
230 scalar trackFraction = 0.0;
232 // Minimum tetrahedron decomposition of each cell of the mesh into
233 // using the cell centre, base point on face, and further two
234 // points on the face. For each face of n points, there are n - 2
237 // The points for each tet are organised to match those used in the
238 // tetrahedron class, supplying them in the order:
239 // Cc, basePt, pA, pB
241 // + Cc is the cell centre;
242 // + basePt is the base point on the face;
243 // + pA and pB are the remaining points on the face, such that
244 // the circulation, {basePt, pA, pB} produces a positive
245 // normal by the right-hand rule. pA and pB are chosen from
246 // tetPtI_ do accomplish this depending if the cell owns the
247 // face, tetPtI_ is the vertex that characterises the tet, and
248 // is the first vertex on the tet when circulating around the
249 // face. Therefore, the same tetPtI represents the same face
250 // triangle for both the owner and neighbour cell.
252 // Each tet has its four triangles represented in the same order:
253 // 0) tri joining a tet to the tet across the face in next cell.
254 // This is the triangle opposite Cc.
255 // 1) tri joining a tet to the tet that is in the same cell, but
256 // belongs to the face that shares the edge of the current face
257 // that doesn't contain basePt. This is the triangle opposite
260 // 2) tri joining a tet to the tet that is in the same cell, but
261 // belongs to the face that shares the tet-edge (basePt - pB).
262 // This may be on the same face, or a different one. This is
263 // the triangle opposite basePt. This is the triangle opposite
266 // 4) tri joining a tet to the tet that is in the same cell, but
267 // belongs to the face that shares the tet-edge (basePt - pA).
268 // This may be on the same face, or a different one. This is
269 // the triangle opposite basePt. This is the triangle opposite
272 // Which tri (0..3) of the tet has been crossed
275 // Determine which face was actually crossed. lambdaMin < SMALL
276 // is considered a trigger for a tracking correction towards the
277 // current tet centre.
278 scalar lambdaMin = VGREAT;
280 DynamicList<label>& tris = cloud.labels();
282 // Tet indices that will be set by hitWallFaces if a wall face is
283 // to be hit, or are set when any wall tri of a tet is hit.
284 // Carries the description of the tet on which the cell face has
285 // been hit. For the case of being set in hitWallFaces, this may
286 // be a different tet to the one that the particle occupies.
287 tetIndices faceHitTetIs;
289 // What tolerance is appropriate the minimum lambda numerator and
290 // denominator for tracking in this cell.
291 scalar lambdaDistanceTolerance =
292 lambdaDistanceToleranceCoeff*mesh_.cellVolumes()[cellI_];
298 // Change tet ownership because a tri face has been crossed
302 const Foam::face& f = pFaces[tetFaceI_];
304 bool own = (mesh_.faceOwner()[tetFaceI_] == cellI_);
306 label tetBasePtI = mesh_.tetBasePtIs()[tetFaceI_];
308 label basePtI = f[tetBasePtI];
310 label facePtI = (tetPtI_ + tetBasePtI) % f.size();
311 label otherFacePtI = f.fcIndex(facePtI);
319 fPtBI = otherFacePtI;
323 fPtAI = otherFacePtI;
335 if (lambdaMin < SMALL)
337 // Apply tracking correction towards tet centre
341 Pout<< "tracking rescue using tetCentre from " << position();
344 position_ += trackingCorrectionTol*(tet.centre() - position_);
348 Pout<< " to " << position() << " due to "
349 << (tet.centre() - position_) << endl;
352 cloud.trackingRescue();
354 return trackFraction;
357 if (triI != -1 && mesh_.moving())
359 // Mesh motion requires stepFraction to be correct for
360 // each tracking portion, so trackToFace must return after
361 // every lambda calculation.
362 return trackFraction;
365 FixedList<vector, 4> tetAreas;
367 tetAreas[0] = tet.Sa();
368 tetAreas[1] = tet.Sb();
369 tetAreas[2] = tet.Sc();
370 tetAreas[3] = tet.Sd();
372 FixedList<label, 4> tetPlaneBasePtIs;
374 tetPlaneBasePtIs[0] = basePtI;
375 tetPlaneBasePtIs[1] = f[fPtAI];
376 tetPlaneBasePtIs[2] = basePtI;
377 tetPlaneBasePtIs[3] = basePtI;
386 lambdaDistanceTolerance
389 // Reset variables for new track
393 // Pout<< "tris " << tris << endl;
395 // Sets a value for lambdaMin and faceI_ if a wall face is hit
406 // Did not hit any tet tri faces, and no wall face has been
408 if (tris.empty() && faceI_ < 0)
410 position_ = endPosition;
416 // Loop over all found tris and see if any of them find a
417 // lambda value smaller than that found for a wall face.
422 scalar lam = tetLambda
428 tetPlaneBasePtIs[tI],
432 lambdaDistanceTolerance
446 // This must be a cell face crossing
449 // Set the faceHitTetIs to those for the current tet in case a
450 // wall interaction is required with the cell face
451 faceHitTetIs = tetIndices
463 // A tri was found to be crossed before a wall face was hit (if any)
467 // Pout<< "track loop " << position_ << " " << endPosition << nl
470 // << " " << tetFaceI_
473 // << " " << lambdaMin
474 // << " " << trackFraction
477 // Pout<< "# Tracking loop tet "
478 // << origId_ << " " << origProc_<< nl
479 // << "# face: " << tetFaceI_ << nl
480 // << "# tetPtI: " << tetPtI_ << nl
481 // << "# tetBasePtI: " << mesh_.tetBasePtIs()[tetFaceI_] << nl
482 // << "# tet.mag(): " << tet.mag() << nl
483 // << "# tet.quality(): " << tet.quality()
486 // meshTools::writeOBJ(Pout, tet.a());
487 // meshTools::writeOBJ(Pout, tet.b());
488 // meshTools::writeOBJ(Pout, tet.c());
489 // meshTools::writeOBJ(Pout, tet.d());
491 // Pout<< "f 1 3 2" << nl
492 // << "f 2 3 4" << nl
493 // << "f 1 4 3" << nl
494 // << "f 1 2 4" << endl;
496 // The particle can be 'outside' the tet. This will yield a
497 // lambda larger than 1, or smaller than 0. For values < 0,
498 // the particle travels away from the tet and we don't move
499 // the particle, only change tet/cell. For values larger than
500 // 1, we move the particle to endPosition before the tet/cell
502 if (lambdaMin > SMALL)
504 if (lambdaMin <= 1.0)
506 trackFraction += lambdaMin*(1 - trackFraction);
508 position_ += lambdaMin*(endPosition - position_);
514 position_ = endPosition;
519 // Set lambdaMin to zero to force a towards-tet-centre
524 } while (faceI_ < 0);
526 particleType& p = static_cast<particleType&>(*this);
529 if (internalFace(faceI_))
531 // Change tet ownership because a tri face has been crossed,
532 // in general this is:
533 // tetNeighbour(triI);
534 // but triI must be 0;
535 // No modifications are required for triI = 0, no call required to
538 if (cellI_ == mesh_.faceOwner()[faceI_])
540 cellI_ = mesh_.faceNeighbour()[faceI_];
542 else if (cellI_ == mesh_.faceNeighbour()[faceI_])
544 cellI_ = mesh_.faceOwner()[faceI_];
548 FatalErrorIn("Particle::trackToFace(const vector&, TrackData&)")
549 << "addressing failure" << abort(FatalError);
554 label origFaceI = faceI_;
555 label patchI = patch(faceI_);
557 // No action taken for tetPtI_ for tetFaceI_ here, handled by
558 // patch interaction call or later during processor transfer.
564 mesh_.boundaryMesh()[patchI],
572 // Did patch interaction model switch patches?
573 if (faceI_ != origFaceI)
575 patchI = patch(faceI_);
578 const polyPatch& patch = mesh_.boundaryMesh()[patchI];
580 if (isA<wedgePolyPatch>(patch))
584 static_cast<const wedgePolyPatch&>(patch), td
587 else if (isA<symmetryPolyPatch>(patch))
591 static_cast<const symmetryPolyPatch&>(patch), td
594 else if (isA<cyclicPolyPatch>(patch))
598 static_cast<const cyclicPolyPatch&>(patch), td
601 else if (isA<processorPolyPatch>(patch))
605 static_cast<const processorPolyPatch&>(patch), td
608 else if (isA<wallPolyPatch>(patch))
612 static_cast<const wallPolyPatch&>(patch), td, faceHitTetIs
617 p.hitPatch(patch, td);
622 if (lambdaMin < SMALL)
624 // Apply tracking correction towards tet centre.
625 // Generate current tet to find centre to apply correction.
627 tetPointRef tet = currentTet();
631 Pout<< "tracking rescue for lambdaMin:" << lambdaMin
632 << "from " << position();
635 position_ += trackingCorrectionTol*(tet.centre() - position_);
639 cloud.hasWallImpactDistance()
640 && !internalFace(faceHitTetIs.face())
641 && cloud.cellHasWallFaces()[faceHitTetIs.cell()]
644 const polyBoundaryMesh& patches = mesh_.boundaryMesh();
646 label fI = faceHitTetIs.face();
648 label patchI = patches.patchID()[fI - mesh_.nInternalFaces()];
650 if (isA<wallPolyPatch>(patches[patchI]))
652 // In the case of collision with a wall where there is
653 // a non-zero wallImpactDistance, it is possible for
654 // there to be a tracking correction required to bring
655 // the particle into the domain, but the position of
656 // the particle is further from the wall than the tet
657 // centre, in which case the normal correction can be
658 // counter-productive, i.e. pushes the particle
659 // further out of the domain. In this case it is the
660 // position that hit the wall that is in need of a
661 // rescue correction.
663 triPointRef wallTri = faceHitTetIs.faceTri(mesh_);
665 tetPointRef wallTet = faceHitTetIs.tet(mesh_);
667 vector nHat = wallTri.normal();
670 const scalar r = p.wallImpactDistance(nHat);
672 // Removing (approximately) the wallTri normal
673 // component of the existing correction, to avoid the
674 // situation where the existing correction in the wall
675 // normal direction is larger towards the wall than
676 // the new correction is away from it.
678 trackingCorrectionTol
680 (wallTet.centre() - (position_ + r*nHat))
681 - (nHat & (tet.centre() - position_))*nHat
688 Pout<< " to " << position() << endl;
691 cloud.trackingRescue();
694 return trackFraction;
698 template<class CloudType>
699 void Foam::particle::hitWallFaces
701 const CloudType& cloud,
705 tetIndices& closestTetIs
708 typedef typename CloudType::particleType particleType;
710 if (!(cloud.hasWallImpactDistance() && cloud.cellHasWallFaces()[cellI_]))
715 particleType& p = static_cast<particleType&>(*this);
717 const faceList& pFaces = mesh_.faces();
719 const Foam::cell& thisCell = mesh_.cells()[cellI_];
721 scalar lambdaDistanceTolerance =
722 lambdaDistanceToleranceCoeff*mesh_.cellVolumes()[cellI_];
724 const polyBoundaryMesh& patches = mesh_.boundaryMesh();
726 forAll(thisCell, cFI)
728 label fI = thisCell[cFI];
730 if (internalFace(fI))
735 label patchI = patches.patchID()[fI - mesh_.nInternalFaces()];
737 if (isA<wallPolyPatch>(patches[patchI]))
739 // Get the decomposition of this wall face
741 const List<tetIndices> faceTetIs =
742 polyMeshTetDecomposition::faceTetIndices(mesh_, fI, cellI_);
744 const Foam::face& f = pFaces[fI];
746 forAll(faceTetIs, tI)
748 const tetIndices& tetIs = faceTetIs[tI];
750 triPointRef tri = tetIs.faceTri(mesh_);
752 vector n = tri.normal();
754 vector nHat = n/mag(n);
756 // Radius of particle with respect to this wall face
757 // triangle. Assuming that the wallImpactDistance
758 // does not change as the particle or the mesh moves
760 scalar r = p.wallImpactDistance(nHat);
762 vector toPlusRNHat = to + r*nHat;
764 // triI = 0 because it is the cell face tri of the tet
765 // we are concerned with.
766 scalar tetClambda = tetLambda
768 tetIs.tet(mesh_).centre(),
772 f[tetIs.faceBasePt()],
776 lambdaDistanceTolerance
779 if ((tetClambda <= 0.0) || (tetClambda >= 1.0))
781 // toPlusRNHat is not on the outside of the plane of
782 // the wall face tri, the tri cannot be hit.
786 // Check if the actual trajectory of the near-tri
787 // points intersects the triangle.
789 vector fromPlusRNHat = from + r*nHat;
791 // triI = 0 because it is the cell face tri of the tet
792 // we are concerned with.
793 scalar lambda = tetLambda
799 f[tetIs.faceBasePt()],
803 lambdaDistanceTolerance
806 pointHit hitInfo(vector::zero);
810 // For a moving mesh, the position of wall
811 // triangle needs to be moved in time to be
812 // consistent with the moment defined by the
813 // current value of stepFraction and the value of
814 // lambda just calculated.
816 // Total fraction thought the timestep of the
817 // motion, including stepFraction before the
818 // current tracking step and the current
821 // let s = stepFraction, l = lambda
822 // Motion of x in time:
823 // |-----------------|---------|---------|
826 // where xi is the correct value of x at the required
829 // x0 = x00 + s*(x - x00) = s*x + (1 - s)*x00
831 // i.e. the motion covered by previous tracking portions
832 // within this timestep, and
834 // xi = x0 + l*(x - x0)
835 // = l*x + (1 - l)*x0
836 // = l*x + (1 - l)*(s*x + (1 - s)*x00)
837 // = (s + l - s*l)*x + (1 - (s + l - s*l))*x00
839 // let m = (s + l - s*l)
841 // xi = m*x + (1 - m)*x00 = x00 + m*(x - x00);
843 // In the same form as before.
845 // Clip lambda to 0.0-1.0 to ensure that sensible
846 // positions are used for triangle intersections.
847 scalar lam = max(0.0, min(1.0, lambda));
849 scalar m = stepFraction_ + lam - (stepFraction_*lam);
851 triPointRef tri00 = tetIs.oldFaceTri(mesh_);
853 // Use SMALL positive tolerance to make the triangle
854 // slightly "fat" to improve robustness. Intersection
855 // is calculated as the ray (from + r*nHat) -> (to +
858 point tPtA = tri00.a() + m*(tri.a() - tri00.a());
859 point tPtB = tri00.b() + m*(tri.b() - tri00.b());
860 point tPtC = tri00.c() + m*(tri.c() - tri00.c());
862 triPointRef t(tPtA, tPtB, tPtC);
864 // The point fromPlusRNHat + m*(to - from) is on the
865 // plane of the triangle. Determine the
866 // intersection with this triangle by testing if
867 // this point is inside or outside of the triangle.
868 hitInfo = t.intersection
870 fromPlusRNHat + m*(to - from),
872 intersection::FULL_RAY,
878 // Use SMALL positive tolerance to make the triangle
879 // slightly "fat" to improve robustness. Intersection
880 // is calculated as the ray (from + r*nHat) -> (to +
882 hitInfo = tri.intersection
886 intersection::FULL_RAY,
893 if (lambda < lambdaMin)
899 closestTetIs = tetIs;
908 template<class TrackData>
909 void Foam::particle::hitFace(TrackData&)
913 template<class TrackData>
914 bool Foam::particle::hitPatch
927 template<class TrackData>
928 void Foam::particle::hitWedgePatch
930 const wedgePolyPatch& wpp,
936 "void Foam::particle::hitWedgePatch"
938 "const wedgePolyPatch& wpp, "
941 ) << "Hitting a wedge patch should not be possible."
942 << abort(FatalError);
944 vector nf = normal();
947 transformProperties(I - 2.0*nf*nf);
951 template<class TrackData>
952 void Foam::particle::hitSymmetryPatch
954 const symmetryPolyPatch& spp,
958 vector nf = normal();
961 transformProperties(I - 2.0*nf*nf);
965 template<class TrackData>
966 void Foam::particle::hitCyclicPatch
968 const cyclicPolyPatch& cpp,
972 faceI_ = cpp.transformGlobalFace(faceI_);
974 cellI_ = mesh_.faceOwner()[faceI_];
978 // See note in correctAfterParallelTransfer for tetPtI_ addressing.
979 tetPtI_ = mesh_.faces()[tetFaceI_].size() - 1 - tetPtI_;
981 const cyclicPolyPatch& receiveCpp = cpp.neighbPatch();
982 label patchFacei = receiveCpp.whichFace(faceI_);
984 // Now the particle is on the receiving side
986 // Have patch transform the position
987 receiveCpp.transformPosition(position_, patchFacei);
989 // Transform the properties
990 if (!receiveCpp.parallel())
994 receiveCpp.forwardT().size() == 1
995 ? receiveCpp.forwardT()[0]
996 : receiveCpp.forwardT()[patchFacei]
998 transformProperties(T);
1000 else if (receiveCpp.separated())
1004 (receiveCpp.separation().size() == 1)
1005 ? receiveCpp.separation()[0]
1006 : receiveCpp.separation()[patchFacei]
1008 transformProperties(-s);
1013 template<class TrackData>
1014 void Foam::particle::hitProcessorPatch(const processorPolyPatch&, TrackData&)
1018 template<class TrackData>
1019 void Foam::particle::hitWallPatch
1021 const wallPolyPatch&,
1028 template<class TrackData>
1029 void Foam::particle::hitPatch(const polyPatch&, TrackData&)
1033 // ************************************************************************* //