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 \*---------------------------------------------------------------------------*/
29 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
31 inline void Foam::particle::findTris
33 const vector& position,
34 DynamicList<label>& faceList,
35 const tetPointRef& tet,
36 const FixedList<vector, 4>& tetAreas,
37 const FixedList<label, 4>& tetPlaneBasePtIs,
43 const point Ct = tet.centre();
45 for (label i = 0; i < 4; i++)
47 scalar lambda = tetLambda
60 if ((lambda > 0.0) && (lambda < 1.0))
68 inline Foam::scalar Foam::particle::tetLambda
74 const label tetPlaneBasePtI,
81 const pointField& pPts = mesh_.points();
85 return movingTetLambda
99 const point& base = pPts[tetPlaneBasePtI];
101 scalar lambdaNumerator = (base - from) & n;
102 scalar lambdaDenominator = (to - from) & n;
104 // n carries the area of the tet faces, so the dot product with a
105 // delta-length has the units of volume. Comparing the component of each
106 // delta-length in the direction of n times the face area to a fraction of
109 if (mag(lambdaDenominator) < tol)
111 if (mag(lambdaNumerator) < tol)
113 // Track starts on the face, and is potentially
114 // parallel to it. +-tol/+-tol is not a good
115 // comparison, return 0.0, in anticipation of tet
116 // centre correction.
122 if (mag((to - from)) < tol/mag(n))
124 // 'Zero' length track (compared to the tolerance, which is
125 // based on the cell volume, divided by the tet face area), not
126 // along the face, face cannot be crossed.
132 // Trajectory is non-zero and parallel to face
134 lambdaDenominator = sign(lambdaDenominator)*SMALL;
139 return lambdaNumerator/lambdaDenominator;
143 inline Foam::scalar Foam::particle::movingTetLambda
149 const label tetPlaneBasePtI,
151 const label tetFaceI,
156 const pointField& pPts = mesh_.points();
157 const pointField& oldPPts = mesh_.oldPoints();
159 // Base point of plane at end of motion
160 const point& b = pPts[tetPlaneBasePtI];
162 // n: Normal of plane at end of motion
164 // Base point of plane at start of timestep
165 const point& b00 = oldPPts[tetPlaneBasePtI];
167 // Base point of plane at start of tracking portion (cast forward by
169 point b0 = b00 + stepFraction_*(b - b00);
171 // Normal of plane at start of tracking portion
172 vector n0 = vector::zero;
175 tetIndices tetIs(cellI, tetFaceI, tetPtI, mesh_);
177 // tet at timestep start
178 tetPointRef tet00 = tetIs.oldTet(mesh_);
180 // tet at timestep end
181 tetPointRef tet = tetIs.tet(mesh_);
183 point tet0PtA = tet00.a() + stepFraction_*(tet.a() - tet00.a());
184 point tet0PtB = tet00.b() + stepFraction_*(tet.b() - tet00.b());
185 point tet0PtC = tet00.c() + stepFraction_*(tet.c() - tet00.c());
186 point tet0PtD = tet00.d() + stepFraction_*(tet.d() - tet00.d());
188 // Tracking portion start tet (cast forward by stepFraction)
189 tetPointRef tet0(tet0PtA, tet0PtB, tet0PtC, tet0PtD);
222 // If the old normal is zero (for example in layer addition)
223 // then use the current normal;
228 scalar lambdaNumerator = 0;
229 scalar lambdaDenominator = 0;
231 vector dP = to - from;
234 vector dS = from - b0;
238 scalar a = (dP - dB) & dN;
239 scalar b = ((dP - dB) & n0) + (dS & dN);
245 // Solve quadratic for lambda
246 scalar discriminant = sqr(b) - 4.0*a*c;
248 if (discriminant < 0)
250 // Imaginary roots only - face not crossed
255 // Numerical Recipes in C,
256 // Second Edition (1992),
258 // q = -0.5*(b + sgn(b)*sqrt(b^2 - 4ac))
262 scalar q = -0.5*(b + sign(b)*Foam::sqrt(discriminant));
266 // If q is zero, then l1 = q/a is the required
267 // value of lambda, and is zero.
275 // There will be two roots, a big one and a little
276 // one, choose the little one.
278 if (mag(l1) < mag(l2))
289 // When a is zero, solve the first order polynomial
291 lambdaNumerator = -c;
292 lambdaDenominator = b;
297 // when n = n0 is zero, there is no plane rotation, solve the
298 // first order polynomial
300 lambdaNumerator = -(dS & n0);
301 lambdaDenominator = ((dP - dB) & n0);
305 if (mag(lambdaDenominator) < tol)
307 if (mag(lambdaNumerator) < tol)
309 // Track starts on the face, and is potentially
310 // parallel to it. +-tol)/+-tol is not a good
311 // comparison, return 0.0, in anticipation of tet
312 // centre correction.
318 if (mag((to - from)) < tol/mag(n))
320 // Zero length track, not along the face, face
321 // cannot be crossed.
327 // Trajectory is non-zero and parallel to face
329 lambdaDenominator = sign(lambdaDenominator)*SMALL;
334 return lambdaNumerator/lambdaDenominator;
339 inline void Foam::particle::tetNeighbour(label triI)
341 const labelList& pOwner = mesh_.faceOwner();
342 const faceList& pFaces = mesh_.faces();
344 bool own = (pOwner[tetFaceI_] == cellI_);
346 const Foam::face& f = pFaces[tetFaceI_];
348 label tetBasePtI = mesh_.tetBasePtIs()[tetFaceI_];
350 if (tetBasePtI == -1)
354 "inline void Foam::particle::tetNeighbour(label triI)"
356 << "No base point for face " << tetFaceI_ << ", " << f
357 << ", produces a valid tet decomposition."
358 << abort(FatalError);
361 label facePtI = (tetPtI_ + tetBasePtI) % f.size();
362 label otherFacePtI = f.fcIndex(facePtI);
368 // Crossing this triangle changes tet to that in the
369 // neighbour cell over tetFaceI
371 // Modification of cellI_ will happen by other indexing,
372 // tetFaceI_ and tetPtI don't change.
378 crossEdgeConnectedFace
383 Foam::edge(f[facePtI], f[otherFacePtI])
392 if (tetPtI_ < f.size() - 2)
394 tetPtI_ = f.fcIndex(tetPtI_);
398 crossEdgeConnectedFace
403 Foam::edge(f[tetBasePtI], f[otherFacePtI])
411 tetPtI_ = f.rcIndex(tetPtI_);
415 crossEdgeConnectedFace
420 Foam::edge(f[tetBasePtI], f[facePtI])
433 tetPtI_ = f.rcIndex(tetPtI_);
437 crossEdgeConnectedFace
442 Foam::edge(f[tetBasePtI], f[facePtI])
448 if (tetPtI_ < f.size() - 2)
450 tetPtI_ = f.fcIndex(tetPtI_);
454 crossEdgeConnectedFace
459 Foam::edge(f[tetBasePtI], f[otherFacePtI])
471 "Foam::particle::tetNeighbour(label triI)"
473 << "Tet tri face index error, can only be 0..3, supplied "
474 << triI << abort(FatalError);
482 inline void Foam::particle::crossEdgeConnectedFace
490 const faceList& pFaces = mesh_.faces();
491 const cellList& pCells = mesh_.cells();
493 const Foam::face& f = pFaces[tetFaceI];
495 const Foam::cell& thisCell = pCells[cellI];
497 forAll(thisCell, cFI)
499 // Loop over all other faces of this cell and
500 // find the one that shares this edge
502 label fI = thisCell[cFI];
509 const Foam::face& otherFace = pFaces[fI];
511 label edDir = otherFace.edgeDirection(e);
517 else if (f == pFaces[fI])
519 // This is a necessary condition if using duplicate baffles
520 // (so coincident faces). We need to make sure we don't cross into
521 // the face with the same vertices since we might enter a tracking
522 // loop where it never exits. This test should be cheap
523 // for most meshes so can be left in for 'normal' meshes.
529 //Found edge on other face
536 // Edge is in the forward circulation of this face, so
537 // work with the start point of the edge
539 eIndex = findIndex(otherFace, e.start());
543 // edDir == -1, so the edge is in the reverse
544 // circulation of this face, so work with the end
547 eIndex = findIndex(otherFace, e.end());
550 label tetBasePtI = mesh_.tetBasePtIs()[fI];
552 if (tetBasePtI == -1)
557 "Foam::particle::crossEdgeConnectedFace"
559 "const label& cellI,"
565 << "No base point for face " << fI << ", " << f
566 << ", produces a decomposition that has a minimum "
567 << "volume greater than tolerance."
568 << abort(FatalError);
571 // Find eIndex relative to the base point on new face
572 eIndex -= tetBasePtI;
576 eIndex = (eIndex + otherFace.size()) % otherFace.size();
581 // The point is the base point, so this is first tet
582 // in the face circulation
586 else if (eIndex == otherFace.size() - 1)
588 // The point is the last before the base point, so
589 // this is the last tet in the face circulation
591 tetPtI = otherFace.size() - 2;
604 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
606 inline Foam::label Foam::particle::getNewParticleID() const
608 label id = particleCount_++;
612 WarningIn("particle::getNewParticleID() const")
613 << "Particle counter has overflowed. This might cause problems"
614 << " when reconstructing particle tracks." << endl;
620 inline const Foam::polyMesh& Foam::particle::mesh() const
626 inline const Foam::vector& Foam::particle::position() const
632 inline Foam::vector& Foam::particle::position()
638 inline Foam::label Foam::particle::cell() const
644 inline Foam::label& Foam::particle::cell()
650 inline Foam::label Foam::particle::tetFace() const
656 inline Foam::label& Foam::particle::tetFace()
662 inline Foam::label Foam::particle::tetPt() const
668 inline Foam::label& Foam::particle::tetPt()
674 inline Foam::tetIndices Foam::particle::currentTetIndices() const
676 return tetIndices(cellI_, tetFaceI_, tetPtI_, mesh_);
680 inline Foam::tetPointRef Foam::particle::currentTet() const
682 return currentTetIndices().tet(mesh_);
686 inline Foam::vector Foam::particle::normal() const
688 return currentTetIndices().faceTri(mesh_).normal();
692 inline Foam::vector Foam::particle::oldNormal() const
694 return currentTetIndices().oldFaceTri(mesh_).normal();
698 inline Foam::label Foam::particle::face() const
704 inline Foam::label& Foam::particle::face()
710 inline void Foam::particle::initCellFacePt()
724 FatalErrorIn("void Foam::particle::initCellFacePt()")
725 << "cell, tetFace and tetPt search failure at position "
726 << position_ << abort(FatalError);
731 mesh_.findTetFacePt(cellI_, position_, tetFaceI_, tetPtI_);
733 if (tetFaceI_ == -1 || tetPtI_ == -1)
735 label oldCellI = cellI_;
745 if (cellI_ == -1 || tetFaceI_ == -1 || tetPtI_ == -1)
747 // The particle has entered this function with a cell
748 // number, but hasn't been able to find a cell to
751 if(!mesh_.pointInCellBB(position_, oldCellI, 0.1))
753 // If the position is not inside the (slightly
754 // extended) bound-box of the cell that it thought
755 // it should be in, then this is considered an
758 FatalErrorIn("void Foam::particle::initCellFacePt()")
759 << "cell, tetFace and tetPt search failure at position "
760 << position_ << nl << "for requested cell " << oldCellI
761 << abort(FatalError);
764 // The position is in the (slightly extended)
765 // bound-box of the cell. This situation may arise
766 // because the face decomposition of the cell is not
767 // the same as when the particle acquired the cell
768 // index. For example, it has been read into a mesh
769 // that has made a different face base-point decision
770 // for a boundary face and now this particle is in a
771 // position that is not in the mesh. Gradually move
772 // the particle towards the centre of the cell that it
773 // thought that it was in.
777 point newPosition = position_;
779 const point& cC = mesh_.cellCentres()[cellI_];
781 label trap(1.0/trackingCorrectionTol + 1);
787 newPosition += trackingCorrectionTol*(cC - position_);
799 } while (tetFaceI_ < 0 && iterNo <= trap);
803 FatalErrorIn("void Foam::particle::initCellFacePt()")
804 << "cell, tetFace and tetPt search failure at position "
805 << position_ << abort(FatalError);
810 WarningIn("void Foam::particle::initCellFacePt()")
811 << "Particle moved from " << position_
812 << " to " << newPosition
813 << " in cell " << cellI_
814 << " tetFace " << tetFaceI_
815 << " tetPt " << tetPtI_ << nl
816 << " (A fraction of "
817 << 1.0 - mag(cC - newPosition)/mag(cC - position_)
818 << " of the distance to the cell centre)"
819 << " because a decomposition tetFace and tetPt "
820 << "could not be found."
824 position_ = newPosition;
827 if (debug && cellI_ != oldCellI)
829 WarningIn("void Foam::particle::initCellFacePt()")
830 << "Particle at position " << position_
831 << " searched for a cell, tetFace and tetPt." << nl
833 << " cell " << cellI_
834 << " tetFace " << tetFaceI_
835 << " tetPt " << tetPtI_ << nl
836 << " This is a different cell to that which was supplied"
837 << " (" << oldCellI << ")." << nl
845 inline bool Foam::particle::onBoundary() const
847 return faceI_ != -1 && faceI_ >= mesh_.nInternalFaces();
851 inline Foam::scalar& Foam::particle::stepFraction()
853 return stepFraction_;
857 inline Foam::scalar Foam::particle::stepFraction() const
859 return stepFraction_;
863 inline Foam::label Foam::particle::origProc() const
869 inline Foam::label& Foam::particle::origProc()
875 inline Foam::label Foam::particle::origId() const
881 inline Foam::label& Foam::particle::origId()
887 inline bool Foam::particle::softImpact() const
893 inline Foam::scalar Foam::particle::currentTime() const
897 + stepFraction_*mesh_.time().deltaTValue();
901 inline bool Foam::particle::internalFace(const label faceI) const
903 return mesh_.isInternalFace(faceI);
907 bool Foam::particle::boundaryFace(const label faceI) const
909 return !internalFace(faceI);
913 inline Foam::label Foam::particle::patch(const label faceI) const
915 return mesh_.boundaryMesh().whichPatch(faceI);
919 inline Foam::label Foam::particle::patchFace
925 return mesh_.boundaryMesh()[patchI].whichFace(faceI);
929 inline Foam::label Foam::particle::faceInterpolation() const
935 // ************************************************************************* //