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 An implementation of dynamic changes to mesh-topology
33 University of Massachusetts Amherst
36 \*---------------------------------------------------------------------------*/
38 #include "dynamicTopoFvMesh.H"
39 #include "addToRunTimeSelectionTable.H"
44 #include "changeMap.H"
45 #include "clockTime.H"
46 #include "volFields.H"
47 #include "MeshObject.H"
48 #include "topoMapper.H"
49 #include "coupledInfo.H"
50 #include "mapPolyMesh.H"
51 #include "MapFvFields.H"
52 #include "SortableList.H"
53 #include "motionSolver.H"
54 #include "fvPatchFields.H"
55 #include "fvsPatchFields.H"
56 #include "lengthScaleEstimator.H"
57 #include "conservativeMapFields.H"
62 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
64 defineTypeNameAndDebug(dynamicTopoFvMesh,0);
66 addToRunTimeSelectionTable(dynamicFvMesh, dynamicTopoFvMesh, IOobject);
68 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
70 // Construct from IOobject
71 dynamicTopoFvMesh::dynamicTopoFvMesh(const IOobject& io)
75 nPatches_(boundaryMesh().size()),
76 topoChangeFlag_(false),
83 polyMesh::time().constant(),
92 dict_.subDict("dynamicTopoFvMesh").lookup("allOptionsMandatory")
94 twoDMesh_(polyMesh::nGeometricD() == 2 ? true : false),
97 dict_.subDict("dynamicTopoFvMesh").lookup("edgeRefinement")
99 loadMotionSolver_(true),
100 bandWidthReduction_(false),
101 coupledModification_(false),
106 lengthEstimator_(NULL),
107 oldPoints_(polyMesh::points()),
108 points_(polyMesh::points()),
109 faces_(polyMesh::faces()),
110 owner_(polyMesh::faceOwner()),
111 neighbour_(polyMesh::faceNeighbour()),
112 cells_(primitiveMesh::cells()),
113 oldPatchSizes_(nPatches_, 0),
114 patchSizes_(nPatches_, 0),
115 oldPatchStarts_(nPatches_, -1),
116 patchStarts_(nPatches_, -1),
117 oldEdgePatchSizes_(nPatches_, 0),
118 edgePatchSizes_(nPatches_, 0),
119 oldEdgePatchStarts_(nPatches_, -1),
120 edgePatchStarts_(nPatches_, -1),
121 oldPatchNMeshPoints_(nPatches_, -1),
122 patchNMeshPoints_(nPatches_, -1),
123 nOldPoints_(primitiveMesh::nPoints()),
124 nPoints_(primitiveMesh::nPoints()),
127 nOldFaces_(primitiveMesh::nFaces()),
128 nFaces_(primitiveMesh::nFaces()),
129 nOldCells_(primitiveMesh::nCells()),
130 nCells_(primitiveMesh::nCells()),
131 nOldInternalFaces_(primitiveMesh::nInternalFaces()),
132 nInternalFaces_(primitiveMesh::nInternalFaces()),
133 nOldInternalEdges_(0),
135 maxModifications_(-1),
137 sliverThreshold_(0.1),
141 allowTableResize_(false)
143 // Check the size of owner/neighbour
144 if (owner_.size() != neighbour_.size())
146 // Size up to number of faces
147 neighbour_.setSize(nFaces_, -1);
150 const polyBoundaryMesh& boundary = polyMesh::boundaryMesh();
152 // Initialize the multiThreading environment
153 initializeThreadingEnvironment();
155 // Read optional parameters.
156 readOptionalParameters();
158 // Initialize patch-size information
159 for (label i = 0; i < nPatches_; i++)
161 patchNMeshPoints_[i] = boundary[i].meshPoints().size();
162 oldPatchSizes_[i] = patchSizes_[i] = boundary[i].size();
163 oldPatchStarts_[i] = patchStarts_[i] = boundary[i].start();
164 oldPatchNMeshPoints_[i] = patchNMeshPoints_[i];
167 // Open the tetMetric dynamic-link library (for 3D only)
170 // Initialize edge-related connectivity structures
173 // Load the mesh-motion solver
176 // Load the field-mapper
179 // Set sizes for the reverse maps
180 reversePointMap_.setSize(nPoints_, -7);
181 reverseEdgeMap_.setSize(nEdges_, -7);
182 reverseFaceMap_.setSize(nFaces_, -7);
183 reverseCellMap_.setSize(nCells_, -7);
185 // Load the length-scale estimator,
186 // and read refinement options
187 loadLengthScaleEstimator();
191 //- Construct from components. Used for subMeshes only.
192 dynamicTopoFvMesh::dynamicTopoFvMesh
194 const dynamicTopoFvMesh& mesh,
196 const Xfer<pointField>& points,
197 const Xfer<pointField>& oldPoints,
198 const Xfer<edgeList>& edges,
199 const Xfer<faceList>& faces,
200 const Xfer<labelListList>& faceEdges,
201 const Xfer<labelList>& owner,
202 const Xfer<labelList>& neighbour,
203 const labelList& faceStarts,
204 const labelList& faceSizes,
205 const labelList& edgeStarts,
206 const labelList& edgeSizes,
207 const wordList& patchNames,
208 const dictionary& patchDict
221 nPatches_(faceStarts.size()),
222 topoChangeFlag_(false),
225 mandatory_(mesh.mandatory_),
226 twoDMesh_(mesh.twoDMesh_),
227 edgeRefinement_(mesh.edgeRefinement_),
228 loadMotionSolver_(mesh.loadMotionSolver_),
229 bandWidthReduction_(mesh.bandWidthReduction_),
230 coupledModification_(false),
235 lengthEstimator_(NULL),
236 oldPoints_(polyMesh::points()),
238 faces_(polyMesh::faces()),
239 cells_(polyMesh::cells()),
241 faceEdges_(faceEdges),
242 oldPatchSizes_(faceSizes),
243 patchSizes_(faceSizes),
244 oldPatchStarts_(faceStarts),
245 patchStarts_(faceStarts),
246 oldEdgePatchSizes_(edgeSizes),
247 edgePatchSizes_(edgeSizes),
248 oldEdgePatchStarts_(edgeStarts),
249 edgePatchStarts_(edgeStarts),
250 oldPatchNMeshPoints_(faceStarts.size(), -1),
251 patchNMeshPoints_(faceStarts.size(), -1),
252 nOldPoints_(points_.size()),
253 nPoints_(points_.size()),
254 nOldEdges_(edges_.size()),
255 nEdges_(edges_.size()),
256 nOldFaces_(faces_.size()),
257 nFaces_(faces_.size()),
258 nOldCells_(cells_.size()),
259 nCells_(cells_.size()),
260 nOldInternalFaces_(faceStarts[0]),
261 nInternalFaces_(faceStarts[0]),
262 nOldInternalEdges_(edgeStarts[0]),
263 nInternalEdges_(edgeStarts[0]),
264 maxModifications_(mesh.maxModifications_),
266 sliverThreshold_(mesh.sliverThreshold_),
268 maxTetsPerEdge_(mesh.maxTetsPerEdge_),
269 swapDeviation_(mesh.swapDeviation_),
270 allowTableResize_(mesh.allowTableResize_),
271 tetMetric_(mesh.tetMetric_)
273 // Initialize owner and neighbour
274 owner_.setSize(faces_.size(), -1);
275 neighbour_.setSize(faces_.size(), -1);
277 // Set owner and neighbour from polyMesh
278 const labelList& own = polyMesh::faceOwner();
279 const labelList& nei = polyMesh::faceNeighbour();
283 owner_[faceI] = own[faceI];
288 neighbour_[faceI] = nei[faceI];
291 // Initialize the multiThreading environment.
292 // Force to single-threaded.
293 initializeThreadingEnvironment(1);
295 const polyBoundaryMesh& boundary = polyMesh::boundaryMesh();
297 // Add a boundary patches for polyMesh.
298 List<polyPatch*> patches(faceStarts.size());
300 forAll(patches, patchI)
318 // Add patches, but don't calculate geometry, etc
319 fvMesh::addFvPatches(patches, false);
321 // Set sizes for the reverse maps
322 reversePointMap_.setSize(nPoints_, -7);
323 reverseEdgeMap_.setSize(nEdges_, -7);
324 reverseFaceMap_.setSize(nFaces_, -7);
325 reverseCellMap_.setSize(nCells_, -7);
327 // Now build edgeFaces and pointEdges information.
328 edgeFaces_ = invertManyToMany<labelList, labelList>(nEdges_, faceEdges_);
332 pointEdges_ = invertManyToMany<edge, labelList>(nPoints_, edges_);
337 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
339 dynamicTopoFvMesh::~dynamicTopoFvMesh()
343 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
345 // Insert the specified cell to the mesh.
346 label dynamicTopoFvMesh::insertCell
349 const scalar lengthScale,
353 label newCellIndex = cells_.size();
357 Pout<< "Inserting cell: "
358 << newCellIndex << ": "
362 cells_.append(newCell);
366 lengthScale_.append(lengthScale);
369 // Add to the zone if necessary
372 addedCellZones_.insert(newCellIndex, zoneID);
381 // Remove the specified cell from the mesh
382 void dynamicTopoFvMesh::removeCell
389 Pout<< "Removing cell: "
395 cells_[cIndex].clear();
399 lengthScale_[cIndex] = -1.0;
402 // Update the number of cells, and the reverse cell map
405 if (cIndex < nOldCells_)
407 reverseCellMap_[cIndex] = -1;
411 // Store this information for the reOrdering stage
412 deletedCells_.insert(cIndex);
415 // Check if this cell was added to a zone
416 Map<label>::iterator it = addedCellZones_.find(cIndex);
418 if (it != addedCellZones_.end())
420 addedCellZones_.erase(it);
423 // Check if the cell was added in the current morph, and delete
424 forAll(cellsFromPoints_, indexI)
426 if (cellsFromPoints_[indexI].index() == cIndex)
428 // Remove entry from the list
429 meshOps::removeIndex(indexI, cellsFromPoints_);
434 forAll(cellsFromEdges_, indexI)
436 if (cellsFromEdges_[indexI].index() == cIndex)
438 // Remove entry from the list
439 meshOps::removeIndex(indexI, cellsFromEdges_);
444 forAll(cellsFromFaces_, indexI)
446 if (cellsFromFaces_[indexI].index() == cIndex)
448 // Remove entry from the list
449 meshOps::removeIndex(indexI, cellsFromFaces_);
454 forAll(cellsFromCells_, indexI)
456 if (cellsFromCells_[indexI].index() == cIndex)
458 // Remove entry from the list
459 meshOps::removeIndex(indexI, cellsFromCells_);
466 // Utility method for face-insertion
467 label dynamicTopoFvMesh::insertFace
471 const label newOwner,
472 const label newNeighbour,
476 // Append the specified face to each face-related list.
477 // Reordering is performed after all pending changes
478 // (flips, bisections, contractions, etc) have been made to the mesh
479 label newFaceIndex = faces_.size();
481 faces_.append(newFace);
482 owner_.append(newOwner);
483 neighbour_.append(newNeighbour);
487 Pout<< "Inserting face: "
488 << newFaceIndex << ": "
490 << " Owner: " << newOwner
491 << " Neighbour: " << newNeighbour;
493 const polyBoundaryMesh& boundary = boundaryMesh();
499 Pout<< "Internal" << endl;
502 if (patch < boundary.size())
504 Pout<< boundary[patch].name() << endl;
508 Pout<< " New patch: " << patch << endl;
512 // Keep track of added boundary faces in a separate hash-table
513 // This information will be required at the reordering stage
514 addedFacePatches_.insert(newFaceIndex,patch);
516 if (newNeighbour == -1)
518 // Modify patch information for this boundary face
519 patchSizes_[patch]++;
521 for (label i = (patch + 1); i < nPatches_; i++)
528 // Increment the number of internal faces,
529 // and subsequent patch-starts
532 for (label i = 0; i < nPatches_; i++)
538 // Add to the zone if explicitly specified
541 addedFaceZones_.insert(newFaceIndex, zoneID);
545 // No zone was specified. Check if this
546 // face is added to a coupled patch associated
548 forAll(patchCoupling_, patchI)
550 if (patchCoupling_(patchI))
554 if (patchCoupling_[patchI].masterFaceZone() > -1)
556 addedFaceZones_.insert
559 patchCoupling_[patchI].masterFaceZone()
566 if (patchCoupling_[patchI].map().slaveIndex() == patch)
568 if (patchCoupling_[patchI].slaveFaceZone() > -1)
570 addedFaceZones_.insert
573 patchCoupling_[patchI].slaveFaceZone()
583 // Increment the total face count
590 // Remove the specified face from the mesh
591 void dynamicTopoFvMesh::removeFace
596 // Identify the patch for this face
597 label patch = whichPatch(fIndex);
601 Pout<< "Removing face: "
605 const polyBoundaryMesh& boundary = boundaryMesh();
611 Pout<< "Internal" << endl;
614 if (patch < boundary.size())
616 Pout<< boundary[patch].name() << endl;
620 Pout<< " New patch: " << patch << endl;
626 // Modify patch information for this boundary face
627 patchSizes_[patch]--;
629 for (label i = (patch + 1); i < nPatches_; i++)
636 // Decrement the internal face count, and subsequent patch-starts
639 forAll(patchStarts_, patchI)
641 patchStarts_[patchI]--;
646 faces_[fIndex].clear();
648 neighbour_[fIndex] = -1;
649 faceEdges_[fIndex].clear();
651 // Entity won't be removed from the stack for efficiency
652 // It will be discarded on access instead.
654 // Update coupled face maps, if necessary.
655 forAll(patchCoupling_, patchI)
657 if (patchCoupling_(patchI))
659 const coupleMap& cMap = patchCoupling_[patchI].map();
661 cMap.removeMaster(coupleMap::FACE, fIndex);
662 cMap.removeSlave(coupleMap::FACE, fIndex);
666 // Update the reverse face-map, but only if this is a face that existed
667 // at time [n]. Added faces which are deleted during the topology change
668 // needn't be updated.
669 if (fIndex < nOldFaces_)
671 reverseFaceMap_[fIndex] = -1;
675 // Store this information for the reOrdering stage
676 deletedFaces_.insert(fIndex);
679 // Check and remove from the list of added face patches
680 Map<label>::iterator fpit = addedFacePatches_.find(fIndex);
682 if (fpit != addedFacePatches_.end())
684 addedFacePatches_.erase(fpit);
687 // Check if this face was added to a zone
688 Map<label>::iterator fzit = addedFaceZones_.find(fIndex);
690 if (fzit != addedFaceZones_.end())
692 addedFaceZones_.erase(fzit);
695 // Check if the face was added in the current morph, and delete
696 forAll(facesFromPoints_, indexI)
698 if (facesFromPoints_[indexI].index() == fIndex)
700 // Remove entry from the list
711 forAll(facesFromEdges_, indexI)
713 if (facesFromEdges_[indexI].index() == fIndex)
715 // Remove entry from the list
726 forAll(facesFromFaces_, indexI)
728 if (facesFromFaces_[indexI].index() == fIndex)
730 // Remove entry from the list
741 // Remove from the flipFaces list, if necessary
742 labelHashSet::iterator ffit = flipFaces_.find(fIndex);
744 if (ffit != flipFaces_.end())
746 flipFaces_.erase(ffit);
749 // Decrement the total face-count
754 // Insert the specified edge to the mesh
755 label dynamicTopoFvMesh::insertEdge
759 const labelList& edgeFaces
762 label newEdgeIndex = edges_.size();
764 edges_.append(newEdge);
765 edgeFaces_.append(edgeFaces);
769 Pout<< "Inserting edge: "
770 << newEdgeIndex << ": "
773 const polyBoundaryMesh& boundary = boundaryMesh();
779 Pout<< "Internal" << endl;
782 if (patch < boundary.size())
784 Pout<< boundary[patch].name() << endl;
788 Pout<< " New patch: " << patch << endl;
792 // Keep track of added edges in a separate hash-table
793 // This information will be required at the reordering stage
794 addedEdgePatches_.insert(newEdgeIndex,patch);
798 // Modify patch information for this boundary edge
799 edgePatchSizes_[patch]++;
801 for (label i = (patch + 1); i < nPatches_; i++)
803 edgePatchStarts_[i]++;
808 // Increment the number of internal edges, and subsequent patch-starts
811 for (label i = 0; i < nPatches_; i++)
813 edgePatchStarts_[i]++;
817 // Size-up the pointEdges list
820 meshOps::sizeUpList(newEdgeIndex, pointEdges_[newEdge[0]]);
821 meshOps::sizeUpList(newEdgeIndex, pointEdges_[newEdge[1]]);
824 // Increment the total edge count
831 // Remove the specified edge from the mesh
832 void dynamicTopoFvMesh::removeEdge
839 const edge& rEdge = edges_[eIndex];
841 // Size-down the pointEdges list
842 if (pointEdges_[rEdge[0]].size())
844 meshOps::sizeDownList(eIndex, pointEdges_[rEdge[0]]);
847 if (pointEdges_[rEdge[1]].size())
849 meshOps::sizeDownList(eIndex, pointEdges_[rEdge[1]]);
852 // Entity won't be removed from the stack for efficiency
853 // It will be discarded on access instead.
855 // Update coupled edge maps, if necessary.
856 forAll(patchCoupling_, patchI)
858 if (patchCoupling_(patchI))
860 const coupleMap& cMap = patchCoupling_[patchI].map();
862 cMap.removeMaster(coupleMap::EDGE, eIndex);
863 cMap.removeSlave(coupleMap::EDGE, eIndex);
868 // Identify the patch for this edge
869 label patch = whichEdgePatch(eIndex);
873 Pout<< "Removing edge: "
877 const polyBoundaryMesh& boundary = boundaryMesh();
883 Pout<< "Internal" << endl;
886 if (patch < boundary.size())
888 Pout<< boundary[patch].name() << endl;
892 Pout<< " New patch: " << patch << endl;
896 // Invalidate edge and edgeFaces
897 edges_[eIndex] = edge(-1, -1);
898 edgeFaces_[eIndex].clear();
902 // Modify patch information for this boundary edge
903 edgePatchSizes_[patch]--;
905 for (label i = (patch + 1); i < nPatches_; i++)
907 edgePatchStarts_[i]--;
912 // Decrement the internal edge count, and subsequent patch-starts
915 forAll(edgePatchStarts_, patchI)
917 edgePatchStarts_[patchI]--;
921 // Update reverse edge-map, but only if this is an edge that existed
922 // at time [n]. Added edges which are deleted during the topology change
923 // needn't be updated.
924 if (eIndex < nOldEdges_)
926 reverseEdgeMap_[eIndex] = -1;
930 // Store this information for the reOrdering stage
931 deletedEdges_.insert(eIndex);
934 // Check and remove from the list of added edge patches
935 Map<label>::iterator it = addedEdgePatches_.find(eIndex);
937 if (it != addedEdgePatches_.end())
939 addedEdgePatches_.erase(it);
942 // Decrement the total edge-count
947 // Insert the specified point to the mesh
948 label dynamicTopoFvMesh::insertPoint
950 const point& newPoint,
951 const point& oldPoint,
952 const labelList& mapPoints,
956 // Add a new point to the end of the list
957 label newPointIndex = points_.size();
959 points_.append(newPoint);
960 oldPoints_.append(oldPoint);
964 Pout<< "Inserting new point: "
965 << newPointIndex << ": "
967 << " and old point: "
970 << mapPoints << endl;
973 // Make a pointsFromPoints entry
976 objectMap(newPointIndex, mapPoints),
980 // Add an empty entry to pointEdges as well.
981 // This entry can be sized-up appropriately at a later stage.
984 pointEdges_.append(labelList(0));
987 // Add to the zone if necessary
990 addedPointZones_.insert(newPointIndex, zoneID);
995 return newPointIndex;
999 // Remove the specified point from the mesh
1000 void dynamicTopoFvMesh::removePoint
1007 Pout<< "Removing point: " << pIndex
1008 << " :: new: " << points_[pIndex]
1009 << " :: old: " << oldPoints_[pIndex]
1014 // (or just make sure that it's never used anywhere else)
1015 // points_[pIndex] = point();
1016 // oldPoints_[pIndex] = point();
1018 // Remove pointEdges as well
1021 pointEdges_[pIndex].clear();
1024 // Update the reverse point map
1025 if (pIndex < nOldPoints_)
1027 reversePointMap_[pIndex] = -1;
1031 deletedPoints_.insert(pIndex);
1034 // Check if this point was added to a zone
1035 Map<label>::iterator pzit = addedPointZones_.find(pIndex);
1037 if (pzit != addedPointZones_.end())
1039 addedPointZones_.erase(pzit);
1042 // Update coupled point maps, if necessary.
1043 forAll(patchCoupling_, patchI)
1045 if (patchCoupling_(patchI))
1047 // Obtain references
1048 const coupleMap & cMap = patchCoupling_[patchI].map();
1050 Map<label>& pointMap = cMap.entityMap(coupleMap::POINT);
1051 Map<label>& rPointMap = cMap.reverseEntityMap(coupleMap::POINT);
1053 Map<label>::iterator pmit = pointMap.find(pIndex);
1055 if (pmit != pointMap.end())
1057 // Erase the reverse map first
1058 rPointMap.erase(pmit());
1061 pointMap.erase(pmit);
1066 // Check if the point was added in the current morph, and delete
1067 forAll(pointsFromPoints_, indexI)
1069 if (pointsFromPoints_[indexI].index() == pIndex)
1071 // Remove entry from the list
1072 meshOps::removeIndex(indexI, pointsFromPoints_);
1077 // Decrement the total point-count
1082 // Utility method to build vertexHull for an edge [3D].
1083 // Assumes that edgeFaces information is consistent.
1084 void dynamicTopoFvMesh::buildVertexHull
1087 labelList& vertexHull,
1088 const label checkIndex
1092 label faceIndex = -1, cellIndex = -1;
1093 label otherPoint = -1, nextPoint = -1;
1096 // Obtain references
1097 const edge& edgeToCheck = edges_[eIndex];
1098 const labelList& eFaces = edgeFaces_[eIndex];
1100 // Re-size the list first
1102 vertexHull.setSize(eFaces.size(), -1);
1104 if (whichEdgePatch(eIndex) == -1)
1107 // Pick the first face and start with that
1108 faceIndex = eFaces[0];
1112 // Need to find a properly oriented start-face
1113 forAll(eFaces, faceI)
1115 if (whichPatch(eFaces[faceI]) > -1)
1117 meshOps::findIsolatedPoint
1119 faces_[eFaces[faceI]],
1125 if (nextPoint == edgeToCheck[checkIndex])
1127 faceIndex = eFaces[faceI];
1134 if (faceIndex == -1)
1136 // Define failure-mode
1141 // Shuffle vertices to appear in CCW order
1142 forAll(vertexHull, indexI)
1144 meshOps::findIsolatedPoint
1152 // Add the isolated point
1153 vertexHull[indexI] = otherPoint;
1155 // Figure out how this edge is oriented.
1156 if (nextPoint == edgeToCheck[checkIndex])
1158 // Counter-clockwise. Pick the owner.
1159 cellIndex = owner_[faceIndex];
1162 if (whichPatch(faceIndex) == -1)
1164 // Clockwise. Pick the neighbour.
1165 cellIndex = neighbour_[faceIndex];
1168 if (indexI != (vertexHull.size() - 1))
1170 // This could be a pinched manifold edge
1171 // (situation with more than two boundary faces)
1172 // Pick another (properly oriented) boundary face.
1175 forAll(eFaces, faceI)
1177 if (whichPatch(eFaces[faceI]) > -1)
1179 meshOps::findIsolatedPoint
1181 faces_[eFaces[faceI]],
1189 (nextPoint == edgeToCheck[checkIndex]) &&
1190 (findIndex(vertexHull, otherPoint) == -1)
1193 faceIndex = eFaces[faceI];
1199 if (faceIndex == -1)
1211 // Looks like we've hit the last boundary face. Break out.
1215 const cell& cellToCheck = cells_[cellIndex];
1219 // Assuming tet-cells,
1220 // Loop through edgeFaces and get the next face
1221 forAll(eFaces, faceI)
1225 eFaces[faceI] != faceIndex
1226 && eFaces[faceI] == cellToCheck[0]
1229 faceIndex = cellToCheck[0];
1230 found = true; break;
1235 eFaces[faceI] != faceIndex
1236 && eFaces[faceI] == cellToCheck[1]
1239 faceIndex = cellToCheck[1];
1240 found = true; break;
1245 eFaces[faceI] != faceIndex
1246 && eFaces[faceI] == cellToCheck[2]
1249 faceIndex = cellToCheck[2];
1250 found = true; break;
1255 eFaces[faceI] != faceIndex
1256 && eFaces[faceI] == cellToCheck[3]
1259 faceIndex = cellToCheck[3];
1260 found = true; break;
1272 if (debug > 2 && !failMode)
1274 // Check for invalid indices
1275 if (findIndex(vertexHull, -1) > -1)
1280 // Check for duplicate labels
1283 labelHashSet uniquePoints;
1285 forAll(vertexHull, pointI)
1287 bool inserted = uniquePoints.insert(vertexHull[pointI]);
1291 Pout<< " vertexHull for edge: "
1292 << eIndex << "::" << edgeToCheck
1293 << " contains identical vertex labels: "
1294 << vertexHull << endl;
1304 // Prepare edgeCells
1305 DynamicList<label> eCells(10);
1307 forAll(eFaces, faceI)
1309 label own = owner_[eFaces[faceI]];
1310 label nei = neighbour_[eFaces[faceI]];
1312 if (findIndex(eCells, own) == -1)
1319 if (findIndex(eCells, nei) == -1)
1326 // Write out for post-processing
1327 label eI = eIndex, epI = whichEdgePatch(eIndex);
1328 word epName(epI < 0 ? "Internal" : boundaryMesh()[epI].name());
1330 writeVTK("vRingEdge_" + Foam::name(eI), eIndex, 1, false, true);
1331 writeVTK("vRingEdgeFaces_" + Foam::name(eI), eFaces, 2, false, true);
1332 writeVTK("vRingEdgeCells_" + Foam::name(eI), eCells, 3, false, true);
1334 Pout<< "edgeFaces: " << endl;
1336 forAll(eFaces, faceI)
1338 label fpI = whichPatch(eFaces[faceI]);
1339 word fpName(fpI < 0 ? "Internal" : boundaryMesh()[fpI].name());
1341 Pout<< " Face: " << eFaces[faceI]
1342 << ":: " << faces_[eFaces[faceI]]
1343 << " Owner: " << owner_[eFaces[faceI]]
1344 << " Neighbour: " << neighbour_[eFaces[faceI]]
1345 << " Patch: " << fpName
1352 "void dynamicTopoFvMesh::buildVertexHull\n"
1354 " const label eIndex,\n"
1355 " labelList& vertexHull,\n"
1356 " const label checkIndex\n"
1359 << " Failed to determine a vertex ring. " << nl
1360 << " Failure mode: " << failMode << nl
1361 << " Edge: " << eIndex << ":: " << edgeToCheck << nl
1362 << " edgeFaces: " << eFaces << nl
1363 << " Patch: " << epName << nl
1364 << " Current vertexHull: " << vertexHull
1365 << abort(FatalError);
1370 void dynamicTopoFvMesh::handleMeshSlicing()
1372 if (slicePairs_.empty())
1377 if (lengthEstimator().holdOff())
1380 slicePairs_.clear();
1381 lengthEstimator().clearBoxes();
1383 // Hold-off mesh slicing for a few time-steps.
1384 lengthEstimator().decrementHoldOff();
1389 Info<< "Slicing Mesh...";
1391 // Loop through candidates and weed-out invalid points
1392 forAll(slicePairs_, pairI)
1394 const labelPair& pairToCheck = slicePairs_[pairI];
1396 bool available = true;
1400 forAll(pairToCheck, indexI)
1402 if (deletedFaces_.found(pairToCheck[indexI]))
1408 if (pairToCheck[indexI] < nOldFaces_)
1410 if (reverseFaceMap_[pairToCheck[indexI]] == -1)
1420 forAll(pairToCheck, indexI)
1422 if (deletedPoints_.found(pairToCheck[indexI]))
1428 if (pairToCheck[indexI] < nOldPoints_)
1430 if (reversePointMap_[pairToCheck[indexI]] == -1)
1441 // Slice the mesh at this point.
1442 sliceMesh(pairToCheck);
1446 Info<< "Done." << endl;
1449 slicePairs_.clear();
1450 lengthEstimator().clearBoxes();
1452 // Set the sliceHoldOff value
1453 lengthEstimator().setHoldOff(50);
1457 // Handle layer addition / removal events
1458 void dynamicTopoFvMesh::handleLayerAdditionRemoval()
1460 const dictionary& meshSubDict = dict_.subDict("dynamicTopoFvMesh");
1462 if (meshSubDict.found("layering") || mandatory_)
1464 // Bail out if no entries are present
1465 if (meshSubDict.subDict("layering").empty())
1472 // No dictionary present.
1476 // Fetch layering patches
1477 const polyBoundaryMesh& boundary = boundaryMesh();
1478 const dictionary& layeringDict = meshSubDict.subDict("layering");
1480 wordList layeringPatches = layeringDict.toc();
1482 forAll(layeringPatches, wordI)
1484 word pName = layeringPatches[wordI];
1485 label patchID = boundary.findPatchID(pName);
1487 // If this patch has no faces, move on
1488 if (boundary[patchID].empty())
1493 // Use first face to determine layer thickness
1494 scalar magSf = mag(boundary[patchID].faceAreas()[0]);
1495 scalar Vc = cellVolumes()[boundary[patchID].faceCells()[0]];
1498 scalar thickness = (Vc / (magSf + VSMALL));
1500 // Fetch min / max thickness
1501 scalar minThickness =
1503 readScalar(layeringDict.subDict(pName).lookup("minThickness"))
1506 scalar maxThickness =
1508 readScalar(layeringDict.subDict(pName).lookup("maxThickness"))
1513 Pout<< " Patch: " << pName << nl
1514 << " Face area: " << magSf << nl
1515 << " Cell volume: " << Vc << nl
1516 << " Layer thickness: " << thickness << nl
1517 << " Min thickness: " << minThickness << nl
1518 << " Max thickness: " << maxThickness << nl
1522 if (thickness > maxThickness)
1524 // Add cell layer above patch
1525 addCellLayer(patchID);
1528 if (thickness < minThickness)
1530 // Remove cell layer above patch
1531 removeCellLayer(patchID);
1537 // Test an edge / face for proximity with other faces on proximity patches
1538 // and return the scalar distance to an oppositely-oriented face.
1539 scalar dynamicTopoFvMesh::testProximity
1542 label& proximityFace
1545 scalar proxDistance = GREAT, testStep = 0.0;
1546 vector gCentre = vector::zero, gNormal = vector::zero;
1550 // Obtain the face-normal.
1551 gNormal = faces_[index].normal(points_);
1553 // Obtain the face centre.
1554 gCentre = faces_[index].centre(points_);
1557 const edge& edgeToCheck = edges_[getTriBoundaryEdge(index)];
1559 // Calculate a test step-size
1564 points_[edgeToCheck.start()],
1565 points_[edgeToCheck.end()]
1571 const edge& edgeToCheck = edges_[index];
1572 const labelList& eFaces = edgeFaces_[index];
1576 points_[edgeToCheck.start()],
1577 points_[edgeToCheck.end()]
1580 // Obtain the edge centre.
1581 gCentre = lpr.centre();
1583 // Obtain the edge-normal
1584 forAll(eFaces, faceI)
1586 if (neighbour_[eFaces[faceI]] == -1)
1588 // Obtain the normal.
1589 gNormal += faces_[eFaces[faceI]].normal(points_);
1593 // Calculate a test step-size
1594 testStep = lpr.mag();
1598 gNormal /= (mag(gNormal) + VSMALL);
1600 // Test for proximity, and mark slice-pairs
1601 // is the distance is below threshold.
1604 lengthEstimator().testProximity
1614 labelPair proxPoints(-1, -1);
1615 bool foundPoint = false;
1619 proxPoints.first() = index;
1620 proxPoints.second() = proximityFace;
1624 (faces_[index].size() == 4) &&
1625 (polyMesh::faces()[proximityFace].size() == 4)
1633 const edge& thisEdge = edges_[index];
1634 const face& proxFace = polyMesh::faces()[proximityFace];
1636 // Check if any points on this face are still around.
1637 // If yes, mark one of them as the end point
1638 // for Dijkstra's algorithm. The start point will be a point
1640 proxPoints.first() = thisEdge[0];
1642 forAll(proxFace, pointI)
1644 if (reversePointMap_[proxFace[pointI]] != -1)
1646 proxPoints.second() = proxFace[pointI];
1655 // Lock the edge mutex
1656 entityMutex_[1].lock();
1658 label newSize = slicePairs_.size() + 1;
1660 // Const-cast slicePairs for resize
1661 List<labelPair>& sP = const_cast<List<labelPair>&>(slicePairs_);
1663 // Add this entry as a candidate for mesh slicing.
1664 sP.setSize(newSize, proxPoints);
1666 // Unlock the edge mutex
1667 entityMutex_[1].unlock();
1671 return proxDistance;
1675 // Calculate the edge length-scale for the mesh
1676 void dynamicTopoFvMesh::calculateLengthScale(bool dump)
1678 if (!edgeRefinement_)
1683 Switch dumpLengthScale(false);
1685 const dictionary& meshDict = dict_.subDict("dynamicTopoFvMesh");
1687 if (meshDict.found("dumpLengthScale") || mandatory_)
1689 dumpLengthScale = readBool(meshDict.lookup("dumpLengthScale"));
1692 autoPtr<volScalarField> lsfPtr(NULL);
1694 if (dumpLengthScale && time().outputTime() && dump)
1710 dimensionedScalar("scalar", dimLength, 0)
1715 // Bail-out if a dumping was not requested in dictionary.
1716 if (dump && !dumpLengthScale)
1721 // Size the field and calculate length-scale
1722 lengthScale_.setSize(nCells_, 0.0);
1724 lengthEstimator().calculateLengthScale(lengthScale_);
1726 // Check if length-scale is to be dumped to disk.
1727 if (dumpLengthScale && time().outputTime() && dump)
1729 // Obtain length-scale values from the mesh
1730 lsfPtr->internalField() = lengthScale_;
1737 // Read optional dictionary parameters
1738 void dynamicTopoFvMesh::readOptionalParameters(bool reRead)
1741 dict_.readIfModified();
1743 const dictionary& meshSubDict = dict_.subDict("dynamicTopoFvMesh");
1745 // Enable/disable run-time debug level
1746 if (meshSubDict.found("debug") || mandatory_)
1748 // Look for a processor-specific debug flag
1749 if (Pstream::parRun() && meshSubDict.found("parDebugProcs"))
1751 labelList procs(meshSubDict.lookup("parDebugProcs"));
1753 forAll(procs, procI)
1755 if (Pstream::myProcNo() == procs[procI])
1757 debug = readLabel(meshSubDict.lookup("debug"));
1763 debug = readLabel(meshSubDict.lookup("debug"));
1771 // Set debug option for underlying classes as well.
1774 fvMesh::debug = true;
1775 polyMesh::debug = true;
1778 // Re-read edge-refinement options, if necessary
1779 if (edgeRefinement_ && reRead)
1781 lengthEstimator().readRefinementOptions
1783 meshSubDict.subDict("refinementOptions"),
1789 // Read re-mesh interval
1790 if (meshSubDict.found("interval") || mandatory_)
1792 interval_ = readLabel(meshSubDict.lookup("interval"));
1799 // Check if an external mesh-motion solver is used
1800 if (meshSubDict.found("loadMotionSolver") || mandatory_)
1802 loadMotionSolver_.readIfPresent("loadMotionSolver", meshSubDict);
1805 // Update bandwidth reduction switch
1806 if (meshSubDict.found("bandwidthReduction") || mandatory_)
1808 bandWidthReduction_.readIfPresent("bandwidthReduction", meshSubDict);
1811 // Update threshold for sliver cells
1812 if (meshSubDict.found("sliverThreshold") || mandatory_)
1814 sliverThreshold_ = readScalar(meshSubDict.lookup("sliverThreshold"));
1816 if (sliverThreshold_ > 1.0 || sliverThreshold_ < 0.0)
1818 FatalErrorIn("void dynamicTopoFvMesh::readOptionalParameters()")
1819 << " Sliver threshold out of range [0..1]"
1820 << abort(FatalError);
1824 // Update limit for max number of bisections / collapses
1825 if (meshSubDict.found("maxModifications") || mandatory_)
1827 maxModifications_ = readLabel(meshSubDict.lookup("maxModifications"));
1830 // Update limit for swap on curved surfaces
1831 if (meshSubDict.found("swapDeviation") || mandatory_)
1833 swapDeviation_ = readScalar(meshSubDict.lookup("swapDeviation"));
1835 if (swapDeviation_ > 1.0 || swapDeviation_ < 0.0)
1837 FatalErrorIn("void dynamicTopoFvMesh::readOptionalParameters()")
1838 << " Swap deviation out of range [0..1]"
1839 << abort(FatalError);
1843 // For tetrahedral meshes...
1846 // Check if swapping is to be avoided on any patches
1847 if (meshSubDict.found("noSwapPatches") || mandatory_)
1849 wordList noSwapPatches =
1851 meshSubDict.subDict("noSwapPatches").toc()
1854 noSwapPatchIDs_.setSize(noSwapPatches.size());
1858 forAll(noSwapPatches, wordI)
1860 const word& patchName = noSwapPatches[wordI];
1862 noSwapPatchIDs_[indexI++] =
1864 boundaryMesh().findPatchID(patchName)
1869 // Check if a limit has been imposed on maxTetsPerEdge
1870 if (meshSubDict.found("maxTetsPerEdge") || mandatory_)
1872 maxTetsPerEdge_ = readLabel(meshSubDict.lookup("maxTetsPerEdge"));
1876 maxTetsPerEdge_ = 7;
1879 // Check if programming tables can be resized at runtime
1880 if (meshSubDict.found("allowTableResize") || mandatory_)
1884 readBool(meshSubDict.lookup("allowTableResize"))
1889 allowTableResize_ = false;
1893 // Check for load-balancing in parallel
1894 if (reRead && (meshSubDict.found("loadBalancing") || mandatory_))
1896 // Execute balancing
1897 executeLoadBalancing(meshSubDict.subDict("loadBalancing"));
1902 // Initialize edge related connectivity lists
1903 void dynamicTopoFvMesh::initEdges()
1905 // Initialize eMesh, and copy to local lists
1906 eMeshPtr_.set(new eMesh(*this));
1908 // Obtain information
1909 nEdges_ = eMeshPtr_->nEdges();
1910 nInternalEdges_ = eMeshPtr_->nInternalEdges();
1911 edgePatchSizes_ = eMeshPtr_->boundary().patchSizes();
1912 edgePatchStarts_ = eMeshPtr_->boundary().patchStarts();
1914 // Set old edge information
1915 nOldEdges_ = nEdges_;
1916 nOldInternalEdges_ = nInternalEdges_;
1917 oldEdgePatchSizes_ = edgePatchSizes_;
1918 oldEdgePatchStarts_ = edgePatchStarts_;
1920 // Set local lists with edge connectivity information
1921 edges_ = eMeshPtr_->edges();
1922 edgeFaces_ = eMeshPtr_->edgeFaces();
1923 faceEdges_ = eMeshPtr_->faceEdges();
1927 // Invert edges to obtain pointEdges
1928 pointEdges_ = invertManyToMany<edge, labelList>(nPoints_, edges_);
1931 // Clear out unwanted eMesh connectivity
1932 eMeshPtr_->clearOut();
1934 // Read coupled patch information from dictionary.
1935 readCoupledPatches();
1939 // Load the mesh-quality metric from the library
1940 void dynamicTopoFvMesh::loadMetric()
1947 // Specify the dictionary we would be looking in...
1948 const dictionary& meshDict = dict_.subDict("dynamicTopoFvMesh");
1950 // Select an appropriate metric
1951 tetMetric_ = tetMetric::New(meshDict, meshDict.lookup("tetMetric"));
1955 // Load the mesh-motion solver
1956 void dynamicTopoFvMesh::loadMotionSolver()
1958 if (motionSolver_.valid())
1962 "void dynamicTopoFvMesh::loadMotionSolver() "
1963 ) << nl << " Motion solver already loaded. "
1964 << abort(FatalError);
1967 if (dict_.found("solver") && loadMotionSolver_)
1969 motionSolver_ = motionSolver::New(*this);
1974 // Load the field mapper
1975 void dynamicTopoFvMesh::loadFieldMapper()
1977 if (mapper_.valid())
1981 "void dynamicTopoFvMesh::loadFieldMapper() "
1982 ) << nl << " Field mapper already loaded. "
1983 << abort(FatalError);
1987 mapper_.set(new topoMapper(*this, dict_));
1992 // Load the length scale estimator
1993 void dynamicTopoFvMesh::loadLengthScaleEstimator()
1995 if (edgeRefinement_)
1997 if (lengthEstimator_.valid())
2001 "void dynamicTopoFvMesh::loadLengthScaleEstimator() "
2002 ) << nl << " Length scale estimator already loaded. "
2003 << abort(FatalError);
2007 lengthEstimator_.set
2009 new lengthScaleEstimator(*this)
2014 lengthEstimator().readRefinementOptions
2016 dict_.subDict("dynamicTopoFvMesh").subDict("refinementOptions"),
2021 // Set coupled patch options, if available
2022 if (dict_.found("coupledPatches") || mandatory_)
2024 lengthEstimator().setCoupledPatches
2026 dict_.subDict("coupledPatches")
2033 // Initialize the threading environment.
2034 // - Provides an override option to avoid reading from the dictionary.
2035 void dynamicTopoFvMesh::initializeThreadingEnvironment
2037 const label specThreads
2040 // Initialize an IOobject for the IOmultiThreader object
2044 this->time().timeName(),
2051 if (specThreads > 0)
2053 threader_.set(new IOmultiThreader(io, specThreads));
2057 if (dict_.subDict("dynamicTopoFvMesh").found("threads") || mandatory_)
2066 dict_.subDict("dynamicTopoFvMesh").lookup("threads")
2073 threader_.set(new IOmultiThreader(io, 1));
2077 // Get the number of threads and allocate threadHandlers
2078 label nThreads = threader_->getNumThreads();
2082 handlerPtr_.setSize(1);
2087 new meshHandler(*this, threader())
2090 handlerPtr_[0].setMaster();
2093 entityStack_.setSize(1);
2097 // Index '0' is master, rest are slaves
2098 handlerPtr_.setSize(nThreads + 1);
2101 entityStack_.setSize(nThreads + 1);
2103 forAll(handlerPtr_, threadI)
2108 new meshHandler(*this, threader())
2113 handlerPtr_[0].setMaster();
2117 handlerPtr_[threadI].setID(threader_->getID(threadI - 1));
2118 handlerPtr_[threadI].setSlave();
2125 // 2D Edge-swapping engine
2126 void dynamicTopoFvMesh::swap2DEdges(void *argument)
2128 // Recast the argument
2129 meshHandler *thread = static_cast<meshHandler*>(argument);
2131 if (thread->slave())
2133 thread->sendSignal(meshHandler::START);
2136 dynamicTopoFvMesh& mesh = thread->reference();
2138 // Figure out which thread this is...
2139 label tIndex = mesh.self();
2144 bool reported = false;
2145 label stackSize = mesh.stack(tIndex).size();
2146 scalar interval = mesh.reportInterval(), oIndex = 0.0, nIndex = 0.0;
2148 oIndex = ::floor(sTimer.elapsedTime() / interval);
2150 // Pick items off the stack
2151 while (!mesh.stack(tIndex).empty())
2154 if (thread->master())
2156 // Update the index, if its changed
2157 nIndex = ::floor(sTimer.elapsedTime() / interval);
2159 if ((nIndex - oIndex) > VSMALL)
2167 (100.0 * mesh.stack(tIndex).size())
2168 / (stackSize + VSMALL)
2172 Info<< " Swap Progress: " << percent << "% :"
2173 << " Total: " << mesh.status(1)
2181 // Retrieve the index for this face
2182 label fIndex = mesh.stack(tIndex).pop();
2184 // Perform a Delaunay test and check if a flip is necesary.
2185 bool failed = mesh.testDelaunay(fIndex);
2189 if (thread->master())
2192 mesh.swapQuadFace(fIndex);
2196 // Push this on to the master stack
2197 mesh.stack(0).push(fIndex);
2202 if (thread->slave())
2204 thread->sendSignal(meshHandler::STOP);
2209 Info<< " Swap Progress: 100% :"
2210 << " Total: " << mesh.status(1)
2217 // 3D Edge-swapping engine
2218 void dynamicTopoFvMesh::swap3DEdges
2223 // Recast the argument
2224 meshHandler *thread = static_cast<meshHandler*>(argument);
2226 if (thread->slave())
2228 thread->sendSignal(meshHandler::START);
2231 dynamicTopoFvMesh& mesh = thread->reference();
2233 // Figure out which thread this is...
2234 label tIndex = mesh.self();
2236 // Dynamic programming variables
2238 PtrList<scalarListList> Q;
2239 PtrList<labelListList> K, triangulations;
2241 // Hull vertices information
2244 // Allocate dynamic programming tables
2245 mesh.initTables(m, Q, K, triangulations);
2250 bool reported = false;
2251 label stackSize = mesh.stack(tIndex).size();
2252 scalar interval = mesh.reportInterval(), oIndex = 0.0, nIndex = 0.0;
2254 oIndex = ::floor(sTimer.elapsedTime() / interval);
2256 // Pick edges off the stack
2257 while (!mesh.stack(tIndex).empty())
2260 if (thread->master())
2262 // Update the index, if its changed
2263 nIndex = ::floor(sTimer.elapsedTime() / interval);
2265 if ((nIndex - oIndex) > VSMALL)
2273 (100.0 * mesh.stack(tIndex).size())
2274 / (stackSize + VSMALL)
2278 Info<< " Swap Progress: " << percent << "% :"
2279 << " Surface: " << mesh.status(2)
2280 << ", Total: " << mesh.status(1)
2288 // Retrieve an edge from the stack
2289 label eIndex = mesh.stack(tIndex).pop();
2291 // Compute the minimum quality of cells around this edge
2292 scalar minQuality = mesh.computeMinQuality(eIndex, hullV);
2294 // Check if this edge is on a bounding curve
2295 // (Override purity check for processor edges)
2296 if (mesh.checkBoundingCurve(eIndex, true))
2301 // Fill the dynamic programming tables
2302 if (mesh.fillTables(eIndex, minQuality, m, hullV, Q, K, triangulations))
2304 // Check if edge-swapping is required.
2305 if (mesh.checkQuality(eIndex, m, Q, minQuality))
2307 if (thread->master())
2309 // Remove this edge according to the swap sequence
2310 mesh.removeEdgeFlips
2322 // Push this on to the master stack
2323 mesh.stack(0).push(eIndex);
2329 if (thread->slave())
2331 thread->sendSignal(meshHandler::STOP);
2336 Info<< " Swap Progress: 100% :"
2337 << " Surface: " << mesh.status(2)
2338 << ", Total: " << mesh.status(1)
2345 // Edge refinement engine
2346 void dynamicTopoFvMesh::edgeRefinementEngine
2351 // Loop through all edges and bisect/collapse by the criterion:
2352 // Bisect when edge-length > ratioMax_*lengthScale
2353 // Collapse when edge-length < ratioMin_*lengthScale
2355 // Recast the argument
2356 meshHandler *thread = static_cast<meshHandler*>(argument);
2358 if (thread->slave())
2360 thread->sendSignal(meshHandler::START);
2363 dynamicTopoFvMesh& mesh = thread->reference();
2365 // Figure out which thread this is...
2366 label tIndex = mesh.self();
2371 bool reported = false;
2372 label stackSize = mesh.stack(tIndex).size();
2373 scalar interval = mesh.reportInterval(), oIndex = 0.0, nIndex = 0.0;
2375 oIndex = ::floor(sTimer.elapsedTime() / interval);
2377 while (!mesh.stack(tIndex).empty())
2379 // Update the index, if its changed
2381 if (thread->master())
2383 nIndex = ::floor(sTimer.elapsedTime() / interval);
2385 if ((nIndex - oIndex) > VSMALL)
2393 (100.0 * mesh.stack(tIndex).size())
2394 / (stackSize + VSMALL)
2398 Info<< " Refinement Progress: " << percent << "% :"
2399 << " Bisections: " << mesh.status(3)
2400 << ", Collapses: " << mesh.status(4)
2401 << ", Total: " << mesh.status(0)
2409 // Retrieve an entity from the stack
2410 label eIndex = mesh.stack(tIndex).pop();
2412 if (mesh.checkBisection(eIndex))
2414 if (thread->master())
2417 mesh.bisectEdge(eIndex);
2421 // Push this on to the master stack
2422 mesh.stack(0).push(eIndex);
2426 if (mesh.checkCollapse(eIndex))
2428 if (thread->master())
2430 // Collapse this edge
2431 mesh.collapseEdge(eIndex);
2435 // Push this on to the master stack
2436 mesh.stack(0).push(eIndex);
2441 if (thread->slave())
2443 thread->sendSignal(meshHandler::STOP);
2448 Info<< " Refinement Progress: 100% :"
2449 << " Bisections: " << mesh.status(3)
2450 << ", Collapses: " << mesh.status(4)
2451 << ", Total: " << mesh.status(0)
2458 // Remove 2D sliver cells from the mesh
2459 void dynamicTopoFvMesh::remove2DSlivers()
2461 // If coupled patches exist, set the flag
2462 if (patchCoupling_.size() || procIndices_.size())
2464 setCoupledModification();
2467 // Sort by sliver-quality.
2468 labelList cIndices(thresholdSlivers_.toc());
2469 SortableList<scalar> values(cIndices.size());
2471 // Fill-in values to sort by...
2472 forAll(cIndices, indexI)
2474 values[indexI] = thresholdSlivers_[cIndices[indexI]];
2477 // Explicitly sort by quality value.
2480 const labelList& indices = values.indices();
2484 if (thresholdSlivers_.size())
2486 Pout<< "Sliver list: " << endl;
2488 forAll(indices, indexI)
2490 label cIndex = cIndices[indices[indexI]];
2492 Pout<< " Cell: " << cIndex
2493 << " Quality: " << thresholdSlivers_[cIndex]
2499 writeVTK("sliverCells", cIndices, 3);
2504 forAll(indices, indexI)
2507 label cIndex = cIndices[indices[indexI]];
2509 // Ensure that this cell actually exists.
2510 if (cells_[cIndex].empty())
2515 const cell& cellToCheck = cells_[cIndex];
2517 // Find an appropriate quad-face
2520 forAll(cellToCheck, faceI)
2522 if (faces_[cellToCheck[faceI]].size() == 4)
2524 fIndex = cellToCheck[faceI];
2529 // Find the prism faces
2530 FixedList<label,2> cBdyIndex(-1), cIntIndex(-1);
2531 FixedList<face,2> cBdyFace, cIntFace;
2533 meshOps::findPrismFaces
2548 InfoIn("void dynamicTopoFvMesh::remove2DSlivers()")
2550 << " Considering cell: " << cIndex
2551 << ":: " << cells_[cIndex]
2552 << " for sliver removal."
2553 << " Using face: " << fIndex
2554 << ":: " << faces_[fIndex]
2558 label triFace = cBdyIndex[0], triEdge = -1;
2560 // Find the common-edge between quad-tri faces
2561 meshOps::findCommonEdge
2569 // Find the isolated point.
2570 label ptIndex = -1, nextPtIndex = -1;
2572 const edge& edgeToCheck = edges_[triEdge];
2574 meshOps::findIsolatedPoint
2582 // Determine the interior faces connected to each edge-point.
2583 label firstFace = -1, secondFace = -1;
2585 if (cIntFace[0].which(edgeToCheck[0]) > -1)
2587 firstFace = cIntIndex[0];
2588 secondFace = cIntIndex[1];
2592 firstFace = cIntIndex[1];
2593 secondFace = cIntIndex[0];
2600 points_[edgeToCheck[0]],
2601 points_[edgeToCheck[1]]
2605 FixedList<vector, 2> p(vector::zero), q(vector::zero);
2606 FixedList<scalar, 2> proj(0.0);
2608 // Find the projection on the edge.
2609 forAll(edgeToCheck, pointI)
2611 p[pointI] = (points_[ptIndex] - points_[edgeToCheck[pointI]]);
2612 q[pointI] = (ec - points_[edgeToCheck[pointI]]);
2614 q[pointI] /= (mag(q[pointI]) + VSMALL);
2616 proj[pointI] = (p[pointI] & q[pointI]);
2619 // Take action based on the magnitude of the projection.
2620 if (mag(proj[0]) < VSMALL)
2622 if (collapseQuadFace(firstFace).type() > 0)
2630 if (mag(proj[1]) < VSMALL)
2632 if (collapseQuadFace(secondFace).type() > 0)
2640 if (proj[0] > 0.0 && proj[1] < 0.0)
2642 changeMap map = bisectQuadFace(firstFace, changeMap());
2644 // Loop through added faces, and collapse
2645 // the appropriate one
2646 const List<objectMap>& aF = map.addedFaceList();
2652 (owner_[aF[faceI].index()] == cIndex) &&
2653 (aF[faceI].index() != firstFace)
2656 if (collapseQuadFace(aF[faceI].index()).type() > 0)
2668 if (proj[0] < 0.0 && proj[1] > 0.0)
2670 changeMap map = bisectQuadFace(secondFace, changeMap());
2672 // Loop through added faces, and collapse
2673 // the appropriate one
2674 const List<objectMap>& aF = map.addedFaceList();
2680 (owner_[aF[faceI].index()] == cIndex) &&
2681 (aF[faceI].index() != secondFace)
2684 if (collapseQuadFace(aF[faceI].index()).type() > 0)
2696 if (proj[0] > 0.0 && proj[1] > 0.0)
2698 changeMap map = bisectQuadFace(fIndex, changeMap());
2700 // Loop through added faces, and collapse
2701 // the appropriate one
2702 const List<objectMap>& aF = map.addedFaceList();
2708 (owner_[aF[faceI].index()] == cIndex) &&
2709 (aF[faceI].index() != fIndex)
2712 if (collapseQuadFace(aF[faceI].index()).type() > 0)
2725 // Clear out the list
2726 thresholdSlivers_.clear();
2728 // If coupled patches exist, reset the flag
2729 if (patchCoupling_.size() || procIndices_.size())
2731 unsetCoupledModification();
2736 // Identify the sliver type in 3D
2737 const changeMap dynamicTopoFvMesh::identifySliverType
2744 // Ensure that this cell actually exists.
2745 if (cells_[cIndex].empty())
2750 label fourthPoint = -1;
2751 scalar minDistance = GREAT;
2752 face tFace(3), testFace(3), faceToCheck(3);
2753 FixedList<edge, 2> edgeToCheck(edge(-1,-1));
2755 const cell& cellToCheck = cells_[cIndex];
2757 // Find the point-face pair with minimum perpendicular distance
2758 forAll(cellToCheck, faceI)
2760 label isolatedPoint = -1;
2761 label nextFaceI = cellToCheck.fcIndex(faceI);
2763 // Pick two faces from this cell.
2764 const face& currFace = faces_[cellToCheck[faceI]];
2765 const face& nextFace = faces_[cellToCheck[nextFaceI]];
2767 // Get the fourth point
2768 forAll(nextFace, pointI)
2772 nextFace[pointI] != currFace[0]
2773 && nextFace[pointI] != currFace[1]
2774 && nextFace[pointI] != currFace[2]
2777 isolatedPoint = nextFace[pointI];
2779 // Configure a triangular face with correct orientation.
2780 if (owner_[cellToCheck[faceI]] == cIndex)
2782 testFace[0] = currFace[2];
2783 testFace[1] = currFace[1];
2784 testFace[2] = currFace[0];
2788 testFace[0] = currFace[0];
2789 testFace[1] = currFace[1];
2790 testFace[2] = currFace[2];
2797 // Obtain the unit normal.
2798 vector testNormal = testFace.normal(points_);
2800 testNormal /= (mag(testNormal) + VSMALL);
2802 // Project the isolated point onto the face.
2803 vector p = points_[isolatedPoint] - points_[testFace[0]];
2804 vector q = p - ((p & testNormal)*testNormal);
2806 // Compute the distance
2807 scalar distance = mag(p - q);
2809 // Is it the least so far?
2810 if (distance < minDistance)
2812 // Use this point-face pair.
2813 fourthPoint = isolatedPoint;
2815 minDistance = distance;
2819 // Obtain the face-normal.
2820 vector refArea = tFace.normal(points_);
2823 vector n = refArea/mag(refArea);
2825 // Define edge-vectors.
2826 vector r1 = points_[tFace[1]] - points_[tFace[0]];
2827 vector r2 = points_[tFace[2]] - points_[tFace[1]];
2828 vector r3 = points_[tFace[0]] - points_[tFace[2]];
2830 // Project the fourth point onto the face.
2831 vector r4 = points_[fourthPoint] - points_[tFace[0]];
2833 r4 = r4 - ((r4 & n)*n);
2835 // Define the two other vectors.
2836 vector r5 = r4 - r1;
2837 vector r6 = r5 - r2;
2839 // Calculate three signed triangle areas, using tFace[0] as the origin.
2840 scalar t1 = n & (0.5 * (r1 ^ r4));
2841 scalar t2 = n & (0.5 * (r2 ^ r5));
2842 scalar t3 = n & (0.5 * (r3 ^ r6));
2844 // Determine sliver types based on are magnitudes.
2845 if (t1 > 0 && t2 > 0 && t3 > 0)
2847 // Region R0: Cap cell.
2849 map.add("apexPoint", fourthPoint);
2851 faceToCheck[0] = tFace[0];
2852 faceToCheck[1] = tFace[1];
2853 faceToCheck[2] = tFace[2];
2856 if (t1 < 0 && t2 > 0 && t3 > 0)
2858 // Region R1: Sliver cell.
2861 edgeToCheck[0][0] = tFace[2];
2862 edgeToCheck[0][1] = fourthPoint;
2863 edgeToCheck[1][0] = tFace[0];
2864 edgeToCheck[1][1] = tFace[1];
2867 if (t1 > 0 && t2 < 0 && t3 > 0)
2869 // Region R2: Sliver cell.
2872 edgeToCheck[0][0] = tFace[0];
2873 edgeToCheck[0][1] = fourthPoint;
2874 edgeToCheck[1][0] = tFace[1];
2875 edgeToCheck[1][1] = tFace[2];
2878 if (t1 > 0 && t2 > 0 && t3 < 0)
2880 // Region R3: Sliver cell.
2883 edgeToCheck[0][0] = tFace[1];
2884 edgeToCheck[0][1] = fourthPoint;
2885 edgeToCheck[1][0] = tFace[2];
2886 edgeToCheck[1][1] = tFace[0];
2889 if (t1 < 0 && t2 > 0 && t3 < 0)
2891 // Region R4: Cap cell.
2893 map.add("apexPoint", tFace[0]);
2895 faceToCheck[0] = tFace[1];
2896 faceToCheck[1] = tFace[2];
2897 faceToCheck[2] = fourthPoint;
2900 if (t1 < 0 && t2 < 0 && t3 > 0)
2902 // Region R5: Cap cell.
2904 map.add("apexPoint", tFace[1]);
2906 faceToCheck[0] = tFace[2];
2907 faceToCheck[1] = tFace[0];
2908 faceToCheck[2] = fourthPoint;
2911 if (t1 > 0 && t2 < 0 && t3 < 0)
2913 // Region R6: Cap cell.
2915 map.add("apexPoint", tFace[2]);
2917 faceToCheck[0] = tFace[0];
2918 faceToCheck[1] = tFace[1];
2919 faceToCheck[2] = fourthPoint;
2922 // See if an over-ride to wedge/spade is necessary.
2923 // Obtain a reference area magnitude.
2924 scalar refMag = 0.1*(refArea & n);
2926 if (mag(t1) < refMag)
2928 if (mag(t3) < refMag)
2930 // Wedge case: Too close to point [0]
2933 edgeToCheck[0][0] = tFace[0];
2934 edgeToCheck[0][1] = fourthPoint;
2937 if (mag(t2) < refMag)
2939 // Wedge case: Too close to point [1]
2942 edgeToCheck[0][0] = tFace[1];
2943 edgeToCheck[0][1] = fourthPoint;
2946 if ((mag(t2) > refMag) && (mag(t3) > refMag))
2948 // Spade case: Too close to edge vector r1
2950 map.add("apexPoint", fourthPoint);
2952 edgeToCheck[0][0] = tFace[0];
2953 edgeToCheck[0][1] = tFace[1];
2957 if (mag(t2) < refMag)
2959 if (mag(t3) < refMag)
2961 // Wedge case: Too close to point [2]
2964 edgeToCheck[0][0] = tFace[2];
2965 edgeToCheck[0][1] = fourthPoint;
2968 if ((mag(t1) > refMag) && (mag(t3) > refMag))
2970 // Spade case: Too close to edge vector r2
2972 map.add("apexPoint", fourthPoint);
2974 edgeToCheck[0][0] = tFace[1];
2975 edgeToCheck[0][1] = tFace[2];
2979 if (mag(t3) < refMag)
2981 if ((mag(t1) > refMag) && (mag(t2) > refMag))
2983 // Spade case: Too close to edge vector r3
2985 map.add("apexPoint", fourthPoint);
2987 edgeToCheck[0][0] = tFace[2];
2988 edgeToCheck[0][1] = tFace[0];
2992 // Determine appropriate information for sliver exudation.
2997 FixedList<bool, 2> foundEdge(false);
2999 // Search the cell-faces for first and second edges.
3000 forAll(cellToCheck, faceI)
3002 const labelList& fEdges = faceEdges_[cellToCheck[faceI]];
3004 forAll(fEdges, edgeI)
3006 const edge& thisEdge = edges_[fEdges[edgeI]];
3008 if (thisEdge == edgeToCheck[0])
3010 map.add("firstEdge", fEdges[edgeI]);
3012 foundEdge[0] = true;
3015 if (thisEdge == edgeToCheck[1])
3017 map.add("secondEdge", fEdges[edgeI]);
3019 foundEdge[1] = true;
3023 if (foundEdge[0] && foundEdge[1])
3034 // Search the cell-faces for opposing faces.
3035 forAll(cellToCheck, faceI)
3037 const face& thisFace = faces_[cellToCheck[faceI]];
3039 if (triFace::compare(triFace(thisFace), triFace(faceToCheck)))
3041 map.add("opposingFace", cellToCheck[faceI]);
3053 bool foundEdge = false;
3055 // Search the cell-faces for first edge.
3056 forAll(cellToCheck, faceI)
3058 const labelList& fEdges = faceEdges_[cellToCheck[faceI]];
3060 forAll(fEdges, edgeI)
3062 const edge& thisEdge = edges_[fEdges[edgeI]];
3064 if (thisEdge == edgeToCheck[0])
3066 map.add("firstEdge", fEdges[edgeI]);
3085 "void dynamicTopoFvMesh::identifySliverType"
3086 "(const label cIndex) const"
3088 << nl << "Could not identify sliver type for cell: "
3096 Pout<< "Cell: " << cIndex
3097 << " Identified sliver type as: "
3098 << map.type() << endl;
3101 // Return the result.
3106 // Remove sliver cells
3107 void dynamicTopoFvMesh::removeSlivers()
3109 if (!edgeRefinement_)
3114 // Check if a removeSlivers entry was found in the dictionary
3115 if (dict_.subDict("dynamicTopoFvMesh").found("removeSlivers"))
3119 dict_.subDict("dynamicTopoFvMesh").lookup("removeSlivers")
3128 // Invoke the 2D sliver removal routine
3135 // If coupled patches exist, set the flag
3136 if (patchCoupling_.size() || procIndices_.size())
3138 setCoupledModification();
3141 // Sort by sliver-quality.
3142 labelList cIndices(thresholdSlivers_.toc());
3143 SortableList<scalar> values(cIndices.size());
3145 // Fill-in values to sort by...
3146 forAll(cIndices, indexI)
3148 values[indexI] = thresholdSlivers_[cIndices[indexI]];
3151 // Explicitly sort by quality value.
3154 const labelList& indices = values.indices();
3156 if (debug && thresholdSlivers_.size())
3158 Pout<< "Sliver list: " << endl;
3160 forAll(indices, indexI)
3162 label cIndex = cIndices[indices[indexI]];
3164 Pout<< " Cell: " << cIndex
3165 << " Quality: " << thresholdSlivers_[cIndex]
3171 writeVTK("sliverCells", cIndices, 3);
3175 forAll(indices, indexI)
3177 // Fetch the cell index
3178 label cIndex = cIndices[indices[indexI]];
3180 // First check if this sliver cell is handled elsewhere.
3181 if (procIndices_.size())
3183 bool foundInSubMesh = false;
3185 forAll(procIndices_, procI)
3187 label proc = procIndices_[procI];
3189 if (proc < Pstream::myProcNo())
3191 Map<label>& rCellMap =
3193 sendMeshes_[proc].map().reverseEntityMap
3199 if (rCellMap.found(cIndex))
3201 // This cell was sent to another sub-domain.
3202 foundInSubMesh = true;
3214 // Identify the sliver type.
3215 changeMap map = identifySliverType(cIndex);
3219 InfoIn("void dynamicTopoFvMesh::removeSlivers()")
3220 << nl << "Removing Cell: " << cIndex
3221 << " of sliver type: " << map.type()
3222 << " with quality: " << thresholdSlivers_[cIndex]
3226 bool success = false;
3228 // Take action based on the type of sliver.
3233 // Sliver cell was removed by a prior operation.
3234 // Nothing needs to be done.
3241 // Determine which edges need to be bisected.
3242 label firstEdge = readLabel(map.lookup("firstEdge"));
3243 label secondEdge = readLabel(map.lookup("secondEdge"));
3245 // Force bisection on both edges.
3246 changeMap firstMap = bisectEdge(firstEdge, false, true);
3247 changeMap secondMap = bisectEdge(secondEdge, false, true);
3249 // Collapse the intermediate edge.
3250 // Since we don't know which edge it is, search
3251 // through recently added edges and compare.
3254 firstMap.addedPointList()[0].index(),
3255 secondMap.addedPointList()[0].index()
3258 bool foundCollapseEdge = false;
3260 const List<objectMap>& firstMapEdges =
3262 firstMap.addedEdgeList()
3265 const List<objectMap>& secondMapEdges =
3267 secondMap.addedEdgeList()
3270 // Loop through the first list.
3271 forAll(firstMapEdges, edgeI)
3273 const edge& thisEdge =
3275 edges_[firstMapEdges[edgeI].index()]
3278 if (thisEdge == edgeToCheck)
3280 // Collapse this edge.
3285 firstMapEdges[edgeI].index(),
3295 foundCollapseEdge = true;
3300 // Loop through the second list.
3301 if (!foundCollapseEdge)
3303 forAll(secondMapEdges, edgeI)
3305 const edge& thisEdge =
3307 edges_[secondMapEdges[edgeI].index()]
3310 if (thisEdge == edgeToCheck)
3312 // Collapse this edge.
3317 secondMapEdges[edgeI].index(),
3338 label opposingFace = readLabel(map.lookup("opposingFace"));
3340 // Force trisection of the opposing face.
3343 trisectFace(opposingFace, false, true)
3346 // Collapse the intermediate edge.
3347 // Since we don't know which edge it is, search
3348 // through recently added edges and compare.
3351 readLabel(map.lookup("apexPoint")),
3352 faceMap.addedPointList()[0].index()
3355 const List<objectMap>& faceMapEdges =
3357 faceMap.addedEdgeList()
3360 forAll(faceMapEdges, edgeI)
3362 const edge& thisEdge = edges_[faceMapEdges[edgeI].index()];
3364 if (thisEdge == edgeToCheck)
3366 // Collapse this edge.
3371 faceMapEdges[edgeI].index(),
3392 // Force bisection on the first edge.
3393 changeMap firstMap =
3397 readLabel(map.lookup("firstEdge")),
3403 // Collapse the intermediate edge.
3404 // Since we don't know which edge it is, search
3405 // through recently added edges and compare.
3408 readLabel(map.lookup("apexPoint")),
3409 firstMap.addedPointList()[0].index()
3412 const List<objectMap>& firstMapEdges =
3414 firstMap.addedEdgeList()
3417 // Loop through the first list.
3418 forAll(firstMapEdges, edgeI)
3420 const edge& thisEdge = edges_[firstMapEdges[edgeI].index()];
3422 if (thisEdge == edgeToCheck)
3424 // Collapse this edge.
3429 firstMapEdges[edgeI].index(),
3450 // Collapse the first edge.
3455 readLabel(map.lookup("firstEdge")),
3470 WarningIn("void dynamicTopoFvMesh::removeSlivers()")
3471 << nl << "Could not identify sliver type for cell: "
3477 // Increment the count for successful sliver removal
3484 // Clear out the list
3485 thresholdSlivers_.clear();
3487 // If coupled patches exist, reset the flag
3488 if (patchCoupling_.size() || procIndices_.size())
3490 unsetCoupledModification();
3495 // Given an input quad-face, determine checkEdges from mesh
3496 // - Does not refer to member data directly,
3497 // since this is also used by subMeshes
3498 void dynamicTopoFvMesh::getCheckEdges
3501 const dynamicTopoFvMesh& mesh,
3503 FixedList<edge,4>& checkEdge,
3504 FixedList<label,4>& checkEdgeIndex
3507 checkEdgeIndex[0] = mesh.getTriBoundaryEdge(fIndex);
3508 checkEdge[0] = mesh.edges_[checkEdgeIndex[0]];
3510 const labelList& fEdges = mesh.faceEdges_[fIndex];
3512 forAll(fEdges, edgeI)
3514 if (checkEdgeIndex[0] != fEdges[edgeI])
3516 const edge& thisEdge = mesh.edges_[fEdges[edgeI]];
3520 checkEdge[0].start() == thisEdge[0] ||
3521 checkEdge[0].start() == thisEdge[1]
3524 checkEdgeIndex[1] = fEdges[edgeI];
3525 checkEdge[1] = thisEdge;
3528 map.add("firstEdge", checkEdgeIndex[1]);
3533 checkEdge[0].end() == thisEdge[0] ||
3534 checkEdge[0].end() == thisEdge[1]
3537 checkEdgeIndex[2] = fEdges[edgeI];
3538 checkEdge[2] = thisEdge;
3541 map.add("secondEdge", checkEdgeIndex[2]);
3545 checkEdgeIndex[3] = fEdges[edgeI];
3546 checkEdge[3] = thisEdge;
3553 // Return length-scale at an face-location in the mesh [2D]
3554 scalar dynamicTopoFvMesh::faceLengthScale
3559 // Reset the scale first
3562 label facePatch = whichPatch(fIndex);
3564 // Determine whether the face is internal
3571 lengthScale_[owner_[fIndex]]
3572 + lengthScale_[neighbour_[fIndex]]
3578 // Fetch the fixed-length scale
3579 scale = lengthEstimator().fixedLengthScale(fIndex, facePatch);
3581 // If this is a floating face, pick the owner length-scale
3582 if (lengthEstimator().isFreePatch(facePatch))
3584 scale = lengthScale_[owner_[fIndex]];
3587 // If proximity-based refinement is requested,
3588 // test the proximity to the nearest non-neighbour.
3589 if (lengthEstimator().isProximityPatch(facePatch))
3591 label proximityFace = -1;
3593 // Perform a proximity-check.
3594 scalar distance = testProximity(fIndex, proximityFace);
3596 if (debug > 3 && self() == 0)
3600 (proximityFace > -1) &&
3601 ((distance / 5.0) < scale)
3604 Pout<< " Closest opposing face detected for face: " << nl
3606 << " :: " << faces_[fIndex]
3608 << '\t' << proximityFace
3609 << " :: " << polyMesh::faces()[proximityFace] << nl
3610 << " with distance: " << distance
3620 ((distance / 3.0) - SMALL)/lengthEstimator().ratioMax()
3625 // If this face lies on a processor patch,
3626 // fetch lengthScale info from patchSubMeshes
3627 if (processorCoupledEntity(fIndex))
3629 scale = processorLengthScale(fIndex);
3632 // Limit scales if necessary
3633 lengthEstimator().limitScale(scale);
3640 // Compute length-scale at an edge-location in the mesh [3D]
3641 scalar dynamicTopoFvMesh::edgeLengthScale
3646 // Reset the scale first
3649 const labelList& eFaces = edgeFaces_[eIndex];
3651 label edgePatch = whichEdgePatch(eIndex);
3653 // Determine whether the edge is internal
3656 forAll(eFaces, faceI)
3658 scale += lengthScale_[owner_[eFaces[faceI]]];
3659 scale += lengthScale_[neighbour_[eFaces[faceI]]];
3662 scale /= (2.0*eFaces.size());
3666 // Search for boundary faces, and average their scale
3667 forAll(eFaces, faceI)
3669 label facePatch = whichPatch(eFaces[faceI]);
3671 // Skip internal faces
3672 if (facePatch == -1)
3677 // If this is a floating face, pick the owner length-scale
3678 if (lengthEstimator().isFreePatch(facePatch))
3680 scale += lengthScale_[owner_[eFaces[faceI]]];
3684 // Fetch fixed length-scale
3687 lengthEstimator().fixedLengthScale
3698 // If proximity-based refinement is requested,
3699 // test the proximity to the nearest non-neighbour.
3700 if (lengthEstimator().isProximityPatch(edgePatch))
3702 label proximityFace = -1;
3704 // Perform a proximity-check.
3705 scalar distance = testProximity(eIndex, proximityFace);
3707 if (debug > 3 && self() == 0)
3711 (proximityFace > -1) &&
3712 ((distance / 5.0) < scale)
3715 Pout<< " Closest opposing face detected for edge: " << nl
3717 << " :: " << edges_[eIndex]
3719 << '\t' << proximityFace
3720 << " :: " << polyMesh::faces()[proximityFace] << nl
3721 << " with distance: " << distance
3731 ((distance / 3.0) - SMALL)/lengthEstimator().ratioMax()
3736 // If curvature-based refinement is requested,
3737 // test the variation in face-normal directions.
3738 if (lengthEstimator().isCurvaturePatch(edgePatch))
3740 // Obtain face-normals for both faces.
3742 FixedList<vector, 2> fNorm;
3744 forAll(eFaces, faceI)
3746 if (neighbour_[eFaces[faceI]] == -1)
3748 // Obtain the normal.
3749 fNorm[count] = faces_[eFaces[faceI]].normal(points_);
3752 fNorm[count] /= mag(fNorm[count]);
3758 scalar deviation = (fNorm[0] & fNorm[1]);
3759 scalar refDeviation = lengthEstimator().curvatureDeviation();
3761 if (mag(deviation) < refDeviation)
3764 const edge& edgeToCheck = edges_[eIndex];
3766 // Get the edge-length.
3771 points_[edgeToCheck.start()],
3772 points_[edgeToCheck.end()]
3776 if (debug > 3 && self() == 0)
3778 Pout<< "Deviation: " << deviation << nl
3779 << "curvatureDeviation: " << refDeviation
3780 << ", Edge: " << eIndex << ", Length: " << length
3781 << ", Scale: " << scale << nl
3782 << " Half-length: " << (0.5*length) << nl
3784 << (lengthEstimator().ratioMin()*scale)
3793 ((length - SMALL)/lengthEstimator().ratioMax())
3799 // If this edge lies on a processor patch,
3800 // fetch lengthScale info from patchSubMeshes
3801 if (processorCoupledEntity(eIndex))
3803 scale = processorLengthScale(eIndex);
3806 // Limit scales if necessary
3807 lengthEstimator().limitScale(scale);
3814 // MultiThreaded topology modifier
3815 void dynamicTopoFvMesh::threadedTopoModifier()
3817 // Remove sliver cells first.
3820 // Coupled entities to avoid during normal modification
3821 labelHashSet entities;
3823 // Handle coupled patches.
3824 handleCoupledPatches(entities);
3826 // Handle layer addition / removal
3827 handleLayerAdditionRemoval();
3829 // Set the thread scheduling sequence
3830 labelList topoSequence(threader_->getNumThreads());
3832 // Linear sequence from 1 to nThreads
3833 forAll(topoSequence, indexI)
3835 topoSequence[indexI] = indexI + 1;
3838 if (edgeRefinement_)
3840 // Initialize stacks
3841 initStacks(entities);
3844 if (threader_->multiThreaded())
3846 executeThreads(topoSequence, handlerPtr_, &edgeRefinementEngine);
3849 // Set the master thread to implement modifications
3850 edgeRefinementEngine(&(handlerPtr_[0]));
3852 // Handle mesh slicing events, if necessary
3853 handleMeshSlicing();
3857 Info<< nl << "Edge Bisection/Collapse complete." << endl;
3861 // Re-Initialize stacks
3862 initStacks(entities);
3865 if (threader_->multiThreaded())
3869 executeThreads(topoSequence, handlerPtr_, &swap2DEdges);
3873 executeThreads(topoSequence, handlerPtr_, &swap3DEdges);
3877 // Set the master thread to implement modifications
3880 swap2DEdges(&(handlerPtr_[0]));
3884 swap3DEdges(&(handlerPtr_[0]));
3889 Info<< nl << "Edge Swapping complete." << endl;
3892 // Synchronize coupled patches
3893 syncCoupledPatches(entities);
3897 // Reset the mesh and generate mapping information
3898 // - Return true if topology changes were made.
3899 // - Return false otherwise (motion only)
3900 bool dynamicTopoFvMesh::resetMesh()
3902 // Reduce across processors.
3903 reduce(topoChangeFlag_, orOp<bool>());
3905 if (topoChangeFlag_)
3907 // Write out statistics
3908 if (Pstream::parRun() && debug)
3910 Pout<< " Bisections :: Total: " << status(3)
3911 << ", Surface: " << status(5) << nl
3912 << " Collapses :: Total: " << status(4)
3913 << ", Surface: " << status(6) << nl
3914 << " Swaps :: Total: " << status(1)
3915 << ", Surface: " << status(2) << endl;
3919 Info<< " Bisections :: Total: " << status(3)
3920 << ", Surface: " << status(5) << nl
3921 << " Collapses :: Total: " << status(4)
3922 << ", Surface: " << status(6) << nl
3923 << " Swaps :: Total: " << status(1)
3924 << ", Surface: " << status(2) << endl;
3929 Pout<< " Slivers :: " << status(7) << endl;
3932 // Fetch reference to mapper
3933 const topoMapper& fieldMapper = mapper_();
3935 // Set information for the mapping stage
3936 // - Must be done prior to field-transfers and mesh reset
3937 fieldMapper.storeMeshInformation();
3939 // Set up field-transfers before dealing with mapping
3940 wordList fieldTypes;
3941 List<wordList> fieldNames;
3942 List<List<char> > sendBuffer, recvBuffer;
3944 // Subset fields and transfer
3953 // Set sizes for mapping
3954 faceWeights_.setSize(facesFromFaces_.size(), scalarField(0));
3955 faceCentres_.setSize(facesFromFaces_.size(), vectorField(0));
3956 cellWeights_.setSize(cellsFromCells_.size(), scalarField(0));
3957 cellCentres_.setSize(cellsFromCells_.size(), vectorField(0));
3959 // Determine if mapping is to be skipped
3960 // Optionally skip mapping for remeshing-only / pre-processing
3961 const dictionary& meshSubDict = dict_.subDict("dynamicTopoFvMesh");
3963 bool skipMapping = false;
3965 if (meshSubDict.found("skipMapping") || mandatory_)
3967 skipMapping = readBool(meshSubDict.lookup("skipMapping"));
3970 // Fetch the tolerance for mapping
3971 scalar mapTol = 1e-10;
3973 if (meshSubDict.found("mappingTol") || mandatory_)
3975 mapTol = readScalar(meshSubDict.lookup("mappingTol"));
3978 // Check if outputs are enabled on failure
3979 bool mappingOutput = false;
3981 if (meshSubDict.found("mappingOutput") || mandatory_)
3983 mappingOutput = readBool(meshSubDict.lookup("mappingOutput"));
3986 clockTime mappingTimer;
3988 // Compute mapping weights for modified entities
3989 threadedMapping(mapTol, skipMapping, mappingOutput);
3992 Info<< " Mapping time: "
3993 << mappingTimer.elapsedTime() << " s"
3996 // Synchronize field transfers prior to the reOrdering stage
4004 // Obtain references to zones, if any
4005 pointZoneMesh& pointZones = polyMesh::pointZones();
4006 faceZoneMesh& faceZones = polyMesh::faceZones();
4007 cellZoneMesh& cellZones = polyMesh::cellZones();
4009 // Allocate temporary lists for mesh-reset
4010 pointField points(nPoints_);
4011 pointField preMotionPoints(nPoints_);
4012 edgeList edges(nEdges_);
4013 faceList faces(nFaces_);
4014 labelList owner(nFaces_);
4015 labelList neighbour(nInternalFaces_);
4016 labelListList faceEdges(nFaces_);
4017 labelListList edgeFaces(nEdges_);
4018 labelList oldPatchStarts(oldPatchStarts_);
4019 labelList oldPatchNMeshPoints(oldPatchNMeshPoints_);
4020 labelListList pointZoneMap(pointZones.size());
4021 labelListList faceZonePointMap(faceZones.size());
4022 labelListList faceZoneFaceMap(faceZones.size());
4023 labelListList cellZoneMap(cellZones.size());
4025 // Obtain faceZone point maps before reordering
4026 List<Map<label> > oldFaceZonePointMaps(faceZones.size());
4028 forAll(faceZones, fzI)
4030 oldFaceZonePointMaps[fzI] = faceZones[fzI]().meshPointMap();
4033 clockTime reOrderingTimer;
4035 // Reorder the mesh and obtain current topological information
4052 Info<< " Reordering time: "
4053 << reOrderingTimer.elapsedTime() << " s"
4056 // Obtain the number of patches before mesh reset
4057 label nOldPatches = boundaryMesh().size();
4059 // Obtain the patch-point maps before resetting the mesh
4060 List<Map<label> > oldPatchPointMaps(nOldPatches);
4062 forAll(oldPatchPointMaps, patchI)
4064 oldPatchPointMaps[patchI] = boundaryMesh()[patchI].meshPointMap();
4067 // Set weighting information.
4068 // This takes over the weight data.
4069 fieldMapper.setFaceWeights
4071 xferMove(faceWeights_),
4072 xferMove(faceCentres_)
4075 fieldMapper.setCellWeights
4077 xferMove(cellWeights_),
4078 xferMove(cellCentres_)
4081 // If the number of patches have changed
4082 // at run-time, reset boundaries first
4083 if (nPatches_ != nOldPatches)
4088 // Reset the mesh, and specify a non-valid
4089 // boundary to avoid globalData construction
4090 polyMesh::resetPrimitives
4092 xferCopy(preMotionPoints),
4095 xferMove(neighbour),
4101 // Check the dictionary to determine whether
4102 // edge-connectivity is to be stored on disk.
4103 // This usually benefits restart-time for large
4104 // cases, at the expense of disk-space.
4105 bool storePrimitives = false;
4107 if (meshSubDict.found("storeEdgePrimitives") || mandatory_)
4111 readBool(meshSubDict.lookup("storeEdgePrimitives"))
4115 // Reset the edge mesh
4116 eMeshPtr_->resetPrimitives
4124 (time().outputTime() && storePrimitives)
4127 // Generate mapping for points on boundary patches
4128 labelListList patchPointMap(nPatches_);
4130 for (label i = 0; i < nPatches_; i++)
4132 // Obtain new patch mesh points after reset.
4133 const labelList& meshPointLabels = boundaryMesh()[i].meshPoints();
4135 patchNMeshPoints_[i] = meshPointLabels.size();
4137 patchPointMap[i].setSize(meshPointLabels.size(), -1);
4139 // Skip for newly introduced patches
4140 if (i >= nOldPatches)
4145 forAll(meshPointLabels, pointI)
4147 label oldIndex = pointMap_[meshPointLabels[pointI]];
4149 // Check if the point existed before...
4152 // Look for the old position on this patch.
4153 Map<label>::const_iterator oIter =
4155 oldPatchPointMaps[i].find(oldIndex)
4158 // Add an entry if the point was found
4159 if (oIter != oldPatchPointMaps[i].end())
4161 patchPointMap[i][pointI] = oIter();
4167 // Generate mapping for faceZone points
4168 forAll(faceZones, fzI)
4170 // Obtain new face zone mesh points after reset.
4171 const labelList& meshPointLabels = faceZones[fzI]().meshPoints();
4173 faceZonePointMap[fzI].setSize(meshPointLabels.size(), -1);
4175 forAll(meshPointLabels, pointI)
4177 label oldIndex = pointMap_[meshPointLabels[pointI]];
4179 // Check if the point existed before...
4182 // Look for the old position on this patch.
4183 Map<label>::const_iterator oIter =
4185 oldFaceZonePointMaps[fzI].find(oldIndex)
4188 // Add an entry if the point was found
4189 if (oIter != oldFaceZonePointMaps[fzI].end())
4191 faceZonePointMap[fzI][pointI] = oIter();
4197 // Generate new mesh mapping information
4226 oldPatchNMeshPoints,
4230 // Update the underlying mesh, and map all related fields
4233 if (mpm.hasMotionPoints())
4235 // Perform a dummy movePoints to force V0 creation
4236 movePoints(mpm.preMotionPoints());
4238 // Reset old-volumes
4243 // Correct volume fluxes on the old mesh
4244 fieldMapper.correctFluxes();
4246 // Clear mapper after use
4247 fieldMapper.clear();
4249 if (mpm.hasMotionPoints())
4251 // Now move mesh to new points and
4252 // compute correct mesh-fluxes.
4256 // Update the mesh-motion solver
4257 if (motionSolver_.valid())
4259 motionSolver_->updateMesh(mpm);
4262 // Clear unwanted member data
4263 addedFacePatches_.clear();
4264 addedEdgePatches_.clear();
4265 addedPointZones_.clear();
4266 addedFaceZones_.clear();
4267 addedCellZones_.clear();
4268 faceParents_.clear();
4269 cellParents_.clear();
4271 // Clear the deleted entity map
4272 deletedPoints_.clear();
4273 deletedEdges_.clear();
4274 deletedFaces_.clear();
4275 deletedCells_.clear();
4280 // Set new sizes for the reverse maps
4281 reversePointMap_.setSize(nPoints_, -7);
4282 reverseEdgeMap_.setSize(nEdges_, -7);
4283 reverseFaceMap_.setSize(nFaces_, -7);
4284 reverseCellMap_.setSize(nCells_, -7);
4286 // Update "old" information
4287 nOldPoints_ = nPoints_;
4288 nOldEdges_ = nEdges_;
4289 nOldFaces_ = nFaces_;
4290 nOldCells_ = nCells_;
4291 nOldInternalFaces_ = nInternalFaces_;
4292 nOldInternalEdges_ = nInternalEdges_;
4294 for (label i = 0; i < nPatches_; i++)
4296 oldPatchSizes_[i] = patchSizes_[i];
4297 oldPatchStarts_[i] = patchStarts_[i];
4298 oldEdgePatchSizes_[i] = edgePatchSizes_[i];
4299 oldEdgePatchStarts_[i] = edgePatchStarts_[i];
4300 oldPatchNMeshPoints_[i] = patchNMeshPoints_[i];
4303 // Clear parallel structures
4304 if (Pstream::parRun())
4306 procIndices_.clear();
4307 sendMeshes_.clear();
4308 recvMeshes_.clear();
4311 bool checkCplBoundaries = false;
4313 if (meshSubDict.found("checkCoupledBoundaries") || mandatory_)
4315 checkCplBoundaries =
4317 readBool(meshSubDict.lookup("checkCoupledBoundaries"))
4321 if (checkCplBoundaries)
4323 bool failed = checkCoupledBoundaries();
4327 FatalErrorIn("bool dynamicTopoFvMesh::resetMesh()")
4328 << " Coupled boundary check failed on processor: "
4329 << Pstream::myProcNo()
4330 << abort(FatalError);
4339 // No topology changes were made.
4340 // Only execute mesh-motion.
4341 if (motionSolver_.valid())
4343 movePoints(points_);
4347 // Obtain mesh stats after topo-changes
4350 // Dump length-scale to disk, if requested.
4351 calculateLengthScale(true);
4353 // Reset and return flag
4354 if (topoChangeFlag_)
4356 topoChangeFlag_ = false;
4360 // No changes were made.
4365 // Update mesh corresponding to the given map
4366 void dynamicTopoFvMesh::updateMesh(const mapPolyMesh& mpm)
4368 if (coupledModification_)
4370 // This bit gets called only during the load-balancing
4371 // stage, since the fvMesh::updateMesh is a bit different
4372 fvMesh::updateMesh(mpm);
4376 // Delete oldPoints in polyMesh
4377 polyMesh::resetMotion();
4379 // Clear-out fvMesh geometry and addressing
4383 polyMesh::updateMesh(mpm);
4390 // Map all fields in time using a customized mapper
4391 void dynamicTopoFvMesh::mapFields(const mapPolyMesh& mpm) const
4393 if (coupledModification_)
4395 // This bit gets called only during the load-balancing
4396 // stage, since the fvMesh::mapFields is a bit different
4397 fvMesh::mapFields(mpm);
4403 Info<< "void dynamicTopoFvMesh::mapFields(const mapPolyMesh&) const: "
4404 << "Mapping fv fields."
4408 const topoMapper& fieldMapper = mapper_();
4410 // Set the mapPolyMesh object in the mapper
4411 fieldMapper.setMapper(mpm);
4413 // Conservatively map scalar/vector volFields
4414 conservativeMapVolFields<scalar>(fieldMapper);
4415 conservativeMapVolFields<vector>(fieldMapper);
4417 // Map all the volFields in the objectRegistry
4418 MapGeometricFields<sphericalTensor,fvPatchField,topoMapper,volMesh>
4420 MapGeometricFields<symmTensor,fvPatchField,topoMapper,volMesh>
4422 MapGeometricFields<tensor,fvPatchField,topoMapper,volMesh>
4425 // Conservatively map scalar/vector surfaceFields
4426 conservativeMapSurfaceFields<scalar>(fieldMapper);
4427 conservativeMapSurfaceFields<vector>(fieldMapper);
4429 // Map all the surfaceFields in the objectRegistry
4430 MapGeometricFields<sphericalTensor,fvsPatchField,topoMapper,surfaceMesh>
4432 MapGeometricFields<symmTensor,fvsPatchField,topoMapper,surfaceMesh>
4434 MapGeometricFields<tensor,fvsPatchField,topoMapper,surfaceMesh>
4439 // Update the mesh for motion / topology changes.
4440 // - Return true if topology changes have occurred
4441 bool dynamicTopoFvMesh::update()
4443 // Re-read options, in case they have been modified at run-time
4444 readOptionalParameters(true);
4446 // Set old point positions
4447 oldPoints_ = polyMesh::points();
4449 // Invoke mesh-motion solver and store new points
4450 if (motionSolver_.valid())
4452 points_ = motionSolver_->newPoints()();
4456 // Set point positions from mesh
4457 points_ = polyMesh::points();
4460 // Obtain mesh stats before topo-changes
4461 bool noSlivers = meshQuality(true);
4463 // Return if the interval is invalid,
4464 // not at re-mesh interval, or slivers are absent.
4465 // Handy while using only mesh-motion.
4466 if (interval_ < 0 || ((time().timeIndex() % interval_ != 0) && noSlivers))
4471 // Calculate the edge length-scale for the mesh
4472 calculateLengthScale();
4474 // Track mesh topology modification time
4475 clockTime topoTimer;
4477 // Invoke the threaded topoModifier
4478 threadedTopoModifier();
4480 Info<< " Topo modifier time: "
4481 << topoTimer.elapsedTime() << " s"
4484 // Apply all topology changes (if any) and reset mesh.
4489 // * * * * * * * * * * * * * * * Member Operators * * * * * * * * * * * * * //
4491 void dynamicTopoFvMesh::operator=(const dynamicTopoFvMesh& rhs)
4493 // Check for assignment to self
4498 "dynamicTopoFvMesh::operator=(const dynamicTopoFvMesh&)"
4500 << "Attempted assignment to self"
4501 << abort(FatalError);
4506 } // End namespace Foam
4508 // ************************************************************************* //