1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | foam-extend: Open Source CFD
4 \\ / O peration | Version: 3.2
5 \\ / A nd | Web: http://www.foam-extend.org
6 \\/ M anipulation | For copyright notice see file Copyright
7 -------------------------------------------------------------------------------
9 This file is part of foam-extend.
11 foam-extend 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 3 of the License, or (at your
14 option) any later version.
16 foam-extend is distributed in the hope that it will be useful, but
17 WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with foam-extend. If not, see <http://www.gnu.org/licenses/>.
28 Functions specific to connectivity checking and debugging
32 University of Massachusetts Amherst
35 \*---------------------------------------------------------------------------*/
37 #include "dynamicTopoFvMesh.H"
40 #include "volFields.H"
41 #include "triPointRef.H"
42 #include "tetPointRef.H"
43 #include "coupledInfo.H"
48 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
50 // Compute mesh-quality, and return true if no slivers are present
51 bool dynamicTopoFvMesh::meshQuality
56 // Compute statistics on the fly
57 label nCells = 0, minCell = -1;
58 scalar maxQuality = -GREAT;
59 scalar minQuality = GREAT;
60 scalar cQuality, meanQuality = 0.0;
63 bool sliversAbsent = true;
64 thresholdSlivers_.clear();
66 // Loop through all cells in the mesh and compute cell quality
69 const cell& cellToCheck = cells_[cellI];
71 if (cellToCheck.empty())
76 // Skip hexahedral cells
77 if (cellToCheck.size() == 6)
80 meanQuality += cQuality;
83 maxQuality = Foam::max(cQuality, maxQuality);
84 minQuality = Foam::min(cQuality, minQuality);
93 // Assume XY plane here
94 vector n = vector(0,0,1);
96 // Get a triangular boundary face
97 forAll(cellToCheck, faceI)
99 const face& faceToCheck = faces_[cellToCheck[faceI]];
101 if (faceToCheck.size() == 3)
105 points_[faceToCheck[0]],
106 points_[faceToCheck[1]],
107 points_[faceToCheck[2]]
110 // Assume centre-plane passes through origin
118 ((tpr.centre() & n) * n)
129 const label bfIndex = cellToCheck[0];
130 const label cfIndex = cellToCheck[1];
132 const face& baseFace = faces_[bfIndex];
133 const face& checkFace = faces_[cfIndex];
135 // Get the fourth point
138 meshOps::findIsolatedPoint(baseFace, checkFace)
141 // Compute cell quality
142 if (owner_[bfIndex] == cellI)
148 points_[baseFace[2]],
149 points_[baseFace[1]],
150 points_[baseFace[0]],
161 points_[baseFace[0]],
162 points_[baseFace[1]],
163 points_[baseFace[2]],
171 maxQuality = Foam::max(cQuality, maxQuality);
173 if (cQuality < minQuality)
175 minQuality = cQuality;
179 meanQuality += cQuality;
182 // Add to the list of slivers
183 if ((cQuality < sliverThreshold_) && (cQuality > 0.0))
185 thresholdSlivers_.insert(cellI, cQuality);
189 if (thresholdSlivers_.size())
191 sliversAbsent = false;
194 // Reduce across processors.
195 reduce(sliversAbsent, andOp<bool>());
197 // Output statistics:
198 if (outputOption || (debug > 0))
200 if (minQuality < 0.0)
203 << " Warning: Minimum cell quality is: " << minQuality
204 << " at cell: " << minCell
208 // Reduce statistics across processors.
209 reduce(minQuality, minOp<scalar>());
210 reduce(maxQuality, maxOp<scalar>());
211 reduce(meanQuality, sumOp<scalar>());
212 reduce(nCells, sumOp<label>());
215 << " ~~~ Mesh Quality Statistics ~~~ " << nl
216 << " Min: " << minQuality << nl
217 << " Max: " << maxQuality << nl
218 << " Mean: " << meanQuality/nCells << nl
219 << " Cells: " << nCells << nl
220 << " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ " << nl
224 return sliversAbsent;
228 // Utility to check whether points of an edge lie on a boundary.
229 const FixedList<bool,2>
230 dynamicTopoFvMesh::checkEdgeBoundary
235 FixedList<bool,2> edgeBoundary(false);
237 const edge& edgeToCheck = edges_[eIndex];
239 // Loop through edges connected to both points,
240 // and check if any of them lie on boundaries.
241 // Used to ensure that collapses happen towards boundaries.
242 forAll(edgeToCheck, pointI)
244 const labelList& pEdges = pointEdges_[edgeToCheck[pointI]];
246 forAll(pEdges, edgeI)
248 // Determine the patch this edge belongs to
249 if (whichEdgePatch(pEdges[edgeI]) > -1)
251 edgeBoundary[pointI] = true;
261 // Check whether the given edge is on a bounding curve
262 // - If nProcCurves is provided, the variable is incremented
263 // if the edge is processor-coupled
264 bool dynamicTopoFvMesh::checkBoundingCurve
267 const bool overRidePurityCheck,
271 // Internal edges don't count
272 label edgePatch = -1;
274 // If this entity was deleted, skip it.
275 if (edgeFaces_[eIndex].empty())
277 // Return true so that swap3DEdges skips this edge.
281 // Check if two boundary faces lie on different face-patches
282 bool procCoupled = false;
283 FixedList<label, 2> fPatches(-1);
284 FixedList<vector, 2> fNorm(vector::zero);
286 if ((edgePatch = whichEdgePatch(eIndex)) < 0)
292 // Check whether this edge shouldn't be swapped
293 if (findIndex(noSwapPatchIDs_, edgePatch) > -1)
298 // Explicit check for processor edges (both 2D and 3D)
299 if (processorCoupledEntity(eIndex, false, true))
301 // Increment nProcCurves
307 // Check for pure processor edge, and if not,
308 // fetch boundary patch labels / normals
311 processorCoupledEntity
322 // 'Pure' processor coupled edges don't count
326 if (!overRidePurityCheck)
328 // This edge lies between a processor and physical patch,
329 // - This a bounding curve (unless an override is requested)
330 // - An override is warranted for 2-2 swaps on impure edges,
331 // which is typically requested by swap3DEdges.
335 // Specify that the edge is procCoupled
342 // Normalize patch normals from coupled check
343 fNorm[0] /= mag(fNorm[0]) + VSMALL;
344 fNorm[1] /= mag(fNorm[1]) + VSMALL;
348 // Fetch patch indices / normals
349 const labelList& eFaces = edgeFaces_[eIndex];
351 label fPatch = -1, count = 0;
353 forAll(eFaces, faceI)
355 if ((fPatch = whichPatch(eFaces[faceI])) > -1)
357 // Obtain the normal.
358 fNorm[count] = faces_[eFaces[faceI]].normal(points_);
361 fNorm[count] /= mag(fNorm[count]) + VSMALL;
364 fPatches[count] = fPatch;
376 // Check for legitimate patches
377 if (fPatches[0] < 0 || fPatches[1] < 0)
379 const labelList& eFaces = edgeFaces_[eIndex];
381 forAll(eFaces, faceI)
383 Pout<< " Face: " << eFaces[faceI]
384 << " :: " << faces_[eFaces[faceI]]
385 << " Patch: " << whichPatch(eFaces[faceI]) << nl;
388 label epI = whichEdgePatch(eIndex);
392 "bool dynamicTopoFvMesh::checkBoundingCurve"
393 "(const label, const bool) const"
395 << " Edge: " << eIndex << ":: " << edges_[eIndex]
397 << (epI < 0 ? "Internal" : boundaryMesh()[epI].name())
398 << " edgeFaces: " << eFaces << nl
399 << " expected 2 boundary patches." << nl
400 << " fPatches[0]: " << fPatches[0] << nl
401 << " fPatches[1]: " << fPatches[1] << nl
402 << " fNorm[0]: " << fNorm[0] << nl
403 << " fNorm[1]: " << fNorm[1] << nl
404 << " coupledModification: " << coupledModification_
405 << abort(FatalError);
408 scalar deviation = (fNorm[0] & fNorm[1]);
410 // Check if the swap-curvature is too high
411 if (mag(deviation) < swapDeviation_)
416 // Check if the edge borders two different patches
417 if (fPatches[0] != fPatches[1])
422 // Not on a bounding curve
427 // Check triangulation quality for an edge index
428 bool dynamicTopoFvMesh::checkQuality
432 const PtrList<scalarListList>& Q,
433 const scalar minQuality,
434 const label checkIndex
437 bool myResult = false;
440 if (Q[checkIndex][0][m[checkIndex]-1] > minQuality)
447 << " eIndex: " << eIndex
448 << " minQuality: " << minQuality
449 << " newQuality: " << Q[checkIndex][0][m[checkIndex]-1]
454 if (coupledModification_)
456 // Only locally coupled indices require checks
457 if (locallyCoupledEntity(eIndex))
459 // Check the quality of the slave edge as well.
462 // Loop through masterToSlave and determine the slave index.
463 forAll(patchCoupling_, patchI)
465 if (patchCoupling_(patchI))
467 const label edgeEnum = coupleMap::EDGE;
468 const coupleMap& cMap = patchCoupling_[patchI].map();
470 if ((sIndex = cMap.findSlave(edgeEnum, eIndex)) > -1)
481 "bool dynamicTopoFvMesh::checkQuality\n"
483 " const label eIndex,\n"
484 " const labelList& m,\n"
485 " const PtrList<scalarListList>& Q,\n"
486 " const scalar minQuality,\n"
487 " const label checkIndex\n"
490 << "Coupled maps were improperly specified." << nl
491 << " Slave index not found for: " << nl
492 << " Edge: " << eIndex << nl
493 << abort(FatalError);
496 // Turn off switch temporarily.
497 unsetCoupledModification();
499 // Recursively call for the slave edge.
502 myResult && checkQuality(sIndex, m, Q, minQuality, 1)
506 setCoupledModification();
514 // Print out tables for debugging
515 void dynamicTopoFvMesh::printTables
518 const PtrList<scalarListList>& Q,
519 const PtrList<labelListList>& K,
520 const label checkIndex
523 Pout<< "m: " << m[checkIndex] << endl;
532 for (label j = 0; j < m[checkIndex]; j++)
534 Pout<< setw(12) << j;
539 for (label j = 0; j < m[checkIndex]; j++)
541 Pout<< "-------------";
546 for (label i = 0; i < (m[checkIndex]-2); i++)
550 for (label j = 0; j < m[checkIndex]; j++)
552 Pout<< setw(12) << Q[checkIndex][i][j];
565 for (label j = 0; j < m[checkIndex]; j++)
567 Pout<< setw(12) << j;
572 for (label i = 0; i < (m[checkIndex]-2); i++)
576 for (label j = 0; j < m[checkIndex]; j++)
578 Pout<< setw(12) << K[checkIndex][i][j];
588 // Check old-volumes for an input triangulation
589 bool dynamicTopoFvMesh::checkTriangulationVolumes
592 const labelList& hullVertices,
593 const labelListList& triangulations
596 label m = hullVertices.size();
597 scalar oldTetVol = 0.0, newTetVol = 0.0;
599 const edge& edgeToCheck = edges_[eIndex];
601 for (label i = 0; i < (m-2); i++)
603 // Compute volume for the upper-half
608 points_[hullVertices[triangulations[0][i]]],
609 points_[hullVertices[triangulations[1][i]]],
610 points_[hullVertices[triangulations[2][i]]],
611 points_[edgeToCheck[0]]
619 oldPoints_[hullVertices[triangulations[0][i]]],
620 oldPoints_[hullVertices[triangulations[1][i]]],
621 oldPoints_[hullVertices[triangulations[2][i]]],
622 oldPoints_[edgeToCheck[0]]
626 if (oldTetVol < 0.0 || (mag(oldTetVol) < mag(0.1 * newTetVol)))
630 Pout<< " Swap sequence leads to bad old-volumes." << nl
631 << " Edge: " << edgeToCheck << nl
632 << " using Points: " << nl
633 << oldPoints_[hullVertices[triangulations[0][i]]] << nl
634 << oldPoints_[hullVertices[triangulations[1][i]]] << nl
635 << oldPoints_[hullVertices[triangulations[2][i]]] << nl
636 << oldPoints_[edgeToCheck[0]] << nl
637 << " Old Volume: " << oldTetVol << nl
638 << " New Volume: " << newTetVol << nl
649 points_[hullVertices[triangulations[2][i]]],
650 points_[hullVertices[triangulations[1][i]]],
651 points_[hullVertices[triangulations[0][i]]],
652 points_[edgeToCheck[1]]
660 oldPoints_[hullVertices[triangulations[2][i]]],
661 oldPoints_[hullVertices[triangulations[1][i]]],
662 oldPoints_[hullVertices[triangulations[0][i]]],
663 oldPoints_[edgeToCheck[1]]
667 if (oldTetVol < 0.0 || (mag(oldTetVol) < mag(0.1 * newTetVol)))
671 Pout<< " Swap sequence leads to bad old-volumes." << nl
672 << " Edge: " << edgeToCheck << nl
673 << " using Points: " << nl
674 << oldPoints_[hullVertices[triangulations[2][i]]] << nl
675 << oldPoints_[hullVertices[triangulations[1][i]]] << nl
676 << oldPoints_[hullVertices[triangulations[0][i]]] << nl
677 << oldPoints_[edgeToCheck[1]] << nl
678 << " Old Volume: " << oldTetVol << nl
679 << " New Volume: " << newTetVol << nl
691 // Write out connectivity for an edge
692 void dynamicTopoFvMesh::writeEdgeConnectivity
698 writeVTK("Edge_" + Foam::name(eIndex), eIndex, 1, false, true);
700 const labelList& eFaces = edgeFaces_[eIndex];
702 // Write out edge faces
708 + Foam::name(Pstream::myProcNo()),
713 dynamicLabelList edgeCells(10);
715 forAll(eFaces, faceI)
717 label pIdx = whichPatch(eFaces[faceI]);
719 word pName((pIdx < 0) ? "Internal" : boundaryMesh()[pIdx].name());
721 Pout<< " Face: " << eFaces[faceI]
722 << " :: " << faces_[eFaces[faceI]]
723 << " Patch: " << pName
724 << " Proc: " << Pstream::myProcNo() << nl;
726 label own = owner_[eFaces[faceI]];
727 label nei = neighbour_[eFaces[faceI]];
729 if (findIndex(edgeCells, own) == -1)
731 edgeCells.append(own);
739 if (findIndex(edgeCells, nei) == -1)
741 edgeCells.append(nei);
745 // Write out cells connected to edge
751 + Foam::name(Pstream::myProcNo()),
761 // Check processors for coupling
762 forAll(procIndices_, pI)
764 // Fetch reference to subMesh
765 const coupleMap& cMap = recvMeshes_[pI].map();
766 const dynamicTopoFvMesh& mesh = recvMeshes_[pI].subMesh();
770 if ((sI = cMap.findSlave(coupleMap::EDGE, eIndex)) == -1)
775 const edge& slaveEdge = mesh.edges_[sI];
776 const labelList& seFaces = mesh.edgeFaces_[sI];
780 cMap.findMaster(coupleMap::POINT, slaveEdge[0]),
781 cMap.findMaster(coupleMap::POINT, slaveEdge[1])
784 Pout<< " >> Edge: " << sI << "::" << slaveEdge
785 << " mapped: " << cE << nl;
792 + Foam::name(procIndices_[pI]),
797 // Clear existing list
800 forAll(seFaces, faceI)
802 label pIdx = mesh.whichPatch(seFaces[faceI]);
808 mesh.boundaryMesh()[pIdx].name()
811 Pout<< " Face: " << seFaces[faceI]
812 << " :: " << mesh.faces_[seFaces[faceI]]
813 << " Patch: " << pName
814 << " Proc: " << procIndices_[pI] << nl;
816 label own = mesh.owner_[seFaces[faceI]];
817 label nei = mesh.neighbour_[seFaces[faceI]];
819 if (findIndex(edgeCells, own) == -1)
821 edgeCells.append(own);
829 if (findIndex(edgeCells, nei) == -1)
831 edgeCells.append(nei);
840 + Foam::name(procIndices_[pI]),
848 // Output an entity as a VTK file
849 void dynamicTopoFvMesh::writeVTK
853 const label primitiveType,
854 const bool useOldConnectivity,
855 const bool useOldPoints
861 labelList(1, entity),
869 // Output a list of primitives as a VTK file.
870 // - primitiveType is:
875 void dynamicTopoFvMesh::writeVTK
878 const labelList& cList,
879 const label primitiveType,
880 const bool useOldConnectivity,
881 const bool useOldPoints,
882 const UList<scalar>& scalField,
883 const UList<label>& lablField
886 // Check if spatial bounding box has been specified
887 const dictionary& meshSubDict = dict_.subDict("dynamicTopoFvMesh");
889 labelList entityList;
891 if (meshSubDict.found("spatialDebug") && !useOldConnectivity)
893 // Read the bounding box
896 meshSubDict.subDict("spatialDebug").lookup("debugBoundBox")
899 dynamicLabelList cSubList(10);
903 label index = cList[cellI];
910 point containPoint(vector::zero);
912 switch (primitiveType)
914 // Are we looking at points?
917 containPoint = points_[index];
921 // Are we looking at edges?
924 containPoint = edges_[index].centre(points_);
928 // Are we looking at faces?
931 containPoint = faces_[index].centre(points_);
935 // Are we looking at cells?
941 meshOps::cellCentreAndVolume
956 // Is the point of interest?
957 if (bb.contains(containPoint))
959 cSubList.append(index);
963 // If nothing is present, don't write out anything
964 if (cSubList.empty())
970 // Take over contents
971 entityList = cSubList;
976 // Conventional output
982 if (useOldConnectivity)
984 // Use points from polyMesh
995 polyMesh::faceOwner(),
1038 // Return the status report interval
1039 scalar dynamicTopoFvMesh::reportInterval() const
1041 // Default to 1 second
1042 scalar interval = 1.0;
1044 const dictionary& meshSubDict = dict_.subDict("dynamicTopoFvMesh");
1046 if (meshSubDict.found("reportInterval") || mandatory_)
1048 interval = readScalar(meshSubDict.lookup("reportInterval"));
1050 // Prevent reports if necessary
1051 if (interval < VSMALL)
1061 // Check the state of connectivity lists
1062 void dynamicTopoFvMesh::checkConnectivity(const label maxErrors) const
1064 label nFailedChecks = 0;
1066 messageStream ConnectivityWarning
1068 "dynamicTopoFvMesh Connectivity Warning",
1069 messageStream::SERIOUS,
1073 // Check face-label ranges
1074 Pout<< "Checking index ranges...";
1076 forAll(edges_, edgeI)
1078 const edge& curEdge = edges_[edgeI];
1080 if (curEdge == edge(-1, -1))
1087 curEdge[0] < 0 || curEdge[0] > (points_.size()-1) ||
1088 curEdge[1] < 0 || curEdge[1] > (points_.size()-1)
1091 Pout<< "Edge " << edgeI
1092 << " contains vertex labels out of range: "
1094 << " Max point index = " << (points_.size()-1) << endl;
1098 ConnectivityWarning()
1099 << nl << "Edge-point connectivity is inconsistent."
1103 // Check for unique point-labels
1104 if (curEdge[0] == curEdge[1])
1106 Pout<< "Edge " << edgeI
1107 << " contains identical vertex labels: "
1113 ConnectivityWarning()
1114 << nl << "Edge-point connectivity is inconsistent."
1119 label allPoints = points_.size();
1120 labelList nPointFaces(allPoints, 0);
1122 forAll(faces_, faceI)
1124 const face& curFace = faces_[faceI];
1126 if (curFace.empty())
1128 // This might be a deleted face
1129 if (faceI < nOldFaces_)
1131 if (reverseFaceMap_[faceI] == -1)
1137 if (deletedFaces_.found(faceI))
1142 Pout<< "Face " << faceI
1143 << " has no vertex labels."
1150 if (min(curFace) < 0 || max(curFace) > (points_.size()-1))
1152 Pout<< "Face " << faceI
1153 << " contains vertex labels out of range: "
1155 << " Max point index = " << (points_.size()-1) << endl;
1159 ConnectivityWarning()
1160 << nl << "Face-point connectivity is inconsistent."
1164 // Check for unique point-labels
1165 labelHashSet uniquePoints;
1167 forAll(curFace, pointI)
1169 bool inserted = uniquePoints.insert(curFace[pointI]);
1173 Pout<< "Face " << faceI
1174 << " contains identical vertex labels: "
1180 ConnectivityWarning()
1181 << nl << "Face-point connectivity is inconsistent."
1186 // Count faces per point
1187 forAll(curFace, pointI)
1189 nPointFaces[curFace[pointI]]++;
1192 // Ensure that cells on either side of this face
1193 // share just one face.
1194 if (neighbour_[faceI] > -1)
1196 const cell& ownCell = cells_[owner_[faceI]];
1197 const cell& neiCell = cells_[neighbour_[faceI]];
1203 if (findIndex(neiCell, ownCell[fi]) > -1)
1211 Pout<< "Cells for face: " << faceI << "::" << curFace << nl
1212 << '\t' << owner_[faceI] << ":: " << ownCell << nl
1213 << '\t' << neighbour_[faceI] << " :: " << neiCell << nl
1214 << " share multiple faces. "
1219 ConnectivityWarning()
1220 << nl << "Cell-Face connectivity is inconsistent."
1226 label allFaces = faces_.size();
1227 labelList nCellsPerFace(allFaces, 0);
1229 forAll(cells_, cellI)
1231 const cell& curCell = cells_[cellI];
1233 if (curCell.empty())
1238 if (min(curCell) < 0 || max(curCell) > (faces_.size()-1))
1240 Pout<< "Cell " << cellI
1241 << " contains face labels out of range: " << curCell
1242 << " Max face index = " << (faces_.size()-1) << endl;
1246 ConnectivityWarning()
1247 << nl << "Cell-Face connectivity is inconsistent."
1251 // Check for unique face-labels
1252 labelHashSet uniqueFaces;
1254 forAll(curCell, faceI)
1256 bool inserted = uniqueFaces.insert(curCell[faceI]);
1260 Pout<< "Cell " << cellI
1261 << " contains identical face labels: "
1267 ConnectivityWarning()
1268 << nl << "Cell-Face connectivity is inconsistent."
1272 // Count cells per face
1273 nCellsPerFace[curCell[faceI]]++;
1277 Pout<< "Done." << endl;
1279 Pout<< "Checking face-cell connectivity...";
1281 forAll(nCellsPerFace, faceI)
1283 // This might be a deleted face
1284 if (faceI < nOldFaces_)
1286 if (reverseFaceMap_[faceI] == -1)
1292 if (deletedFaces_.found(faceI))
1298 label uPatch = whichPatch(faceI);
1300 if (nCellsPerFace[faceI] == 0)
1302 // Looks like this is really an unused face.
1303 Pout<< "Face " << faceI << " :: " << faces_[faceI]
1304 << " is unused. Patch: "
1305 << (uPatch > -1 ? boundaryMesh()[uPatch].name() : "Internal")
1310 ConnectivityWarning()
1311 << nl << "Cell-Face connectivity is inconsistent."
1315 if (nCellsPerFace[faceI] != 2 && uPatch == -1)
1317 // Internal face is not shared by exactly two cells
1318 Pout<< "Internal Face " << faceI
1319 << " :: " << faces_[faceI]
1320 << " Owner: " << owner_[faceI]
1321 << " Neighbour: " << neighbour_[faceI]
1322 << " is multiply connected." << nl
1323 << " nCellsPerFace: " << nCellsPerFace[faceI] << nl
1324 << " Patch: Internal" << nl
1327 // Loop through cells and find another instance
1328 forAll(cells_, cellI)
1330 if (findIndex(cells_[cellI], faceI) > -1)
1332 Pout<< " Cell: " << cellI
1333 << " :: " << cells_[cellI]
1340 ConnectivityWarning()
1341 << nl << "Cell-Face connectivity is inconsistent."
1345 if (nCellsPerFace[faceI] != 1 && uPatch > -1)
1347 // Boundary face is not shared by exactly one cell
1348 Pout<< "Boundary Face " << faceI
1349 << " :: " << faces_[faceI]
1350 << " Owner: " << owner_[faceI]
1351 << " Neighbour: " << neighbour_[faceI]
1352 << " is multiply connected." << nl
1353 << " nCellsPerFace: " << nCellsPerFace[faceI] << nl
1354 << " Patch: " << boundaryMesh()[uPatch].name() << nl
1357 // Loop through cells and find another instance
1358 forAll(cells_, cellI)
1360 if (findIndex(cells_[cellI], faceI) > -1)
1362 Pout<< " Cell: " << cellI
1363 << " :: " << cells_[cellI]
1370 ConnectivityWarning()
1371 << nl << "Cell-Face connectivity is inconsistent."
1376 Pout<< "Done." << endl;
1378 Pout<< "Checking for unused points...";
1380 forAll(nPointFaces, pointI)
1382 if (nPointFaces[pointI] == 0)
1384 // This might be a deleted point.
1385 if (pointI < nOldPoints_)
1387 if (reversePointMap_[pointI] == -1)
1394 if (deletedPoints_.found(pointI))
1400 // Looks like this is really an unused point.
1401 Pout<< nl << nl << "Point " << pointI
1402 << " is unused. " << endl;
1406 ConnectivityWarning()
1407 << nl << "Point-Face connectivity is inconsistent."
1412 Pout<< "Done." << endl;
1414 Pout<< "Checking edge-face connectivity...";
1416 label allEdges = edges_.size();
1417 labelList nEdgeFaces(allEdges, 0);
1419 forAll(faceEdges_, faceI)
1421 const labelList& faceEdges = faceEdges_[faceI];
1423 if (faceEdges.empty())
1428 // Check consistency of face-edge-points as well
1429 edgeList eList = faces_[faceI].edges();
1431 forAll(faceEdges,edgeI)
1433 nEdgeFaces[faceEdges[edgeI]]++;
1435 // Check if this edge actually belongs to this face
1437 const edge& edgeToCheck = edges_[faceEdges[edgeI]];
1439 forAll(eList, edgeII)
1441 if (edgeToCheck == eList[edgeII])
1450 Pout<< nl << nl << "Edge: " << faceEdges[edgeI]
1451 << ": " << edgeToCheck << nl
1452 << "was not found in face: " << faceI
1453 << ": " << faces_[faceI] << nl
1454 << "faceEdges: " << faceEdges
1459 ConnectivityWarning()
1460 << nl << "Edge-Face connectivity is inconsistent."
1466 label nInternalEdges = 0;
1467 dynamicLabelList bPatchIDs(10);
1468 labelList patchInfo(boundaryMesh().size(), 0);
1470 forAll(edgeFaces_, edgeI)
1472 const labelList& edgeFaces = edgeFaces_[edgeI];
1474 if (edgeFaces.empty())
1479 if (edgeFaces.size() != nEdgeFaces[edgeI])
1481 Pout<< nl << nl << "Edge: " << edgeI << ": " << edges_[edgeI]
1482 << ": edgeFaces: " << edgeFaces
1483 << nl << UIndirectList<face>(faces_, edgeFaces)
1484 << nl << " Expected nFaces: " << nEdgeFaces[edgeI]
1489 ConnectivityWarning()
1490 << nl << "Edge-Face connectivity is inconsistent."
1497 // Check if this edge belongs to faceEdges for each face
1498 forAll(edgeFaces, faceI)
1500 const labelList& faceEdges = faceEdges_[edgeFaces[faceI]];
1502 if (findIndex(faceEdges, edgeI) == -1)
1504 Pout<< nl << nl << "Edge: " << edgeI << ": " << edges_[edgeI]
1505 << ", edgeFaces: " << edgeFaces
1506 << nl << UIndirectList<face>(faces_, edgeFaces)
1507 << "was not found in faceEdges of face: "
1508 << edgeFaces[faceI] << ": " << faces_[edgeFaces[faceI]]
1509 << nl << "faceEdges: " << faceEdges
1510 << nl << UIndirectList<edge>(edges_, faceEdges)
1515 ConnectivityWarning()
1516 << nl << "Edge-Face connectivity is inconsistent."
1520 if (neighbour_[edgeFaces[faceI]] == -1)
1522 // Add to list of patch IDs
1523 bPatchIDs.append(whichPatch(edgeFaces[faceI]));
1533 // Check if this edge is actually internal.
1534 if (whichEdgePatch(edgeI) >= 0)
1536 Pout<< "Edge: " << edgeI
1537 << ": " << edges_[edgeI] << " is internal, "
1538 << " but patch is specified as: "
1539 << whichEdgePatch(edgeI)
1547 label patchID = whichEdgePatch(edgeI);
1549 // Check if this edge is actually on a boundary.
1552 Pout<< "Edge: " << edgeI
1553 << ": " << edges_[edgeI]
1554 << " is on a boundary, but patch is specified as: "
1561 patchInfo[patchID]++;
1564 bool failedManifoldCheck = false;
1568 if (Pstream::parRun())
1570 // Pinched manifolds should be allowed in parallel
1571 failedManifoldCheck = false;
1575 failedManifoldCheck = true;
1579 if (failedManifoldCheck)
1581 // Write out for post-processing
1582 forAll(bPatchIDs, faceI)
1584 if (bPatchIDs[faceI] == -1)
1586 Pout<< " Edge: " << edgeI
1587 << " Face Patch: Internal" << nl;
1591 Pout<< " Edge: " << edgeI
1593 << boundaryMesh()[bPatchIDs[faceI]].name() << nl;
1599 writeVTK("pinched_" + Foam::name(edgeI), edgeFaces, 2);
1601 Pout<< "Edge: " << edgeI
1602 << ": " << edges_[edgeI]
1604 << " boundary faces connected to it." << nl
1605 << " Pinched manifolds are not allowed."
1613 if (nInternalEdges != nInternalEdges_)
1615 Pout<< nl << "Internal edge-count is inconsistent." << nl
1616 << " Counted internal edges: " << nInternalEdges
1617 << " Actual count: " << nInternalEdges_ << endl;
1622 forAll(patchInfo, patchI)
1624 if (patchInfo[patchI] != edgePatchSizes_[patchI])
1626 Pout<< "Patch-count is inconsistent." << nl
1627 << " Patch: " << patchI
1628 << " Counted edges: " << patchInfo[patchI]
1629 << " Actual count: " << edgePatchSizes_[patchI] << endl;
1635 // Check added edge patches to ensure that it is consistent
1636 forAllConstIter(Map<label>, addedEdgePatches_, aepIter)
1638 label key = aepIter.key();
1639 label patch = aepIter();
1642 const labelList& edgeFaces = edgeFaces_[key];
1644 // Check if any faces on boundaries
1645 forAll(edgeFaces, faceI)
1647 if (neighbour_[edgeFaces[faceI]] == -1)
1653 if ((patch < 0) && (nBF > 0))
1655 Pout<< nl << nl << "Edge: " << key
1656 << "::" << edges_[key]
1657 << ", edgeFaces: " << edgeFaces
1658 << " is internal, but contains boundary faces."
1664 if ((patch >= 0) && (nBF != 2))
1666 if (!Pstream::parRun())
1668 Pout<< nl << nl << "Edge: " << key
1669 << "::" << edges_[key]
1670 << ", edgeFaces: " << edgeFaces
1671 << " is on a boundary patch, but doesn't contain"
1672 << " two boundary faces."
1680 Pout<< "Done." << endl;
1682 // Check coupled-patch sizes
1683 forAll(patchCoupling_, patchI)
1685 if (patchCoupling_(patchI))
1687 const coupleMap& cMap = patchCoupling_[patchI].map();
1689 label mSize = patchSizes_[cMap.masterIndex()];
1690 label sSize = patchSizes_[cMap.slaveIndex()];
1694 Pout<< "Coupled patch-count is inconsistent." << nl
1695 << " Master Patch: " << cMap.masterIndex()
1696 << " Count: " << mSize << nl
1697 << " Slave Patch: " << cMap.slaveIndex()
1698 << " Count: " << sSize
1708 Pout<< "Checking point-edge connectivity...";
1710 label allPoints = points_.size();
1711 List<labelHashSet> hlPointEdges(allPoints);
1713 forAll(edges_, edgeI)
1715 if (edgeFaces_[edgeI].size())
1717 hlPointEdges[edges_[edgeI][0]].insert(edgeI);
1718 hlPointEdges[edges_[edgeI][1]].insert(edgeI);
1722 forAll(pointEdges_, pointI)
1724 const labelList& pointEdges = pointEdges_[pointI];
1726 if (pointEdges.empty())
1731 forAll(pointEdges, edgeI)
1733 if (!hlPointEdges[pointI].found(pointEdges[edgeI]))
1735 Pout<< nl << nl << "Point: " << pointI << nl
1736 << "pointEdges: " << pointEdges << nl
1737 << "hlPointEdges: " << hlPointEdges[pointI]
1742 ConnectivityWarning()
1743 << nl << "Point-Edge connectivity is inconsistent."
1748 // Do a size check as well
1751 hlPointEdges[pointI].size() != pointEdges.size() ||
1752 pointEdges.size() == 1
1755 Pout<< nl << nl << "Point: " << pointI << nl
1756 << "pointEdges: " << pointEdges << nl
1757 << "hlPointEdges: " << hlPointEdges[pointI]
1762 ConnectivityWarning()
1763 << nl << "Size inconsistency."
1764 << nl << "Point-Edge connectivity is inconsistent."
1769 Pout<< "Done." << endl;
1772 Pout<< "Checking cell-point connectivity...";
1774 // Loop through all cells and construct cell-to-node
1776 label allCells = cells_.size();
1777 labelList cellIndex(allCells);
1778 List<labelHashSet> cellToNode(allCells);
1780 forAll(cells_, cellI)
1782 const cell& thisCell = cells_[cellI];
1784 if (thisCell.empty())
1789 cellIndex[cIndex] = cellI;
1791 forAll(thisCell, faceI)
1793 const labelList& fEdges = faceEdges_[thisCell[faceI]];
1795 forAll(fEdges, edgeI)
1797 const edge& thisEdge = edges_[fEdges[edgeI]];
1799 if (!cellToNode[cIndex].found(thisEdge[0]))
1801 cellToNode[cIndex].insert(thisEdge[0]);
1804 if (!cellToNode[cIndex].found(thisEdge[1]))
1806 cellToNode[cIndex].insert(thisEdge[1]);
1815 cellIndex.setSize(cIndex);
1816 cellToNode.setSize(cIndex);
1818 // Preliminary check for size
1819 forAll(cellToNode, cellI)
1821 // Check for hexahedral cells
1824 (cellToNode[cellI].size() == 8) &&
1825 (cells_[cellIndex[cellI]].size() == 6)
1833 (cellToNode[cellI].size() != 6 && is2D()) ||
1834 (cellToNode[cellI].size() != 4 && is3D())
1837 Pout<< nl << "Warning: Cell: "
1838 << cellIndex[cellI] << " is inconsistent. "
1841 const cell& failedCell = cells_[cellIndex[cellI]];
1843 Pout<< "Cell faces: " << failedCell << endl;
1845 forAll(failedCell, faceI)
1847 Pout<< "\tFace: " << failedCell[faceI]
1848 << " :: " << faces_[failedCell[faceI]]
1851 const labelList& fEdges = faceEdges_[failedCell[faceI]];
1853 forAll(fEdges, edgeI)
1855 Pout<< "\t\tEdge: " << fEdges[edgeI]
1856 << " :: " << edges_[fEdges[edgeI]]
1865 Pout<< "Done." << endl;
1871 "void dynamicTopoFvMesh::checkConnectivity"
1872 "(const label maxErrors) const"
1874 << nFailedChecks << " failures were found in connectivity."
1875 << abort(FatalError);
1880 // Utility method to check the quality
1881 // of a triangular face after bisection.
1882 // - Returns 'true' if the bisection in NOT feasible.
1883 bool dynamicTopoFvMesh::checkBisection
1886 const label bFaceIndex,
1890 scalar bisectionQuality = GREAT, minArea = GREAT;
1892 label commonEdge = -1;
1894 // Find the common edge index
1895 meshOps::findCommonEdge
1904 const edge& checkEdge = edges_[commonEdge];
1905 const face& checkFace = faces_[bFaceIndex];
1907 // Compute old / new mid-points
1912 oldPoints_[checkEdge.start()],
1913 oldPoints_[checkEdge.end()]
1921 points_[checkEdge.start()],
1922 points_[checkEdge.end()]
1926 // Find the isolated point on the face
1927 label iPoint = -1, nPoint = -1;
1929 meshOps::findIsolatedPoint
1937 // Find the other point
1940 (nPoint == checkEdge.start()) ?
1941 checkEdge.end() : checkEdge.start()
1944 // Configure old / new triangle faces
1945 FixedList<FixedList<point, 3>, 2> tfNew, tfOld;
1947 tfNew[0][0] = mpNew;
1948 tfNew[0][1] = points_[iPoint];
1949 tfNew[0][2] = points_[nPoint];
1951 tfOld[0][0] = mpOld;
1952 tfOld[0][1] = oldPoints_[iPoint];
1953 tfOld[0][2] = oldPoints_[nPoint];
1955 tfNew[1][0] = points_[oPoint];
1956 tfNew[1][1] = points_[iPoint];
1957 tfNew[1][2] = mpNew;
1959 tfOld[1][0] = oldPoints_[oPoint];
1960 tfOld[1][1] = oldPoints_[iPoint];
1961 tfOld[1][2] = mpOld;
1963 // Assume XY plane here
1964 vector n = vector(0,0,1);
1968 // Configure triangles
1969 triPointRef tprNew(tfNew[fI][0], tfNew[fI][1], tfNew[fI][2]);
1970 triPointRef tprOld(tfOld[fI][0], tfOld[fI][1], tfOld[fI][2]);
1979 ((tprNew.centre() & n) * n)
1986 mag(tprOld.normal()) *
1991 ((tprOld.centre() & n) * n)
1996 // Update statistics
1997 minArea = Foam::min(minArea, oldArea);
1998 bisectionQuality = Foam::min(bisectionQuality, tQuality);
2001 // Final quality check
2002 if (bisectionQuality < sliverThreshold_ && !forceOp)
2007 // Negative quality is a no-no
2008 if (bisectionQuality < 0.0)
2013 // Negative old-area is also a no-no
2019 // No problems, so a bisection is feasible.
2024 // Utility method to check whether the faces in triFaces will yield
2025 // valid triangles when 'pointIndex' is moved to 'newPoint'.
2026 // - The routine performs metric-based checks.
2027 // - Returns 'true' if the collapse in NOT feasible.
2028 // - Does not reference member data, because this function
2029 // is also used on subMeshes
2030 bool dynamicTopoFvMesh::checkCollapse
2032 const dynamicTopoFvMesh& mesh,
2033 const labelList& triFaces,
2034 const FixedList<label,2>& c0BdyIndex,
2035 const FixedList<label,2>& c1BdyIndex,
2036 const FixedList<label,2>& pointIndex,
2037 const FixedList<point,2>& newPoint,
2038 const FixedList<point,2>& oldPoint,
2039 scalar& collapseQuality,
2040 const bool checkNeighbour,
2045 collapseQuality = GREAT;
2046 scalar minArea = GREAT;
2048 forAll(triFaces, indexI)
2052 (triFaces[indexI] == c0BdyIndex[0])
2053 || (triFaces[indexI] == c0BdyIndex[1])
2063 (triFaces[indexI] == c1BdyIndex[0])
2064 || (triFaces[indexI] == c1BdyIndex[1])
2071 const face& checkFace = mesh.faces_[triFaces[indexI]];
2073 // Configure a triangle face
2074 FixedList<point, 3> tFNew(vector::zero);
2075 FixedList<point, 3> tFOld(vector::zero);
2077 // Make necessary replacements
2078 forAll(checkFace, pointI)
2080 tFNew[pointI] = mesh.points_[checkFace[pointI]];
2081 tFOld[pointI] = mesh.oldPoints_[checkFace[pointI]];
2083 if (checkFace[pointI] == pointIndex[0])
2085 tFNew[pointI] = newPoint[0];
2086 tFOld[pointI] = oldPoint[0];
2089 if (checkFace[pointI] == pointIndex[1])
2091 tFNew[pointI] = newPoint[1];
2092 tFOld[pointI] = oldPoint[1];
2096 // Configure triangles
2097 triPointRef tprNew(tFNew[0], tFNew[1], tFNew[2]);
2098 triPointRef tprOld(tFOld[0], tFOld[1], tFOld[2]);
2100 // Assume XY plane here
2101 vector n = vector(0,0,1);
2103 // Compute the quality.
2104 // Assume centre-plane passes through origin
2112 ((tprNew.centre() & n) * n)
2119 mag(tprOld.normal()) *
2124 ((tprOld.centre() & n) * n)
2129 // Update statistics
2130 minArea = Foam::min(minArea, oldArea);
2131 collapseQuality = Foam::min(collapseQuality, tQuality);
2134 // Final quality check
2135 if (collapseQuality < mesh.sliverThreshold_ && !forceOp)
2139 Pout<< " * * * 2D checkCollapse * * * " << nl
2140 << " collapseQuality: " << collapseQuality
2141 << " below threshold: " << mesh.sliverThreshold_
2148 // Negative quality is a no-no
2149 if (collapseQuality < 0.0)
2153 Pout<< " * * * 2D checkCollapse * * * " << nl
2154 << " Negative collapseQuality: " << collapseQuality << nl
2155 << " Operation cannot be forced."
2162 // Negative old-area is also a no-no
2167 Pout<< " * * * 2D checkCollapse * * * " << nl
2168 << " minArea: " << minArea << nl
2169 << " Operation cannot be forced."
2176 // No problems, so a collapse is feasible.
2181 // Utility method to check whether the cell given by 'cellIndex' will yield
2182 // a valid cell when 'pointIndex' is moved to 'newPoint'.
2183 // - The routine performs metric-based checks.
2184 // - Returns 'true' if the collapse in NOT feasible, and
2185 // makes entries in cellsChecked to avoid repetitive checks.
2186 bool dynamicTopoFvMesh::checkCollapse
2188 const point& newPoint,
2189 const point& oldPoint,
2190 const label pointIndex,
2191 const label cellIndex,
2192 dynamicLabelList& cellsChecked,
2193 scalar& collapseQuality,
2197 label faceIndex = -1;
2198 scalar cQuality = 0.0, oldVolume = 0.0, newVolume = 0.0;
2199 const cell& cellToCheck = cells_[cellIndex];
2201 // Look for a face that doesn't contain 'pointIndex'
2202 forAll(cellToCheck, faceI)
2204 const face& currFace = faces_[cellToCheck[faceI]];
2206 if (currFace.which(pointIndex) < 0)
2208 faceIndex = cellToCheck[faceI];
2213 // Compute cell-volume
2214 const face& faceToCheck = faces_[faceIndex];
2216 if (owner_[faceIndex] == cellIndex)
2222 points_[faceToCheck[2]],
2223 points_[faceToCheck[1]],
2224 points_[faceToCheck[0]],
2233 points_[faceToCheck[2]],
2234 points_[faceToCheck[1]],
2235 points_[faceToCheck[0]],
2244 oldPoints_[faceToCheck[2]],
2245 oldPoints_[faceToCheck[1]],
2246 oldPoints_[faceToCheck[0]],
2257 points_[faceToCheck[0]],
2258 points_[faceToCheck[1]],
2259 points_[faceToCheck[2]],
2268 points_[faceToCheck[0]],
2269 points_[faceToCheck[1]],
2270 points_[faceToCheck[2]],
2279 oldPoints_[faceToCheck[0]],
2280 oldPoints_[faceToCheck[1]],
2281 oldPoints_[faceToCheck[2]],
2287 // Final quality check
2288 if (cQuality < sliverThreshold_ && !forceOp)
2292 Pout<< " * * * 3D checkCollapse * * * " << nl
2293 << "\nCollapsing cell: " << cellIndex
2294 << " containing points:\n"
2295 << faceToCheck[0] << "," << faceToCheck[1] << ","
2296 << faceToCheck[2] << "," << pointIndex << nl
2297 << "will yield a quality of: " << cQuality
2298 << ", when " << pointIndex
2299 << " is moved to location: " << nl
2307 // Negative quality is a no-no
2312 Pout<< " * * * 3D checkCollapse * * * " << nl
2313 << "\nCollapsing cell: " << cellIndex
2314 << " containing points:\n"
2315 << faceToCheck[0] << "," << faceToCheck[1] << ","
2316 << faceToCheck[2] << "," << pointIndex << nl
2317 << "will yield a quality of: " << cQuality
2318 << ", when " << pointIndex
2319 << " is moved to location: " << nl
2321 << "Operation cannot be forced."
2328 // Negative old-volume is also a no-no
2329 if (oldVolume < 0.0 || (mag(oldVolume) < mag(0.1 * newVolume)))
2333 Pout<< " * * * 3D checkCollapse * * * " << nl
2334 << "\nCollapsing cell: " << cellIndex
2335 << " containing points:\n"
2336 << faceToCheck[0] << "," << faceToCheck[1] << ","
2337 << faceToCheck[2] << "," << pointIndex << nl
2338 << "will yield an old-volume of: " << oldVolume
2339 << ", when " << pointIndex
2340 << " is moved to location: " << nl
2342 << "newVolume: " << newVolume << nl
2343 << "Operation cannot be forced."
2350 // No problems, so a collapse is feasible
2351 cellsChecked.append(cellIndex);
2353 // Update input quality
2354 collapseQuality = Foam::min(collapseQuality, cQuality);
2360 } // End namespace Foam
2362 // ************************************************************************* //