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 Various utility functions that perform mesh-related operations.
32 University of Massachusetts Amherst
35 \*---------------------------------------------------------------------------*/
37 #include "objectRegistry.H"
46 #include "triPointRef.H"
48 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
56 // Utility method to build a hull of cells
57 // connected to the edge (for 2D simplical meshes)
58 inline void constructPrismHull
61 const UList<face>& faces,
62 const UList<cell>& cells,
63 const UList<label>& owner,
64 const UList<label>& neighbour,
65 const UList<labelList>& edgeFaces,
66 labelList& hullTriFaces,
70 labelHashSet cellSet, triFaceSet;
73 const labelList& eFaces = edgeFaces[eIndex];
75 // Loop through edgeFaces and add cells
78 label c0 = owner[eFaces[faceI]];
79 label c1 = neighbour[eFaces[faceI]];
81 if (!cellSet.found(c0))
86 // Find associated triFaces and add them too
87 const cell& cC = cells[c0];
91 const face& cF = faces[cC[faceJ]];
93 if ((cF.size() == 3) && !(triFaceSet.found(cC[faceJ])))
95 triFaceSet.insert(cC[faceJ]);
100 if (!cellSet.found(c1) && (c1 != -1))
105 // Find associated triFaces and add them too
106 const cell& cC = cells[c1];
110 const face& cF = faces[cC[faceJ]];
112 if ((cF.size() == 3) && !(triFaceSet.found(cC[faceJ])))
114 triFaceSet.insert(cC[faceJ]);
120 // Obtain lists from hashSets
121 hullCells = cellSet.toc();
122 hullTriFaces = triFaceSet.toc();
126 // Utility method to build a hull of cells (and faces)
127 // around an edge (for 3D simplical meshes)
128 inline void constructHull
131 const UList<face>& faces,
132 const UList<edge>& edges,
133 const UList<cell>& cells,
134 const UList<label>& owner,
135 const UList<label>& neighbour,
136 const UList<labelList>& faceEdges,
137 const UList<labelList>& edgeFaces,
138 const labelList& vertexHull,
139 labelList& hullEdges,
140 labelList& hullFaces,
141 labelList& hullCells,
142 labelListList& ringEntities
145 // [1] hullEdges is an ordered list of edge-labels around eIndex,
146 // but not connected to it.
147 // - Ordering is in the same manner as vertexHull.
148 // [2] hullFaces is an ordered list of face-labels connected to eIndex.
149 // - Ordering is in the same manner as vertexHull.
150 // [3] hullCells is an ordered list of cell-labels connected to eIndex.
151 // - For boundary hulls, the last cell label is -1
152 // [4] ringEntities are edges and faces connected to eIndex[0] and eIndex[1]
153 // - ringEntities[0]: edges connected to eIndex[0]
154 // - ringEntities[1]: faces connected to eIndex[0]
155 // - ringEntities[2]: edges connected to eIndex[1]
156 // - ringEntities[3]: faces connected to eIndex[1]
159 label otherPoint = -1, nextPoint = -1;
161 // Obtain a reference to this edge, and its edgeFaces
162 const edge& edgeToCheck = edges[eIndex];
163 const labelList& eFaces = edgeFaces[eIndex];
165 // Loop through all faces of this edge and add them to hullFaces
166 forAll(eFaces, faceI)
168 const face& faceToCheck = faces[eFaces[faceI]];
170 // Find the isolated point on this face,
171 // and compare it with vertexHull
172 meshOps::findIsolatedPoint
182 forAll(vertexHull, indexI)
184 if (vertexHull[indexI] == otherPoint)
186 // Fill in the position of this face on the hull
187 hullFaces[indexI] = eFaces[faceI];
189 // Obtain edges connected to top and bottom
190 // vertices of edgeToCheck
191 const labelList& fEdges = faceEdges[hullFaces[indexI]];
193 forAll(fEdges, edgeI)
198 == edge(edgeToCheck[0], otherPoint)
201 ringEntities[0][indexI] = fEdges[edgeI];
207 == edge(edgeToCheck[1], otherPoint)
210 ringEntities[2][indexI] = fEdges[edgeI];
214 // Depending on the orientation of this face,
215 // fill in hull cell indices as well
216 if (nextPoint == edgeToCheck[0])
218 hullCells[indexI] = owner[eFaces[faceI]];
221 if (nextPoint == edgeToCheck[1])
223 hullCells[indexI] = neighbour[eFaces[faceI]];
227 // Something's terribly wrong
228 FatalErrorIn("void meshOps::constructHull()")
229 << nl << " Failed to construct hull. "
230 << nl << " Possibly not a tetrahedral mesh. "
231 << abort(FatalError);
234 if (hullCells[indexI] != -1)
236 label nextI = vertexHull.fcIndex(indexI);
237 label nextHullPoint = vertexHull[nextI];
238 const cell& currCell = cells[hullCells[indexI]];
240 // Look for the ring-faces
241 forAll(currCell, faceI)
243 const face& cFace = faces[currCell[faceI]];
245 // Check if this face contains edgeToCheck[0]
260 ringEntities[1][indexI] = currCell[faceI];
263 // Check if this face contains edgeToCheck[1]
278 ringEntities[3][indexI] = currCell[faceI];
282 // Scan one the faces for the ring-edge
283 const labelList& rFaceEdges =
285 faceEdges[ringEntities[1][indexI]]
288 forAll(rFaceEdges, edgeI)
292 edges[rFaceEdges[edgeI]]
293 == edge(otherPoint,nextHullPoint)
296 hullEdges[indexI] = rFaceEdges[edgeI];
302 // Done with this index. Break out.
308 // Throw an error if the point wasn't found
311 // Something's terribly wrong
312 FatalErrorIn("void meshOps::constructHull()")
313 << " Failed to construct hull. " << nl
314 << " edgeFaces connectivity is inconsistent. " << nl
315 << " Edge: " << eIndex << ":: " << edgeToCheck << nl
316 << " edgeFaces: " << eFaces << nl
317 << " vertexHull: " << vertexHull
318 << abort(FatalError);
324 // Given a set of points and edges, find the shortest path
325 // between the start and end point, using Dijkstra's algorithm.
326 // - Takes a Map of points and edges that use those points.
327 // - Edge weights are currently edge-lengths, but can easily be adapted.
328 // - Returns true if the endPoint was found by the algorithm.
329 // - The Map 'pi' returns a preceding point for every point in 'points'.
331 // Algorithm is inspired by:
333 // Dijkstra's Shortest Path Algorithm in Java
334 // http://renaud.waldura.com/
337 const Map<point>& points,
338 const Map<edge>& edges,
339 const label startPoint,
340 const label endPoint,
344 bool foundEndPoint = false;
346 // Set of unvisited (Q) / visited (S) points and distances (d)
350 // Initialize distances to large values
351 forAllConstIter(Map<point>, points, pIter)
353 d.insert(pIter.key(), GREAT);
356 // Invert edges to make a local pointEdges list
357 Map<labelList> localPointEdges;
359 forAllConstIter(Map<edge>, edges, eIter)
361 const edge& edgeToCheck = eIter();
363 forAll(edgeToCheck, pointI)
365 if (!localPointEdges.found(edgeToCheck[pointI]))
367 localPointEdges.insert(edgeToCheck[pointI], labelList(0));
373 localPointEdges[edgeToCheck[pointI]]
378 // Mark the startPoint as having the smallest distance
381 // Add the startPoint to the list of unvisited points
382 Q.insert(startPoint);
386 // Step 1: Find the node with the smallest distance from the start.
387 labelHashSet::iterator smallest = Q.begin();
391 labelHashSet::iterator iter = ++Q.begin();
396 if (d[iter.key()] < d[smallest.key()])
402 label pointIndex = smallest.key();
403 scalar smallestDistance = d[pointIndex];
405 // Move to the visited points list
406 S.insert(pointIndex);
409 // Step 2: Build a list of points adjacent to pointIndex
410 // but not in the visited list
411 dynamicLabelList adjacentPoints(10);
413 const labelList& pEdges = localPointEdges[pointIndex];
415 forAll(pEdges, edgeI)
417 const edge& edgeToCheck = edges[pEdges[edgeI]];
419 label otherPoint = edgeToCheck.otherVertex(pointIndex);
421 if (!S.found(otherPoint))
423 adjacentPoints.append(otherPoint);
427 // Step 3: Perform distance-based checks for adjacent points
428 forAll(adjacentPoints, pointI)
430 label adjPoint = adjacentPoints[pointI];
434 mag(points[adjPoint] - points[pointIndex])
438 // Check if the end-point has been touched.
439 if (adjPoint == endPoint)
441 foundEndPoint = true;
444 if (distance < d[adjPoint])
446 // Update to the shorter distance
447 d[adjPoint] = distance;
449 // Update the predecessor
450 if (pi.found(adjPoint))
452 pi[adjPoint] = pointIndex;
456 pi.insert(adjPoint, pointIndex);
459 // Add to the list of unvisited points
466 // Write out the path
471 dynamicLabelList pathNodes(50);
473 label currentPoint = endPoint;
475 while (currentPoint != startPoint)
477 pathNodes.append(currentPoint);
479 currentPoint = pi[currentPoint];
482 pathNodes.append(startPoint);
489 + Foam::name(startPoint)
491 + Foam::name(endPoint),
499 return foundEndPoint;
503 // Select a list of elements from connectivity,
504 // and output to a VTK format
507 const polyMesh& mesh,
509 const labelList& cList,
510 const label primitiveType,
511 const UList<point>& meshPoints,
512 const UList<edge>& edges,
513 const UList<face>& faces,
514 const UList<cell>& cells,
515 const UList<label>& owner,
516 const UList<scalar>& scalField,
517 const UList<label>& lablField,
518 const UList<vector>& vectField
521 label nTotalCells = 0;
522 label nPoints = 0, nCells = 0;
524 // Estimate a size for points and cellPoints
525 pointField points(6*cList.size());
527 // Connectivity lists
528 labelListList cpList(cList.size());
530 // Create a map for local points
531 Map<label> pointMap, reversePointMap, reverseCellMap;
535 if (cList[cellI] < 0)
540 bool isPolyhedron = false;
542 switch (primitiveType)
544 // Are we looking at points?
548 cpList[nCells].setSize(1);
550 cpList[nCells] = cList[cellI];
557 // Are we looking at edges?
561 cpList[nCells].setSize(2);
563 const edge& tEdge = edges[cList[cellI]];
565 cpList[nCells][0] = tEdge[0];
566 cpList[nCells][1] = tEdge[1];
573 // Are we looking at faces?
576 const face& tFace = faces[cList[cellI]];
578 // Size the list and assign
579 cpList[nCells] = tFace;
581 nTotalCells += tFace.size();
586 // Are we looking at cells?
589 const cell& tCell = cells[cList[cellI]];
591 // Is face information provided?
594 if (tCell.size() == 4)
596 // Point-ordering for tetrahedra
597 const face& baseFace = faces[tCell[0]];
598 const face& checkFace = faces[tCell[1]];
601 cpList[nCells].setSize(4);
603 // Get the fourth point
606 meshOps::findIsolatedPoint(baseFace, checkFace)
609 // Something's wrong with connectivity.
614 "void meshOps::writeVTK\n"
616 " const polyMesh& mesh,\n"
617 " const word& name,\n"
618 " const labelList& cList,\n"
619 " const label primitiveType,\n"
620 " const UList<point>& meshPoints,\n"
621 " const UList<edge>& edges,\n"
622 " const UList<face>& faces,\n"
623 " const UList<cell>& cells,\n"
624 " const UList<label>& owner\n"
625 " const UList<scalar>& scalField,\n"
626 " const UList<label>& lablField,\n"
627 " const UList<vector>& vectField\n"
630 << "Cell: " << cList[cellI]
632 << " has inconsistent connectivity."
633 << abort(FatalError);
636 // Write-out in order
637 label ownCell = owner[tCell[0]];
639 if (ownCell == cList[cellI])
641 cpList[nCells][0] = baseFace[2];
642 cpList[nCells][1] = baseFace[1];
643 cpList[nCells][2] = baseFace[0];
644 cpList[nCells][3] = apexPoint;
648 cpList[nCells][0] = baseFace[0];
649 cpList[nCells][1] = baseFace[1];
650 cpList[nCells][2] = baseFace[2];
651 cpList[nCells][3] = apexPoint;
657 if (tCell.size() > 4)
659 // Point ordering for polyhedra
662 // First obtain the face count
667 npF += faces[tCell[faceI]].size();
671 cpList[nCells].setSize(tCell.size() + npF + 1);
673 // Fill in facePoints
676 // Fill in the number of faces
677 cpList[nCells][nP++] = tCell.size();
681 const face& checkFace = faces[tCell[faceI]];
683 if (checkFace.empty())
685 FatalErrorIn("void meshOps::writeVTK()")
686 << " Empty face: " << tCell[faceI]
687 << abort(FatalError);
690 // First fill in face size
691 cpList[nCells][nP++] = checkFace.size();
693 // Next fill in points in order
694 if (owner[tCell[faceI]] == cList[cellI])
696 forAll(checkFace, pI)
698 cpList[nCells][nP++] = checkFace[pI];
703 forAllReverse(checkFace, pI)
705 cpList[nCells][nP++] = checkFace[pI];
710 nTotalCells += (tCell.size() + npF + 1);
714 FatalErrorIn("void meshOps::writeVTK()")
715 << " Wrong cell size: " << tCell << nl
716 << " Index: " << cList[cellI] << nl
717 << abort(FatalError);
722 // No face information.
723 // Assume cell-to-node information.
724 if (tCell.size() == 4)
726 // Build a face out of first three points.
729 meshPoints[tCell[0]],
730 meshPoints[tCell[1]],
734 // Fetch fourth point
735 const point& d = meshPoints[tCell[3]];
737 scalar dDotN = ((d - tpr.a()) & tpr.normal());
740 cpList[nCells].setSize(4);
744 // Correct orientation
745 cpList[nCells][0] = tCell[0];
746 cpList[nCells][1] = tCell[1];
747 cpList[nCells][2] = tCell[2];
748 cpList[nCells][3] = tCell[3];
753 cpList[nCells][0] = tCell[2];
754 cpList[nCells][1] = tCell[1];
755 cpList[nCells][2] = tCell[0];
756 cpList[nCells][3] = tCell[3];
763 FatalErrorIn("void meshOps::writeVTK()")
764 << " Wrong cell size: " << tCell << nl
765 << " Index: " << cList[cellI] << nl
766 << abort(FatalError);
775 FatalErrorIn("void meshOps::writeVTK() const")
776 << " Incorrect primitiveType: "
778 << abort(FatalError);
782 // Renumber to local ordering
785 // Polyhedra also contain face sizes, so use a different scheme
788 // Fetch number of faces
789 label npF = cpList[nCells][pCtr++];
791 for (label i = 0; i < npF; i++)
793 // Fetch number of points
794 label npP = cpList[nCells][pCtr++];
796 // Read points in order
797 for (label j = 0; j < npP; j++)
799 // Check if this point was added to the map
800 if (!pointMap.found(cpList[nCells][pCtr]))
802 // Resize pointField if necessary
803 if (nPoints >= points.size())
805 points.setSize(2 * nPoints);
808 // Point was not found, so add it
809 points[nPoints] = meshPoints[cpList[nCells][pCtr]];
812 pointMap.insert(cpList[nCells][pCtr], nPoints);
813 reversePointMap.insert(nPoints, cpList[nCells][pCtr]);
815 // Increment the number of points
820 cpList[nCells][pCtr] = pointMap[cpList[nCells][pCtr]];
822 // Increment point counter
829 forAll(cpList[nCells], pointI)
831 // Check if this point was added to the map
832 if (!pointMap.found(cpList[nCells][pointI]))
834 // Resize pointField if necessary
835 if (nPoints >= points.size())
837 points.setSize(2 * nPoints);
840 // Point was not found, so add it
841 points[nPoints] = meshPoints[cpList[nCells][pointI]];
844 pointMap.insert(cpList[nCells][pointI], nPoints);
845 reversePointMap.insert(nPoints, cpList[nCells][pointI]);
847 // Increment the number of points
852 cpList[nCells][pointI] = pointMap[cpList[nCells][pointI]];
856 // Update the cell map.
857 reverseCellMap.insert(nCells, cList[cellI]);
862 // Finally write it out
882 // Actual routine to write out the VTK file
885 const polyMesh& mesh,
889 const label nTotalCells,
890 const vectorField& points,
891 const labelListList& cpList,
892 const label primitiveType,
893 const Map<label>& reversePointMap,
894 const Map<label>& reverseCellMap,
895 const UList<scalar>& scalField,
896 const UList<label>& lablField,
897 const UList<vector>& vectField
900 // Make the directory
901 fileName dirName(mesh.time().path()/"VTK"/mesh.time().timeName());
905 // Open stream for output
906 OFstream file(dirName/name+".vtk");
908 // Write out the header
909 file << "# vtk DataFile Version 2.0" << nl
910 << name << ".vtk" << nl
912 << "DATASET UNSTRUCTURED_GRID" << nl
913 << "POINTS " << nPoints << " double" << nl;
915 for (label i = 0; i < nPoints; i++)
917 file << setprecision(15)
918 << points[i].x() << ' '
919 << points[i].y() << ' '
920 << points[i].z() << ' '
924 file << "CELLS " << nCells << " " << nTotalCells + nCells << endl;
930 if (cpList[i].size())
932 file << cpList[i].size() << ' ';
936 file << cpList[i][j] << ' ';
946 for (label i = 0; i < nPoints; i++)
948 file << 1 << ' ' << i << nl;
952 file << "CELL_TYPES " << nCells << endl;
958 switch (primitiveType)
974 // General Polygonal Face
984 if (cpList[i].size() == 4)
990 if (cpList[i].size() > 4)
1001 FatalErrorIn("void meshOps::writeVTK() const")
1002 << " Incorrect primitiveType: "
1004 << abort(FatalError);
1012 for (label i = 0; i < nPoints; i++)
1019 label nCellFields = 0, nPointFields = 0;
1021 if (reverseCellMap.size())
1026 if (scalField.size() == nCells && scalField.size())
1031 if (lablField.size() == nCells && lablField.size())
1036 if (vectField.size() == nCells && vectField.size())
1041 // Write out indices for visualization.
1044 file << "CELL_DATA " << nCells << endl;
1046 file << "FIELD CellFields " << nCellFields << endl;
1048 if (reverseCellMap.size())
1050 file << "CellIds 1 " << nCells << " int" << endl;
1052 for (label i = 0; i < nCells; i++)
1054 file << reverseCellMap[i] << ' ';
1060 if (scalField.size() == nCells)
1062 file << "CellScalars 1 " << nCells << " double" << endl;
1064 forAll(scalField, i)
1066 file << scalField[i] << ' ';
1072 if (lablField.size() == nCells)
1074 file << "CellLabels 1 " << nCells << " int" << endl;
1076 forAll(lablField, i)
1078 file << lablField[i] << ' ';
1084 if (vectField.size() == nCells)
1086 file << "CellVectors 3 " << nCells << " double" << endl;
1088 forAll(vectField, i)
1090 file << vectField[i].x() << ' '
1091 << vectField[i].y() << ' '
1092 << vectField[i].z() << ' ';
1099 if (reversePointMap.size())
1104 if (scalField.size() == nPoints && scalField.size())
1109 if (lablField.size() == nPoints && lablField.size())
1114 if (vectField.size() == nPoints && vectField.size())
1119 // Write out indices for visualization.
1122 file << "POINT_DATA " << nPoints << endl;
1124 file << "FIELD PointFields " << nPointFields << endl;
1126 if (reversePointMap.size())
1128 file << "PointIds 1 " << nPoints << " int" << endl;
1130 for (label i = 0; i < nPoints; i++)
1132 file << reversePointMap[i] << ' ';
1138 if (scalField.size() == nPoints)
1140 file << "PointScalars 1 " << nPoints << " double" << endl;
1142 forAll(scalField, i)
1144 file << scalField[i] << ' ';
1150 if (lablField.size() == nPoints)
1152 file << "PointLabels 1 " << nPoints << " int" << endl;
1154 forAll(lablField, i)
1156 file << lablField[i] << ' ';
1162 if (vectField.size() == nPoints)
1164 file << "PointVectors 3 " << nPoints << " double" << endl;
1166 forAll(vectField, i)
1168 file << vectField[i].x() << ' '
1169 << vectField[i].y() << ' '
1170 << vectField[i].z() << ' ';
1179 } // End namespace meshOps
1182 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
1184 } // End namespace Foam
1186 // ************************************************************************* //