1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright (C) 2010-2011 OpenCFD Ltd.
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 "polyMeshTetDecomposition.H"
28 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
30 // Note: the use of this tolerance is ad-hoc, there may be extreme
31 // cases where the resulting tetrahedra still have particle tracking
32 // problems, or tets with lower quality may track OK.
33 const Foam::scalar Foam::polyMeshTetDecomposition::minTetQuality = sqr(SMALL);
36 // * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
38 Foam::label Foam::polyMeshTetDecomposition::findSharedBasePoint
47 const faceList& pFaces = mesh.faces();
48 const pointField& pPts = mesh.points();
49 const vectorField& pC = mesh.cellCentres();
50 const labelList& pOwner = mesh.faceOwner();
52 const face& f = pFaces[fI];
54 label oCI = pOwner[fI];
56 const point& oCc = pC[oCI];
58 List<scalar> tetQualities(2, 0.0);
60 forAll(f, faceBasePtI)
62 scalar thisBaseMinTetQuality = VGREAT;
64 const point& tetBasePt = pPts[f[faceBasePtI]];
66 for (label tetPtI = 1; tetPtI < f.size() - 1; tetPtI++)
68 label facePtI = (tetPtI + faceBasePtI) % f.size();
69 label otherFacePtI = f.fcIndex(facePtI);
73 label ptAI = f[facePtI];
74 label ptBI = f[otherFacePtI];
76 const point& pA = pPts[ptAI];
77 const point& pB = pPts[ptBI];
79 tetPointRef tet(oCc, tetBasePt, pA, pB);
81 tetQualities[0] = tet.quality();
86 label ptAI = f[otherFacePtI];
87 label ptBI = f[facePtI];
89 const point& pA = pPts[ptAI];
90 const point& pB = pPts[ptBI];
92 tetPointRef tet(nCc, tetBasePt, pA, pB);
94 tetQualities[1] = tet.quality();
97 if (min(tetQualities) < thisBaseMinTetQuality)
99 thisBaseMinTetQuality = min(tetQualities);
103 if (thisBaseMinTetQuality > tol)
109 // If a base point hasn't triggered a return by now, then there is
110 // none that can produce a good decomposition
115 Foam::label Foam::polyMeshTetDecomposition::findSharedBasePoint
117 const polyMesh& mesh,
123 return findSharedBasePoint
127 mesh.cellCentres()[mesh.faceNeighbour()[fI]],
134 Foam::label Foam::polyMeshTetDecomposition::findBasePoint
136 const polyMesh& mesh,
142 const faceList& pFaces = mesh.faces();
143 const pointField& pPts = mesh.points();
144 const vectorField& pC = mesh.cellCentres();
145 const labelList& pOwner = mesh.faceOwner();
147 const face& f = pFaces[fI];
149 label cI = pOwner[fI];
151 bool own = (pOwner[fI] == cI);
153 const point& cC = pC[cI];
155 forAll(f, faceBasePtI)
157 scalar thisBaseMinTetQuality = VGREAT;
159 const point& tetBasePt = pPts[f[faceBasePtI]];
161 for (label tetPtI = 1; tetPtI < f.size() - 1; tetPtI++)
163 label facePtI = (tetPtI + faceBasePtI) % f.size();
164 label otherFacePtI = f.fcIndex(facePtI);
172 ptBI = f[otherFacePtI];
176 ptAI = f[otherFacePtI];
180 const point& pA = pPts[ptAI];
181 const point& pB = pPts[ptBI];
183 tetPointRef tet(cC, tetBasePt, pA, pB);
185 scalar tetQuality = tet.quality();
187 if (tetQuality < thisBaseMinTetQuality)
189 thisBaseMinTetQuality = tetQuality;
193 if (thisBaseMinTetQuality > tol)
199 // If a base point hasn't triggered a return by now, then there is
200 // non that can produce a good decomposition
205 Foam::labelList Foam::polyMeshTetDecomposition::findFaceBasePts
207 const polyMesh& mesh,
212 const labelList& pOwner = mesh.faceOwner();
214 // Find a suitable base point for each face, considering both
215 // cells for interface faces or those on coupled patches
217 labelList tetBasePtIs(mesh.nFaces(), -1);
219 label nInternalFaces = mesh.nInternalFaces();
221 for (label fI = 0; fI < nInternalFaces; fI++)
223 tetBasePtIs[fI] = findSharedBasePoint(mesh, fI, tol, report);
226 pointField neighbourCellCentres(mesh.nFaces() - nInternalFaces);
228 for(label faceI = nInternalFaces; faceI < mesh.nFaces(); faceI++)
230 neighbourCellCentres[faceI - nInternalFaces] =
231 mesh.cellCentres()[pOwner[faceI]];
234 syncTools::swapBoundaryFacePositions(mesh, neighbourCellCentres);
236 const polyBoundaryMesh& patches = mesh.boundaryMesh();
238 SubList<label> boundaryFaceTetBasePtIs
241 mesh.nFaces() - nInternalFaces,
247 label fI = nInternalFaces, bFI = 0;
253 mesh.boundaryMesh().patchID()[bFI];
255 if (patches[patchI].coupled())
257 const coupledPolyPatch& pp =
258 refCast<const coupledPolyPatch>(patches[patchI]);
262 boundaryFaceTetBasePtIs[bFI] = findSharedBasePoint
266 neighbourCellCentres[bFI],
273 // Assign -2, to distinguish from a failed base point
274 // find, which returns -1.
275 boundaryFaceTetBasePtIs[bFI] = -2;
280 boundaryFaceTetBasePtIs[bFI] = findBasePoint
290 // maxEqOp will replace the -2 values on the neighbour patches
291 // with the result from the owner base point find.
293 syncTools::syncBoundaryFaceList
296 boundaryFaceTetBasePtIs,
302 label fI = nInternalFaces, bFI = 0;
307 label& bFTetBasePtI = boundaryFaceTetBasePtIs[bFI];
309 if (bFTetBasePtI == -2)
314 "Foam::polyMeshTetDecomposition::findFaceBasePts"
316 "const polyMesh& mesh, "
321 << "Coupled face base point exchange failure for face "
323 << abort(FatalError);
326 if (bFTetBasePtI < 1)
328 // If the base point is -1, it should be left as such to
329 // indicate a problem, if it is 0, then no action is required.
334 label patchI = mesh.boundaryMesh().patchID()[bFI];
336 if (patches[patchI].coupled())
338 const coupledPolyPatch& pp =
339 refCast<const coupledPolyPatch>(patches[patchI]);
341 // Calculated base points on coupled faces are those of
342 // the owner patch face. They need to be reindexed to for
343 // the non-owner face, which has the opposite order.
345 // So, for fPtI_o != 0, fPtI_n = f.size() - fPtI_o
349 // owner coupledPolyPatch face
350 // face (a b c d e f)
355 // neighbour coupledPolyPatch face
356 // face (a f e d c b)
365 bFTetBasePtI = mesh.faces()[fI].size() - bFTetBasePtI;
374 bool Foam::polyMeshTetDecomposition::checkFaceTets
376 const polyMesh& mesh,
382 const labelList& own = mesh.faceOwner();
383 const labelList& nei = mesh.faceNeighbour();
384 const polyBoundaryMesh& patches = mesh.boundaryMesh();
386 const vectorField& cc = mesh.cellCentres();
387 const vectorField& fc = mesh.faceCentres();
389 // Calculate coupled cell centre
390 pointField neiCc(mesh.nFaces() - mesh.nInternalFaces());
392 for (label faceI = mesh.nInternalFaces(); faceI < mesh.nFaces(); faceI++)
394 neiCc[faceI - mesh.nInternalFaces()] = cc[own[faceI]];
397 syncTools::swapBoundaryFacePositions(mesh, neiCc);
399 const faceList& fcs = mesh.faces();
401 const pointField& p = mesh.points();
403 label nErrorTets = 0;
407 const face& f = fcs[faceI];
411 scalar tetQual = tetPointRef
414 p[f.nextLabel(fPtI)],
423 setPtr->insert(faceI);
427 break; // no need to check other tets
431 if (mesh.isInternalFace(faceI))
433 // Create the neighbour tet - it will have positive volume
434 const face& f = fcs[faceI];
438 scalar tetQual = tetPointRef
441 p[f.nextLabel(fPtI)],
450 setPtr->insert(faceI);
458 if (findSharedBasePoint(mesh, faceI, tol, report) == -1)
462 setPtr->insert(faceI);
470 label patchI = patches.patchID()[faceI - mesh.nInternalFaces()];
472 if (patches[patchI].coupled())
480 neiCc[faceI - mesh.nInternalFaces()],
488 setPtr->insert(faceI);
496 if (findBasePoint(mesh, faceI, tol, report) == -1)
500 setPtr->insert(faceI);
509 reduce(nErrorTets, sumOp<label>());
515 Info<< " ***Error in face tets: "
516 << nErrorTets << " faces with low quality or negative volume "
517 << "decomposition tets." << endl;
526 Info<< " Face tets OK." << endl;
534 Foam::List<Foam::tetIndices> Foam::polyMeshTetDecomposition::faceTetIndices
536 const polyMesh& mesh,
541 const faceList& pFaces = mesh.faces();
542 const labelList& pOwner = mesh.faceOwner();
544 const face& f = pFaces[fI];
546 label nTets = f.size() - 2;
548 List<tetIndices> faceTets(nTets);
550 bool own = (pOwner[fI] == cI);
552 label tetBasePtI = mesh.tetBasePtIs()[fI];
554 if (tetBasePtI == -1)
558 "Foam::List<Foam::FixedList<Foam::label, 4> >"
559 "Foam::Cloud<ParticleType>::"
560 "faceTetIndices(label fI, label cI) const"
562 << "No base point for face " << fI << ", " << f
563 << ", produces a valid tet decomposition."
569 for (label tetPtI = 1; tetPtI < f.size() - 1; tetPtI++)
571 tetIndices& faceTetIs = faceTets[tetPtI - 1];
573 label facePtI = (tetPtI + tetBasePtI) % f.size();
574 label otherFacePtI = f.fcIndex(facePtI);
576 faceTetIs.cell() = cI;
578 faceTetIs.face() = fI;
580 faceTetIs.faceBasePt() = tetBasePtI;
584 faceTetIs.facePtA() = facePtI;
585 faceTetIs.facePtB() = otherFacePtI;
589 faceTetIs.facePtA() = otherFacePtI;
590 faceTetIs.facePtB() = facePtI;
593 faceTetIs.tetPt() = tetPtI;
600 Foam::List<Foam::tetIndices> Foam::polyMeshTetDecomposition::cellTetIndices
602 const polyMesh& mesh,
606 const faceList& pFaces = mesh.faces();
607 const cellList& pCells = mesh.cells();
609 const cell& thisCell = pCells[cI];
613 forAll(thisCell, cFI)
615 nTets += pFaces[thisCell[cFI]].size() - 2;
618 DynamicList<tetIndices> cellTets(nTets);
620 forAll(thisCell, cFI)
622 label fI = thisCell[cFI];
624 cellTets.append(faceTetIndices(mesh, fI, cI));
631 // ************************************************************************* //