1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright held by original author
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 the
13 Free Software Foundation; either version 2 of the License, or (at your
14 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, write to the Free Software Foundation,
23 Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
29 Functions specific to connectivity checking and debugging
33 University of Massachusetts Amherst
36 \*---------------------------------------------------------------------------*/
38 #include "dynamicTopoFvMesh.H"
41 #include "volFields.H"
42 #include "triPointRef.H"
43 #include "tetPointRef.H"
44 #include "coupledInfo.H"
49 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
51 // Compute mesh-quality, and return true if no slivers are present
52 bool dynamicTopoFvMesh::meshQuality
57 // Compute statistics on the fly
58 label nCells = 0, minCell = -1;
59 scalar maxQuality = -GREAT;
60 scalar minQuality = GREAT;
61 scalar cQuality = 0.0;
62 scalar meanQuality = 0.0;
65 bool sliversAbsent = true;
66 thresholdSlivers_.clear();
68 // Loop through all cells in the mesh and compute cell quality
71 const cell& cellToCheck = cells_[cellI];
73 if (cellToCheck.empty())
78 // Skip hexahedral cells
79 if (cellToCheck.size() == 6)
82 meanQuality += cQuality;
85 maxQuality = Foam::max(cQuality, maxQuality);
86 minQuality = Foam::min(cQuality, minQuality);
95 // Assume XY plane here
96 vector n = vector(0,0,1);
98 // Get a triangular boundary face
99 forAll(cellToCheck, faceI)
101 const face& faceToCheck = faces_[cellToCheck[faceI]];
103 if (faceToCheck.size() == 3)
107 points_[faceToCheck[0]],
108 points_[faceToCheck[1]],
109 points_[faceToCheck[2]]
112 // Assume centre-plane passes through origin
120 ((tpr.centre() & n) * n)
131 const label bfIndex = cellToCheck[0];
132 const label cfIndex = cellToCheck[1];
134 const face& baseFace = faces_[bfIndex];
135 const face& checkFace = faces_[cfIndex];
137 // Get the fourth point
140 meshOps::findIsolatedPoint(baseFace, checkFace)
143 // Compute cell quality
144 if (owner_[bfIndex] == cellI)
150 points_[baseFace[2]],
151 points_[baseFace[1]],
152 points_[baseFace[0]],
163 points_[baseFace[0]],
164 points_[baseFace[1]],
165 points_[baseFace[2]],
173 maxQuality = Foam::max(cQuality, maxQuality);
175 if (cQuality < minQuality)
177 minQuality = cQuality;
181 meanQuality += cQuality;
184 // Add to the list of slivers
185 if ((cQuality < sliverThreshold_) && (cQuality > 0.0))
187 thresholdSlivers_.insert(cellI, cQuality);
191 if (thresholdSlivers_.size())
193 sliversAbsent = false;
196 // Reduce across processors.
197 reduce(sliversAbsent, andOp<bool>());
199 // Output statistics:
200 if (outputOption || (debug > 0))
202 // Reduce statistics across processors.
203 reduce(minQuality, minOp<scalar>());
204 reduce(maxQuality, maxOp<scalar>());
205 reduce(meanQuality, sumOp<scalar>());
206 reduce(nCells, sumOp<label>());
208 if (minQuality < 0.0)
212 "bool dynamicTopoFvMesh::meshQuality"
213 "(bool outputOption)"
216 << "Minimum cell quality is: " << minQuality
217 << " at cell: " << minCell
222 << " ~~~ Mesh Quality Statistics ~~~ " << nl
223 << " Min: " << minQuality << nl
224 << " Max: " << maxQuality << nl
225 << " Mean: " << meanQuality/nCells << nl
226 << " Cells: " << nCells << nl
227 << " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ " << nl
231 return sliversAbsent;
235 // Utility to check whether points of an edge lie on a boundary.
236 const FixedList<bool,2>
237 dynamicTopoFvMesh::checkEdgeBoundary
242 FixedList<bool,2> edgeBoundary(false);
244 const edge& edgeToCheck = edges_[eIndex];
246 // Loop through edges connected to both points,
247 // and check if any of them lie on boundaries.
248 // Used to ensure that collapses happen towards boundaries.
249 forAll(edgeToCheck, pointI)
251 const labelList& pEdges = pointEdges_[edgeToCheck[pointI]];
253 forAll(pEdges, edgeI)
255 // Determine the patch this edge belongs to
256 if (whichEdgePatch(pEdges[edgeI]) > -1)
258 edgeBoundary[pointI] = true;
268 // Check whether the given edge is on a bounding curve
269 // - If nProcCurves is provided, the variable is incremented
270 // if the edge is processor-coupled
271 bool dynamicTopoFvMesh::checkBoundingCurve
274 const bool overRidePurityCheck,
278 // Internal edges don't count
279 label edgePatch = -1;
281 // If this entity was deleted, skip it.
282 if (edgeFaces_[eIndex].empty())
284 // Return true so that swap3DEdges skips this edge.
288 // Check if two boundary faces lie on different face-patches
289 bool procCoupled = false;
290 FixedList<label, 2> fPatches(-1);
291 FixedList<vector, 2> fNorm(vector::zero);
293 if ((edgePatch = whichEdgePatch(eIndex)) < 0)
299 // Check whether this edge shouldn't be swapped
300 if (findIndex(noSwapPatchIDs_, edgePatch) > -1)
305 // Explicit check for processor edges (both 2D and 3D)
306 if (processorCoupledEntity(eIndex, false, true))
308 // Increment nProcCurves
314 // Check for pure processor edge, and if not,
315 // fetch boundary patch labels / normals
318 processorCoupledEntity
329 // 'Pure' processor coupled edges don't count
333 if (!overRidePurityCheck)
335 // This edge lies between a processor and physical patch,
336 // - This a bounding curve (unless an override is requested)
337 // - An override is warranted for 2-2 swaps on impure edges,
338 // which is typically requested by swap3DEdges.
342 // Specify that the edge is procCoupled
349 // Normalize patch normals from coupled check
350 fNorm[0] /= mag(fNorm[0]) + VSMALL;
351 fNorm[1] /= mag(fNorm[1]) + VSMALL;
355 // Fetch patch indices / normals
356 const labelList& eFaces = edgeFaces_[eIndex];
358 label fPatch = -1, count = 0;
360 forAll(eFaces, faceI)
362 if ((fPatch = whichPatch(eFaces[faceI])) > -1)
364 // Obtain the normal.
365 fNorm[count] = faces_[eFaces[faceI]].normal(points_);
368 fNorm[count] /= mag(fNorm[count]) + VSMALL;
371 fPatches[count] = fPatch;
383 // Check for legitimate patches
384 if (fPatches[0] < 0 || fPatches[1] < 0)
386 const labelList& eFaces = edgeFaces_[eIndex];
388 forAll(eFaces, faceI)
390 Pout<< " Face: " << eFaces[faceI]
391 << " :: " << faces_[eFaces[faceI]]
392 << " Patch: " << whichPatch(eFaces[faceI]) << nl;
395 label epI = whichEdgePatch(eIndex);
399 "bool dynamicTopoFvMesh::checkBoundingCurve"
400 "(const label, const bool) const"
402 << " Edge: " << eIndex << ":: " << edges_[eIndex]
404 << (epI < 0 ? "Internal" : boundaryMesh()[epI].name())
405 << " edgeFaces: " << eFaces << nl
406 << " expected 2 boundary patches." << nl
407 << " fPatches[0]: " << fPatches[0] << nl
408 << " fPatches[1]: " << fPatches[1] << nl
409 << " fNorm[0]: " << fNorm[0] << nl
410 << " fNorm[1]: " << fNorm[1] << nl
411 << " coupledModification: " << coupledModification_
412 << abort(FatalError);
415 scalar deviation = (fNorm[0] & fNorm[1]);
417 // Check if the swap-curvature is too high
418 if (mag(deviation) < swapDeviation_)
423 // Check if the edge borders two different patches
424 if (fPatches[0] != fPatches[1])
429 // Not on a bounding curve
434 // Check triangulation quality for an edge index
435 bool dynamicTopoFvMesh::checkQuality
439 const PtrList<scalarListList>& Q,
440 const scalar minQuality,
441 const label checkIndex
444 bool myResult = false;
447 if (Q[checkIndex][0][m[checkIndex]-1] > minQuality)
454 << " eIndex: " << eIndex
455 << " minQuality: " << minQuality
456 << " newQuality: " << Q[checkIndex][0][m[checkIndex]-1]
461 if (coupledModification_)
463 // Only locally coupled indices require checks
464 if (locallyCoupledEntity(eIndex))
466 // Check the quality of the slave edge as well.
469 // Loop through masterToSlave and determine the slave index.
470 forAll(patchCoupling_, patchI)
472 if (patchCoupling_(patchI))
474 const label edgeEnum = coupleMap::EDGE;
475 const coupleMap& cMap = patchCoupling_[patchI].map();
477 if ((sIndex = cMap.findSlave(edgeEnum, eIndex)) > -1)
488 "bool dynamicTopoFvMesh::checkQuality\n"
490 " const label eIndex,\n"
491 " const labelList& m,\n"
492 " const PtrList<scalarListList>& Q,\n"
493 " const scalar minQuality,\n"
494 " const label checkIndex\n"
497 << "Coupled maps were improperly specified." << nl
498 << " Slave index not found for: " << nl
499 << " Edge: " << eIndex << nl
500 << abort(FatalError);
503 // Turn off switch temporarily.
504 unsetCoupledModification();
506 // Recursively call for the slave edge.
509 myResult && checkQuality(sIndex, m, Q, minQuality, 1)
513 setCoupledModification();
521 // Print out tables for debugging
522 void dynamicTopoFvMesh::printTables
525 const PtrList<scalarListList>& Q,
526 const PtrList<labelListList>& K,
527 const label checkIndex
530 Pout<< "m: " << m[checkIndex] << endl;
539 for (label j = 0; j < m[checkIndex]; j++)
541 Pout<< setw(12) << j;
546 for (label j = 0; j < m[checkIndex]; j++)
548 Pout<< "-------------";
553 for (label i = 0; i < (m[checkIndex]-2); i++)
557 for (label j = 0; j < m[checkIndex]; j++)
559 Pout<< setw(12) << Q[checkIndex][i][j];
572 for (label j = 0; j < m[checkIndex]; j++)
574 Pout<< setw(12) << j;
579 for (label i = 0; i < (m[checkIndex]-2); i++)
583 for (label j = 0; j < m[checkIndex]; j++)
585 Pout<< setw(12) << K[checkIndex][i][j];
595 // Check old-volumes for an input triangulation
596 bool dynamicTopoFvMesh::checkTriangulationVolumes
599 const labelList& hullVertices,
600 const labelListList& triangulations
603 label m = hullVertices.size();
604 scalar oldTetVol = 0.0;
605 scalar newTetVol = 0.0;
607 const edge& edgeToCheck = edges_[eIndex];
609 for (label i = 0; i < (m-2); i++)
611 // Compute volume for the upper-half
616 points_[hullVertices[triangulations[0][i]]],
617 points_[hullVertices[triangulations[1][i]]],
618 points_[hullVertices[triangulations[2][i]]],
619 points_[edgeToCheck[0]]
627 oldPoints_[hullVertices[triangulations[0][i]]],
628 oldPoints_[hullVertices[triangulations[1][i]]],
629 oldPoints_[hullVertices[triangulations[2][i]]],
630 oldPoints_[edgeToCheck[0]]
634 if (oldTetVol < 0.0 || (mag(oldTetVol) < mag(0.1 * newTetVol)))
638 Pout<< " Swap sequence leads to bad old-volumes." << nl
639 << " Edge: " << edgeToCheck << nl
640 << " using Points: " << nl
641 << oldPoints_[hullVertices[triangulations[0][i]]] << nl
642 << oldPoints_[hullVertices[triangulations[1][i]]] << nl
643 << oldPoints_[hullVertices[triangulations[2][i]]] << nl
644 << oldPoints_[edgeToCheck[0]] << nl
645 << " Old Volume: " << oldTetVol << nl
646 << " New Volume: " << newTetVol << nl
657 points_[hullVertices[triangulations[2][i]]],
658 points_[hullVertices[triangulations[1][i]]],
659 points_[hullVertices[triangulations[0][i]]],
660 points_[edgeToCheck[1]]
668 oldPoints_[hullVertices[triangulations[2][i]]],
669 oldPoints_[hullVertices[triangulations[1][i]]],
670 oldPoints_[hullVertices[triangulations[0][i]]],
671 oldPoints_[edgeToCheck[1]]
675 if (oldTetVol < 0.0 || (mag(oldTetVol) < mag(0.1 * newTetVol)))
679 Pout<< " Swap sequence leads to bad old-volumes." << nl
680 << " Edge: " << edgeToCheck << nl
681 << " using Points: " << nl
682 << oldPoints_[hullVertices[triangulations[2][i]]] << nl
683 << oldPoints_[hullVertices[triangulations[1][i]]] << nl
684 << oldPoints_[hullVertices[triangulations[0][i]]] << nl
685 << oldPoints_[edgeToCheck[1]] << nl
686 << " Old Volume: " << oldTetVol << nl
687 << " New Volume: " << newTetVol << nl
699 // Write out connectivity for an edge
700 void dynamicTopoFvMesh::writeEdgeConnectivity
706 writeVTK("Edge_" + Foam::name(eIndex), eIndex, 1, false, true);
708 const labelList& eFaces = edgeFaces_[eIndex];
710 // Write out edge faces
716 + Foam::name(Pstream::myProcNo()),
721 DynamicList<label> edgeCells(10);
723 forAll(eFaces, faceI)
725 label pIdx = whichPatch(eFaces[faceI]);
727 word pName((pIdx < 0) ? "Internal" : boundaryMesh()[pIdx].name());
729 Pout<< " Face: " << eFaces[faceI]
730 << " :: " << faces_[eFaces[faceI]]
731 << " Patch: " << pName
732 << " Proc: " << Pstream::myProcNo() << nl;
734 label own = owner_[eFaces[faceI]];
735 label nei = neighbour_[eFaces[faceI]];
737 if (findIndex(edgeCells, own) == -1)
739 edgeCells.append(own);
747 if (findIndex(edgeCells, nei) == -1)
749 edgeCells.append(nei);
753 // Write out cells connected to edge
759 + Foam::name(Pstream::myProcNo()),
769 // Check processors for coupling
770 forAll(procIndices_, pI)
772 // Fetch reference to subMesh
773 const coupleMap& cMap = recvMeshes_[pI].map();
774 const dynamicTopoFvMesh& mesh = recvMeshes_[pI].subMesh();
778 if ((sI = cMap.findSlave(coupleMap::EDGE, eIndex)) == -1)
783 const edge& slaveEdge = mesh.edges_[sI];
784 const labelList& seFaces = mesh.edgeFaces_[sI];
788 cMap.findMaster(coupleMap::POINT, slaveEdge[0]),
789 cMap.findMaster(coupleMap::POINT, slaveEdge[1])
792 Pout<< " >> Edge: " << sI << "::" << slaveEdge
793 << " mapped: " << cE << nl;
800 + Foam::name(procIndices_[pI]),
805 // Clear existing list
808 forAll(seFaces, faceI)
810 label pIdx = mesh.whichPatch(seFaces[faceI]);
816 mesh.boundaryMesh()[pIdx].name()
819 Pout<< " Face: " << seFaces[faceI]
820 << " :: " << mesh.faces_[seFaces[faceI]]
821 << " Patch: " << pName
822 << " Proc: " << procIndices_[pI] << nl;
824 label own = mesh.owner_[seFaces[faceI]];
825 label nei = mesh.neighbour_[seFaces[faceI]];
827 if (findIndex(edgeCells, own) == -1)
829 edgeCells.append(own);
837 if (findIndex(edgeCells, nei) == -1)
839 edgeCells.append(nei);
848 + Foam::name(procIndices_[pI]),
856 // Output an entity as a VTK file
857 void dynamicTopoFvMesh::writeVTK
861 const label primitiveType,
862 const bool useOldConnectivity,
863 const bool useOldPoints
869 labelList(1, entity),
877 // Output a list of primitives as a VTK file.
878 // - primitiveType is:
883 void dynamicTopoFvMesh::writeVTK
886 const labelList& cList,
887 const label primitiveType,
888 const bool useOldConnectivity,
889 const bool useOldPoints,
890 const UList<scalar>& scalField,
891 const UList<label>& lablField
894 // Check if spatial bounding box has been specified
895 const dictionary& meshSubDict = dict_.subDict("dynamicTopoFvMesh");
897 labelList entityList;
899 if (meshSubDict.found("spatialDebug") && !useOldConnectivity)
901 // Read the bounding box
904 meshSubDict.subDict("spatialDebug").lookup("debugBoundBox")
907 DynamicList<label> cSubList(10);
911 label index = cList[cellI];
918 point containPoint(vector::zero);
920 switch (primitiveType)
922 // Are we looking at points?
925 containPoint = points_[index];
929 // Are we looking at edges?
932 containPoint = edges_[index].centre(points_);
936 // Are we looking at faces?
939 containPoint = faces_[index].centre(points_);
943 // Are we looking at cells?
949 meshOps::cellCentreAndVolume
964 // Is the point of interest?
965 if (bb.contains(containPoint))
967 cSubList.append(index);
971 // If nothing is present, don't write out anything
972 if (cSubList.empty())
978 // Take over contents
979 entityList = cSubList;
984 // Conventional output
990 if (useOldConnectivity)
992 // Use points from polyMesh
1003 polyMesh::faceOwner(),
1046 // Return the status report interval
1047 scalar dynamicTopoFvMesh::reportInterval() const
1049 // Default to 1 second
1050 scalar interval = 1.0;
1052 const dictionary& meshSubDict = dict_.subDict("dynamicTopoFvMesh");
1054 if (meshSubDict.found("reportInterval") || mandatory_)
1056 interval = readScalar(meshSubDict.lookup("reportInterval"));
1058 // Prevent reports if necessary
1059 if (interval < VSMALL)
1069 // Check the state of connectivity lists
1070 void dynamicTopoFvMesh::checkConnectivity(const label maxErrors) const
1072 label nFailedChecks = 0;
1074 messageStream ConnectivityWarning
1076 "dynamicTopoFvMesh Connectivity Warning",
1077 messageStream::SERIOUS,
1081 // Check face-label ranges
1082 Pout<< "Checking index ranges...";
1084 forAll(edges_, edgeI)
1086 const edge& curEdge = edges_[edgeI];
1088 if (curEdge == edge(-1, -1))
1095 curEdge[0] < 0 || curEdge[0] > (points_.size()-1) ||
1096 curEdge[1] < 0 || curEdge[1] > (points_.size()-1)
1099 Pout<< "Edge " << edgeI
1100 << " contains vertex labels out of range: "
1102 << " Max point index = " << (points_.size()-1) << endl;
1106 ConnectivityWarning()
1107 << nl << "Edge-point connectivity is inconsistent."
1111 // Check for unique point-labels
1112 if (curEdge[0] == curEdge[1])
1114 Pout<< "Edge " << edgeI
1115 << " contains identical vertex labels: "
1121 ConnectivityWarning()
1122 << nl << "Edge-point connectivity is inconsistent."
1127 label allPoints = points_.size();
1128 labelList nPointFaces(allPoints, 0);
1130 forAll(faces_, faceI)
1132 const face& curFace = faces_[faceI];
1134 if (curFace.empty())
1139 if (min(curFace) < 0 || max(curFace) > (points_.size()-1))
1141 Pout<< "Face " << faceI
1142 << " contains vertex labels out of range: "
1144 << " Max point index = " << (points_.size()-1) << endl;
1148 ConnectivityWarning()
1149 << nl << "Face-point connectivity is inconsistent."
1153 // Check for unique point-labels
1154 labelHashSet uniquePoints;
1156 forAll(curFace, pointI)
1158 bool inserted = uniquePoints.insert(curFace[pointI]);
1162 Pout<< "Face " << faceI
1163 << " contains identical vertex labels: "
1169 ConnectivityWarning()
1170 << nl << "Face-point connectivity is inconsistent."
1175 // Count faces per point
1176 forAll(curFace, pointI)
1178 nPointFaces[curFace[pointI]]++;
1181 // Ensure that cells on either side of this face
1182 // share just one face.
1183 if (neighbour_[faceI] > -1)
1185 const cell& ownCell = cells_[owner_[faceI]];
1186 const cell& neiCell = cells_[neighbour_[faceI]];
1192 if (findIndex(neiCell, ownCell[fi]) > -1)
1200 Pout<< "Cells: " << nl
1201 << '\t' << owner_[faceI] << ":: " << ownCell << nl
1202 << '\t' << neighbour_[faceI] << " :: " << neiCell << nl
1203 << " share multiple faces. "
1208 ConnectivityWarning()
1209 << nl << "Cell-Face connectivity is inconsistent."
1215 label allFaces = faces_.size();
1216 labelList nCellsPerFace(allFaces, 0);
1218 forAll(cells_, cellI)
1220 const cell& curCell = cells_[cellI];
1222 if (curCell.empty())
1227 if (min(curCell) < 0 || max(curCell) > (faces_.size()-1))
1229 Pout<< "Cell " << cellI
1230 << " contains face labels out of range: " << curCell
1231 << " Max face index = " << (faces_.size()-1) << endl;
1235 ConnectivityWarning()
1236 << nl << "Cell-Face connectivity is inconsistent."
1240 // Check for unique face-labels
1241 labelHashSet uniqueFaces;
1243 forAll(curCell, faceI)
1245 bool inserted = uniqueFaces.insert(curCell[faceI]);
1249 Pout<< "Cell " << cellI
1250 << " contains identical face labels: "
1256 ConnectivityWarning()
1257 << nl << "Cell-Face connectivity is inconsistent."
1261 // Count cells per face
1262 nCellsPerFace[curCell[faceI]]++;
1266 Pout<< "Done." << endl;
1268 Pout<< "Checking face-cell connectivity...";
1270 forAll(nCellsPerFace, faceI)
1272 // This might be a deleted face
1273 if (faceI < nOldFaces_)
1275 if (reverseFaceMap_[faceI] == -1)
1281 if (deletedFaces_.found(faceI))
1287 label uPatch = whichPatch(faceI);
1289 if (nCellsPerFace[faceI] == 0)
1291 // Looks like this is really an unused face.
1292 Pout<< "Face " << faceI << " :: " << faces_[faceI]
1293 << " is unused. Patch: "
1294 << (uPatch > -1 ? boundaryMesh()[uPatch].name() : "Internal")
1299 ConnectivityWarning()
1300 << nl << "Cell-Face connectivity is inconsistent."
1304 if (nCellsPerFace[faceI] != 2 && uPatch == -1)
1306 // Internal face is not shared by exactly two cells
1307 Pout<< "Internal Face " << faceI
1308 << " :: " << faces_[faceI]
1309 << " Owner: " << owner_[faceI]
1310 << " Neighbour: " << neighbour_[faceI]
1311 << " is multiply connected." << nl
1312 << " nCellsPerFace: " << nCellsPerFace[faceI] << nl
1313 << " Patch: Internal" << nl
1316 // Loop through cells and find another instance
1317 forAll(cells_, cellI)
1319 if (findIndex(cells_[cellI], faceI) > -1)
1321 Pout<< " Cell: " << cellI
1322 << " :: " << cells_[cellI]
1329 ConnectivityWarning()
1330 << nl << "Cell-Face connectivity is inconsistent."
1334 if (nCellsPerFace[faceI] != 1 && uPatch > -1)
1336 // Boundary face is not shared by exactly one cell
1337 Pout<< "Boundary Face " << faceI
1338 << " :: " << faces_[faceI]
1339 << " Owner: " << owner_[faceI]
1340 << " Neighbour: " << neighbour_[faceI]
1341 << " is multiply connected." << nl
1342 << " nCellsPerFace: " << nCellsPerFace[faceI] << nl
1343 << " Patch: " << boundaryMesh()[uPatch].name() << nl
1346 // Loop through cells and find another instance
1347 forAll(cells_, cellI)
1349 if (findIndex(cells_[cellI], faceI) > -1)
1351 Pout<< " Cell: " << cellI
1352 << " :: " << cells_[cellI]
1359 ConnectivityWarning()
1360 << nl << "Cell-Face connectivity is inconsistent."
1365 Pout<< "Done." << endl;
1367 Pout<< "Checking for unused points...";
1369 forAll(nPointFaces, pointI)
1371 if (nPointFaces[pointI] == 0)
1373 // This might be a deleted point.
1374 if (pointI < nOldPoints_)
1376 if (reversePointMap_[pointI] == -1)
1383 if (deletedPoints_.found(pointI))
1389 // Looks like this is really an unused point.
1390 Pout<< nl << nl << "Point " << pointI
1391 << " is unused. " << endl;
1395 ConnectivityWarning()
1396 << nl << "Point-Face connectivity is inconsistent."
1401 Pout<< "Done." << endl;
1403 Pout<< "Checking edge-face connectivity...";
1405 label allEdges = edges_.size();
1406 labelList nEdgeFaces(allEdges, 0);
1408 forAll(faceEdges_, faceI)
1410 const labelList& faceEdges = faceEdges_[faceI];
1412 if (faceEdges.empty())
1417 // Check consistency of face-edge-points as well
1418 edgeList eList = faces_[faceI].edges();
1420 forAll(faceEdges,edgeI)
1422 nEdgeFaces[faceEdges[edgeI]]++;
1424 // Check if this edge actually belongs to this face
1426 const edge& edgeToCheck = edges_[faceEdges[edgeI]];
1428 forAll(eList, edgeII)
1430 if (edgeToCheck == eList[edgeII])
1439 Pout<< nl << nl << "Edge: " << faceEdges[edgeI]
1440 << ": " << edgeToCheck << nl
1441 << "was not found in face: " << faceI
1442 << ": " << faces_[faceI] << nl
1443 << "faceEdges: " << faceEdges
1448 ConnectivityWarning()
1449 << nl << "Edge-Face connectivity is inconsistent."
1455 label nInternalEdges = 0;
1456 DynamicList<label> bPatchIDs(10);
1457 labelList patchInfo(boundaryMesh().size(), 0);
1459 forAll(edgeFaces_, edgeI)
1461 const labelList& edgeFaces = edgeFaces_[edgeI];
1463 if (edgeFaces.empty())
1468 if (edgeFaces.size() != nEdgeFaces[edgeI])
1470 Pout<< nl << nl << "Edge: " << edgeI << ": " << edges_[edgeI]
1471 << ": edgeFaces: " << edgeFaces
1472 << nl << UIndirectList<face>(faces_, edgeFaces)
1473 << nl << " Expected nFaces: " << nEdgeFaces[edgeI]
1478 ConnectivityWarning()
1479 << nl << "Edge-Face connectivity is inconsistent."
1486 // Check if this edge belongs to faceEdges for each face
1487 forAll(edgeFaces, faceI)
1489 const labelList& faceEdges = faceEdges_[edgeFaces[faceI]];
1491 if (findIndex(faceEdges, edgeI) == -1)
1493 Pout<< nl << nl << "Edge: " << edgeI << ": " << edges_[edgeI]
1494 << ", edgeFaces: " << edgeFaces
1495 << nl << UIndirectList<face>(faces_, edgeFaces)
1496 << "was not found in faceEdges of face: "
1497 << edgeFaces[faceI] << ": " << faces_[edgeFaces[faceI]]
1498 << nl << "faceEdges: " << faceEdges
1499 << nl << UIndirectList<edge>(edges_, faceEdges)
1504 ConnectivityWarning()
1505 << nl << "Edge-Face connectivity is inconsistent."
1509 if (neighbour_[edgeFaces[faceI]] == -1)
1511 // Add to list of patch IDs
1512 bPatchIDs.append(whichPatch(edgeFaces[faceI]));
1522 // Check if this edge is actually internal.
1523 if (whichEdgePatch(edgeI) >= 0)
1525 Pout<< "Edge: " << edgeI
1526 << ": " << edges_[edgeI] << " is internal, "
1527 << " but patch is specified as: "
1528 << whichEdgePatch(edgeI)
1536 label patchID = whichEdgePatch(edgeI);
1538 // Check if this edge is actually on a boundary.
1541 Pout<< "Edge: " << edgeI
1542 << ": " << edges_[edgeI]
1543 << " is on a boundary, but patch is specified as: "
1550 patchInfo[patchID]++;
1553 bool failedManifoldCheck = false;
1557 if (Pstream::parRun())
1559 // Pinched manifolds should be allowed in parallel
1560 failedManifoldCheck = false;
1564 failedManifoldCheck = true;
1568 if (failedManifoldCheck)
1570 // Write out for post-processing
1571 forAll(bPatchIDs, faceI)
1573 if (bPatchIDs[faceI] == -1)
1575 Pout<< " Edge: " << edgeI
1576 << " Face Patch: Internal" << nl;
1580 Pout<< " Edge: " << edgeI
1582 << boundaryMesh()[bPatchIDs[faceI]].name() << nl;
1588 writeVTK("pinched_" + Foam::name(edgeI), edgeFaces, 2);
1590 Pout<< "Edge: " << edgeI
1591 << ": " << edges_[edgeI]
1593 << " boundary faces connected to it." << nl
1594 << " Pinched manifolds are not allowed."
1602 if (nInternalEdges != nInternalEdges_)
1604 Pout<< nl << "Internal edge-count is inconsistent." << nl
1605 << " Counted internal edges: " << nInternalEdges
1606 << " Actual count: " << nInternalEdges_ << endl;
1611 forAll(patchInfo, patchI)
1613 if (patchInfo[patchI] != edgePatchSizes_[patchI])
1615 Pout<< "Patch-count is inconsistent." << nl
1616 << " Patch: " << patchI
1617 << " Counted edges: " << patchInfo[patchI]
1618 << " Actual count: " << edgePatchSizes_[patchI] << endl;
1624 // Check added edge patches to ensure that it is consistent
1625 forAllConstIter(Map<label>, addedEdgePatches_, aepIter)
1627 label key = aepIter.key();
1628 label patch = aepIter();
1631 const labelList& edgeFaces = edgeFaces_[key];
1633 // Check if any faces on boundaries
1634 forAll(edgeFaces, faceI)
1636 if (neighbour_[edgeFaces[faceI]] == -1)
1642 if ((patch < 0) && (nBF > 0))
1644 Pout<< nl << nl << "Edge: " << key
1645 << "::" << edges_[key]
1646 << ", edgeFaces: " << edgeFaces
1647 << " is internal, but contains boundary faces."
1653 if ((patch >= 0) && (nBF != 2))
1655 if (!Pstream::parRun())
1657 Pout<< nl << nl << "Edge: " << key
1658 << "::" << edges_[key]
1659 << ", edgeFaces: " << edgeFaces
1660 << " is on a boundary patch, but doesn't contain"
1661 << " two boundary faces."
1669 Pout<< "Done." << endl;
1671 // Check coupled-patch sizes
1672 forAll(patchCoupling_, patchI)
1674 if (patchCoupling_(patchI))
1676 const coupleMap& cMap = patchCoupling_[patchI].map();
1678 label mSize = patchSizes_[cMap.masterIndex()];
1679 label sSize = patchSizes_[cMap.slaveIndex()];
1683 Pout<< "Coupled patch-count is inconsistent." << nl
1684 << " Master Patch: " << cMap.masterIndex()
1685 << " Count: " << mSize << nl
1686 << " Slave Patch: " << cMap.slaveIndex()
1687 << " Count: " << sSize
1697 Pout<< "Checking point-edge connectivity...";
1699 label allPoints = points_.size();
1700 List<labelHashSet> hlPointEdges(allPoints);
1702 forAll(edges_, edgeI)
1704 if (edgeFaces_[edgeI].size())
1706 hlPointEdges[edges_[edgeI][0]].insert(edgeI);
1707 hlPointEdges[edges_[edgeI][1]].insert(edgeI);
1711 forAll(pointEdges_, pointI)
1713 const labelList& pointEdges = pointEdges_[pointI];
1715 if (pointEdges.empty())
1720 forAll(pointEdges, edgeI)
1722 if (!hlPointEdges[pointI].found(pointEdges[edgeI]))
1724 Pout<< nl << nl << "Point: " << pointI << nl
1725 << "pointEdges: " << pointEdges << nl
1726 << "hlPointEdges: " << hlPointEdges[pointI]
1731 ConnectivityWarning()
1732 << nl << "Point-Edge connectivity is inconsistent."
1737 // Do a size check as well
1740 hlPointEdges[pointI].size() != pointEdges.size() ||
1741 pointEdges.size() == 1
1744 Pout<< nl << nl << "Point: " << pointI << nl
1745 << "pointEdges: " << pointEdges << nl
1746 << "hlPointEdges: " << hlPointEdges[pointI]
1751 ConnectivityWarning()
1752 << nl << "Size inconsistency."
1753 << nl << "Point-Edge connectivity is inconsistent."
1758 Pout<< "Done." << endl;
1761 Pout<< "Checking cell-point connectivity...";
1763 // Loop through all cells and construct cell-to-node
1765 label allCells = cells_.size();
1766 labelList cellIndex(allCells);
1767 List<labelHashSet> cellToNode(allCells);
1769 forAll(cells_, cellI)
1771 const cell& thisCell = cells_[cellI];
1773 if (thisCell.empty())
1778 cellIndex[cIndex] = cellI;
1780 forAll(thisCell, faceI)
1782 const labelList& fEdges = faceEdges_[thisCell[faceI]];
1784 forAll(fEdges, edgeI)
1786 const edge& thisEdge = edges_[fEdges[edgeI]];
1788 if (!cellToNode[cIndex].found(thisEdge[0]))
1790 cellToNode[cIndex].insert(thisEdge[0]);
1793 if (!cellToNode[cIndex].found(thisEdge[1]))
1795 cellToNode[cIndex].insert(thisEdge[1]);
1804 cellIndex.setSize(cIndex);
1805 cellToNode.setSize(cIndex);
1807 // Preliminary check for size
1808 forAll(cellToNode, cellI)
1810 // Check for hexahedral cells
1813 (cellToNode[cellI].size() == 8) &&
1814 (cells_[cellIndex[cellI]].size() == 6)
1822 (cellToNode[cellI].size() != 6 && twoDMesh_) ||
1823 (cellToNode[cellI].size() != 4 && !twoDMesh_)
1826 Pout<< nl << "Warning: Cell: "
1827 << cellIndex[cellI] << " is inconsistent. "
1830 const cell& failedCell = cells_[cellIndex[cellI]];
1832 Pout<< "Cell faces: " << failedCell << endl;
1834 forAll(failedCell, faceI)
1836 Pout<< "\tFace: " << failedCell[faceI]
1837 << " :: " << faces_[failedCell[faceI]]
1840 const labelList& fEdges = faceEdges_[failedCell[faceI]];
1842 forAll(fEdges, edgeI)
1844 Pout<< "\t\tEdge: " << fEdges[edgeI]
1845 << " :: " << edges_[fEdges[edgeI]]
1854 Pout<< "Done." << endl;
1860 "void dynamicTopoFvMesh::checkConnectivity"
1861 "(const label maxErrors) const"
1863 << nFailedChecks << " failures were found in connectivity."
1864 << abort(FatalError);
1869 // Utility method to check the quality
1870 // of a triangular face after bisection.
1871 // - Returns 'true' if the bisection in NOT feasible.
1872 bool dynamicTopoFvMesh::checkBisection
1875 const label bFaceIndex,
1879 scalar bisectionQuality = GREAT, minArea = GREAT;
1881 label commonEdge = -1;
1883 // Find the common edge index
1884 meshOps::findCommonEdge
1893 const edge& checkEdge = edges_[commonEdge];
1894 const face& checkFace = faces_[bFaceIndex];
1896 // Compute old / new mid-points
1901 oldPoints_[checkEdge.start()],
1902 oldPoints_[checkEdge.end()]
1910 points_[checkEdge.start()],
1911 points_[checkEdge.end()]
1915 // Find the isolated point on the face
1916 label iPoint = -1, nPoint = -1;
1918 meshOps::findIsolatedPoint
1926 // Find the other point
1929 (nPoint == checkEdge.start()) ?
1930 checkEdge.end() : checkEdge.start()
1933 // Configure old / new triangle faces
1934 FixedList<FixedList<point, 3>, 2> tfNew, tfOld;
1936 tfNew[0][0] = mpNew;
1937 tfNew[0][1] = points_[iPoint];
1938 tfNew[0][2] = points_[nPoint];
1940 tfOld[0][0] = mpOld;
1941 tfOld[0][1] = oldPoints_[iPoint];
1942 tfOld[0][2] = oldPoints_[nPoint];
1944 tfNew[1][0] = points_[oPoint];
1945 tfNew[1][1] = points_[iPoint];
1946 tfNew[1][2] = mpNew;
1948 tfOld[1][0] = oldPoints_[oPoint];
1949 tfOld[1][1] = oldPoints_[iPoint];
1950 tfOld[1][2] = mpOld;
1952 // Assume XY plane here
1953 vector n = vector(0,0,1);
1957 // Configure triangles
1958 triPointRef tprNew(tfNew[fI][0], tfNew[fI][1], tfNew[fI][2]);
1959 triPointRef tprOld(tfOld[fI][0], tfOld[fI][1], tfOld[fI][2]);
1968 ((tprNew.centre() & n) * n)
1975 mag(tprOld.normal()) *
1980 ((tprOld.centre() & n) * n)
1985 // Update statistics
1986 minArea = Foam::min(minArea, oldArea);
1987 bisectionQuality = Foam::min(bisectionQuality, tQuality);
1990 // Final quality check
1991 if (bisectionQuality < sliverThreshold_ && !forceOp)
1996 // Negative quality is a no-no
1997 if (bisectionQuality < 0.0)
2002 // Negative old-area is also a no-no
2008 // No problems, so a bisection is feasible.
2013 // Utility method to check whether the faces in triFaces will yield
2014 // valid triangles when 'pointIndex' is moved to 'newPoint'.
2015 // - The routine performs metric-based checks.
2016 // - Returns 'true' if the collapse in NOT feasible.
2017 // - Does not reference member data, because this function
2018 // is also used on subMeshes
2019 bool dynamicTopoFvMesh::checkCollapse
2021 const dynamicTopoFvMesh& mesh,
2022 const labelList& triFaces,
2023 const FixedList<label,2>& c0BdyIndex,
2024 const FixedList<label,2>& c1BdyIndex,
2025 const FixedList<label,2>& pointIndex,
2026 const FixedList<point,2>& newPoint,
2027 const FixedList<point,2>& oldPoint,
2028 scalar& collapseQuality,
2029 const bool checkNeighbour,
2034 collapseQuality = GREAT;
2035 scalar minArea = GREAT;
2037 forAll(triFaces, indexI)
2041 (triFaces[indexI] == c0BdyIndex[0])
2042 || (triFaces[indexI] == c0BdyIndex[1])
2052 (triFaces[indexI] == c1BdyIndex[0])
2053 || (triFaces[indexI] == c1BdyIndex[1])
2060 const face& checkFace = mesh.faces_[triFaces[indexI]];
2062 // Configure a triangle face
2063 FixedList<point, 3> tFNew(vector::zero);
2064 FixedList<point, 3> tFOld(vector::zero);
2066 // Make necessary replacements
2067 forAll(checkFace, pointI)
2069 tFNew[pointI] = mesh.points_[checkFace[pointI]];
2070 tFOld[pointI] = mesh.oldPoints_[checkFace[pointI]];
2072 if (checkFace[pointI] == pointIndex[0])
2074 tFNew[pointI] = newPoint[0];
2075 tFOld[pointI] = oldPoint[0];
2078 if (checkFace[pointI] == pointIndex[1])
2080 tFNew[pointI] = newPoint[1];
2081 tFOld[pointI] = oldPoint[1];
2085 // Configure triangles
2086 triPointRef tprNew(tFNew[0], tFNew[1], tFNew[2]);
2087 triPointRef tprOld(tFOld[0], tFOld[1], tFOld[2]);
2089 // Assume XY plane here
2090 vector n = vector(0,0,1);
2092 // Compute the quality.
2093 // Assume centre-plane passes through origin
2101 ((tprNew.centre() & n) * n)
2108 mag(tprOld.normal()) *
2113 ((tprOld.centre() & n) * n)
2118 // Update statistics
2119 minArea = Foam::min(minArea, oldArea);
2120 collapseQuality = Foam::min(collapseQuality, tQuality);
2123 // Final quality check
2124 if (collapseQuality < mesh.sliverThreshold_ && !forceOp)
2128 Pout<< " * * * 2D checkCollapse * * * " << nl
2129 << " collapseQuality: " << collapseQuality
2130 << " below threshold: " << mesh.sliverThreshold_
2137 // Negative quality is a no-no
2138 if (collapseQuality < 0.0)
2142 Pout<< " * * * 2D checkCollapse * * * " << nl
2143 << " Negative collapseQuality: " << collapseQuality << nl
2144 << " Operation cannot be forced."
2151 // Negative old-area is also a no-no
2156 Pout<< " * * * 2D checkCollapse * * * " << nl
2157 << " minArea: " << minArea << nl
2158 << " Operation cannot be forced."
2165 // No problems, so a collapse is feasible.
2170 // Utility method to check whether the cell given by 'cellIndex' will yield
2171 // a valid cell when 'pointIndex' is moved to 'newPoint'.
2172 // - The routine performs metric-based checks.
2173 // - Returns 'true' if the collapse in NOT feasible, and
2174 // makes entries in cellsChecked to avoid repetitive checks.
2175 bool dynamicTopoFvMesh::checkCollapse
2177 const point& newPoint,
2178 const point& oldPoint,
2179 const label pointIndex,
2180 const label cellIndex,
2181 DynamicList<label>& cellsChecked,
2182 scalar& collapseQuality,
2186 label faceIndex = -1;
2187 scalar cQuality = 0;
2188 scalar oldVolume = 0;
2189 scalar newVolume = 0;
2190 const cell& cellToCheck = cells_[cellIndex];
2192 // Look for a face that doesn't contain 'pointIndex'
2193 forAll(cellToCheck, faceI)
2195 const face& currFace = faces_[cellToCheck[faceI]];
2197 if (currFace.which(pointIndex) < 0)
2199 faceIndex = cellToCheck[faceI];
2204 // Compute cell-volume
2205 const face& faceToCheck = faces_[faceIndex];
2207 if (owner_[faceIndex] == cellIndex)
2213 points_[faceToCheck[2]],
2214 points_[faceToCheck[1]],
2215 points_[faceToCheck[0]],
2224 points_[faceToCheck[2]],
2225 points_[faceToCheck[1]],
2226 points_[faceToCheck[0]],
2235 oldPoints_[faceToCheck[2]],
2236 oldPoints_[faceToCheck[1]],
2237 oldPoints_[faceToCheck[0]],
2248 points_[faceToCheck[0]],
2249 points_[faceToCheck[1]],
2250 points_[faceToCheck[2]],
2259 points_[faceToCheck[0]],
2260 points_[faceToCheck[1]],
2261 points_[faceToCheck[2]],
2270 oldPoints_[faceToCheck[0]],
2271 oldPoints_[faceToCheck[1]],
2272 oldPoints_[faceToCheck[2]],
2278 // Final quality check
2279 if (cQuality < sliverThreshold_ && !forceOp)
2283 Pout<< " * * * 3D checkCollapse * * * " << nl
2284 << "\nCollapsing cell: " << cellIndex
2285 << " containing points:\n"
2286 << faceToCheck[0] << "," << faceToCheck[1] << ","
2287 << faceToCheck[2] << "," << pointIndex << nl
2288 << "will yield a quality of: " << cQuality
2289 << ", when " << pointIndex
2290 << " is moved to location: " << nl
2298 // Negative quality is a no-no
2303 Pout<< " * * * 3D checkCollapse * * * " << nl
2304 << "\nCollapsing cell: " << cellIndex
2305 << " containing points:\n"
2306 << faceToCheck[0] << "," << faceToCheck[1] << ","
2307 << faceToCheck[2] << "," << pointIndex << nl
2308 << "will yield a quality of: " << cQuality
2309 << ", when " << pointIndex
2310 << " is moved to location: " << nl
2312 << "Operation cannot be forced."
2319 // Negative old-volume is also a no-no
2320 if (oldVolume < 0.0 || (mag(oldVolume) < mag(0.1 * newVolume)))
2324 Pout<< " * * * 3D checkCollapse * * * " << nl
2325 << "\nCollapsing cell: " << cellIndex
2326 << " containing points:\n"
2327 << faceToCheck[0] << "," << faceToCheck[1] << ","
2328 << faceToCheck[2] << "," << pointIndex << nl
2329 << "will yield an old-volume of: " << oldVolume
2330 << ", when " << pointIndex
2331 << " is moved to location: " << nl
2333 << "newVolume: " << newVolume << nl
2334 << "Operation cannot be forced."
2341 // No problems, so a collapse is feasible
2342 cellsChecked.append(cellIndex);
2344 // Update input quality
2345 collapseQuality = Foam::min(collapseQuality, cQuality);
2351 } // End namespace Foam
2353 // ************************************************************************* //