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 Various utility functions that perform mesh-related operations.
33 University of Massachusetts Amherst
36 \*---------------------------------------------------------------------------*/
38 #include "objectRegistry.H"
47 #include "triPointRef.H"
48 #include "tetPointRef.H"
50 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
58 // Utility method to build a hull of cells
59 // connected to the edge (for 2D simplical meshes)
60 void constructPrismHull
63 const UList<face>& faces,
64 const UList<cell>& cells,
65 const UList<label>& owner,
66 const UList<label>& neighbour,
67 const UList<labelList>& edgeFaces,
68 labelList& hullTriFaces,
72 labelHashSet cellSet, triFaceSet;
75 const labelList& eFaces = edgeFaces[eIndex];
77 // Loop through edgeFaces and add cells
80 label c0 = owner[eFaces[faceI]];
81 label c1 = neighbour[eFaces[faceI]];
83 if (!cellSet.found(c0))
88 // Find associated triFaces and add them too
89 const cell& cC = cells[c0];
93 const face& cF = faces[cC[faceJ]];
95 if ((cF.size() == 3) && !(triFaceSet.found(cC[faceJ])))
97 triFaceSet.insert(cC[faceJ]);
102 if (!cellSet.found(c1) && (c1 != -1))
107 // Find associated triFaces and add them too
108 const cell& cC = cells[c1];
112 const face& cF = faces[cC[faceJ]];
114 if ((cF.size() == 3) && !(triFaceSet.found(cC[faceJ])))
116 triFaceSet.insert(cC[faceJ]);
122 // Obtain lists from hashSets
123 hullCells = cellSet.toc();
124 hullTriFaces = triFaceSet.toc();
128 // Utility method to build a hull of cells (and faces)
129 // around an edge (for 3D simplical meshes)
133 const UList<face>& faces,
134 const UList<edge>& edges,
135 const UList<cell>& cells,
136 const UList<label>& owner,
137 const UList<label>& neighbour,
138 const UList<labelList>& faceEdges,
139 const UList<labelList>& edgeFaces,
140 const labelList& vertexHull,
141 labelList& hullEdges,
142 labelList& hullFaces,
143 labelList& hullCells,
144 labelListList& ringEntities
147 // [1] hullEdges is an ordered list of edge-labels around eIndex,
148 // but not connected to it.
149 // - Ordering is in the same manner as vertexHull.
150 // [2] hullFaces is an ordered list of face-labels connected to eIndex.
151 // - Ordering is in the same manner as vertexHull.
152 // [3] hullCells is an ordered list of cell-labels connected to eIndex.
153 // - For boundary hulls, the last cell label is -1
154 // [4] ringEntities are edges and faces connected to eIndex[0] and eIndex[1]
155 // - ringEntities[0]: edges connected to eIndex[0]
156 // - ringEntities[1]: faces connected to eIndex[0]
157 // - ringEntities[2]: edges connected to eIndex[1]
158 // - ringEntities[3]: faces connected to eIndex[1]
161 label otherPoint = -1, nextPoint = -1;
163 // Obtain a reference to this edge, and its edgeFaces
164 const edge& edgeToCheck = edges[eIndex];
165 const labelList& eFaces = edgeFaces[eIndex];
167 // Loop through all faces of this edge and add them to hullFaces
168 forAll(eFaces, faceI)
170 const face& faceToCheck = faces[eFaces[faceI]];
172 // Find the isolated point on this face,
173 // and compare it with vertexHull
174 meshOps::findIsolatedPoint
184 forAll(vertexHull, indexI)
186 if (vertexHull[indexI] == otherPoint)
188 // Fill in the position of this face on the hull
189 hullFaces[indexI] = eFaces[faceI];
191 // Obtain edges connected to top and bottom
192 // vertices of edgeToCheck
193 const labelList& fEdges = faceEdges[hullFaces[indexI]];
195 forAll(fEdges, edgeI)
200 == edge(edgeToCheck[0], otherPoint)
203 ringEntities[0][indexI] = fEdges[edgeI];
209 == edge(edgeToCheck[1], otherPoint)
212 ringEntities[2][indexI] = fEdges[edgeI];
216 // Depending on the orientation of this face,
217 // fill in hull cell indices as well
218 if (nextPoint == edgeToCheck[0])
220 hullCells[indexI] = owner[eFaces[faceI]];
223 if (nextPoint == edgeToCheck[1])
225 hullCells[indexI] = neighbour[eFaces[faceI]];
229 // Something's terribly wrong
230 FatalErrorIn("void meshOps::constructHull()")
231 << nl << " Failed to construct hull. "
232 << nl << " Possibly not a tetrahedral mesh. "
233 << abort(FatalError);
236 if (hullCells[indexI] != -1)
238 label nextI = vertexHull.fcIndex(indexI);
239 label nextHullPoint = vertexHull[nextI];
240 const cell& currCell = cells[hullCells[indexI]];
242 // Look for the ring-faces
243 forAll(currCell, faceI)
245 const face& cFace = faces[currCell[faceI]];
247 // Check if this face contains edgeToCheck[0]
262 ringEntities[1][indexI] = currCell[faceI];
265 // Check if this face contains edgeToCheck[1]
280 ringEntities[3][indexI] = currCell[faceI];
284 // Scan one the faces for the ring-edge
285 const labelList& rFaceEdges =
287 faceEdges[ringEntities[1][indexI]]
290 forAll(rFaceEdges, edgeI)
294 edges[rFaceEdges[edgeI]]
295 == edge(otherPoint,nextHullPoint)
298 hullEdges[indexI] = rFaceEdges[edgeI];
304 // Done with this index. Break out.
310 // Throw an error if the point wasn't found
313 // Something's terribly wrong
314 FatalErrorIn("void meshOps::constructHull()")
315 << " Failed to construct hull. " << nl
316 << " edgeFaces connectivity is inconsistent. " << nl
317 << " Edge: " << eIndex << ":: " << edgeToCheck << nl
318 << " edgeFaces: " << eFaces << nl
319 << " vertexHull: " << vertexHull
320 << abort(FatalError);
326 // Given a set of points and edges, find the shortest path
327 // between the start and end point, using Dijkstra's algorithm.
328 // - Takes a Map of points and edges that use those points.
329 // - Edge weights are currently edge-lengths, but can easily be adapted.
330 // - Returns true if the endPoint was found by the algorithm.
331 // - The Map 'pi' returns a preceding point for every point in 'points'.
333 // Algorithm is inspired by:
335 // Dijkstra's Shortest Path Algorithm in Java
336 // http://renaud.waldura.com/
339 const Map<point>& points,
340 const Map<edge>& edges,
341 const label startPoint,
342 const label endPoint,
346 bool foundEndPoint = false;
348 // Set of unvisited (Q) / visited (S) points and distances (d)
352 // Initialize distances to large values
353 forAllConstIter(Map<point>, points, pIter)
355 d.insert(pIter.key(), GREAT);
358 // Invert edges to make a local pointEdges list
359 Map<labelList> localPointEdges;
361 forAllConstIter(Map<edge>, edges, eIter)
363 const edge& edgeToCheck = eIter();
365 forAll(edgeToCheck, pointI)
367 if (!localPointEdges.found(edgeToCheck[pointI]))
369 localPointEdges.insert(edgeToCheck[pointI], labelList(0));
375 localPointEdges[edgeToCheck[pointI]]
380 // Mark the startPoint as having the smallest distance
383 // Add the startPoint to the list of unvisited points
384 Q.insert(startPoint);
388 // Step 1: Find the node with the smallest distance from the start.
389 labelHashSet::iterator smallest = Q.begin();
393 labelHashSet::iterator iter = ++Q.begin();
398 if (d[iter.key()] < d[smallest.key()])
404 label pointIndex = smallest.key();
405 scalar smallestDistance = d[pointIndex];
407 // Move to the visited points list
408 S.insert(pointIndex);
411 // Step 2: Build a list of points adjacent to pointIndex
412 // but not in the visited list
413 DynamicList<label> adjacentPoints(10);
415 const labelList& pEdges = localPointEdges[pointIndex];
417 forAll(pEdges, edgeI)
419 const edge& edgeToCheck = edges[pEdges[edgeI]];
421 label otherPoint = edgeToCheck.otherVertex(pointIndex);
423 if (!S.found(otherPoint))
425 adjacentPoints.append(otherPoint);
429 // Step 3: Perform distance-based checks for adjacent points
430 forAll(adjacentPoints, pointI)
432 label adjPoint = adjacentPoints[pointI];
436 mag(points[adjPoint] - points[pointIndex])
440 // Check if the end-point has been touched.
441 if (adjPoint == endPoint)
443 foundEndPoint = true;
446 if (distance < d[adjPoint])
448 // Update to the shorter distance
449 d[adjPoint] = distance;
451 // Update the predecessor
452 if (pi.found(adjPoint))
454 pi[adjPoint] = pointIndex;
458 pi.insert(adjPoint, pointIndex);
461 // Add to the list of unvisited points
467 return foundEndPoint;
471 // Select a list of elements from connectivity,
472 // and output to a VTK format
475 const polyMesh& mesh,
477 const labelList& cList,
478 const label primitiveType,
479 const UList<point>& meshPoints,
480 const UList<edge>& edges,
481 const UList<face>& faces,
482 const UList<cell>& cells,
483 const UList<label>& owner,
484 const UList<scalar>& scalField,
485 const UList<label>& lablField,
486 const UList<vector>& vectField
489 label nTotalCells = 0;
490 label nPoints = 0, nCells = 0;
492 // Estimate a size for points and cellPoints
493 pointField points(6*cList.size());
495 // Connectivity lists
496 labelListList cpList(cList.size());
498 // Create a map for local points
499 Map<label> pointMap, reversePointMap, reverseCellMap;
503 if (cList[cellI] < 0)
508 bool isPolyhedron = false;
510 switch (primitiveType)
512 // Are we looking at points?
516 cpList[nCells].setSize(1);
518 cpList[nCells] = cList[cellI];
525 // Are we looking at edges?
529 cpList[nCells].setSize(2);
531 const edge& tEdge = edges[cList[cellI]];
533 cpList[nCells][0] = tEdge[0];
534 cpList[nCells][1] = tEdge[1];
541 // Are we looking at faces?
544 const face& tFace = faces[cList[cellI]];
546 // Size the list and assign
547 cpList[nCells] = tFace;
549 nTotalCells += tFace.size();
554 // Are we looking at cells?
557 const cell& tCell = cells[cList[cellI]];
559 // Is face information provided?
562 if (tCell.size() == 4)
564 // Point-ordering for tetrahedra
565 const face& baseFace = faces[tCell[0]];
566 const face& checkFace = faces[tCell[1]];
569 cpList[nCells].setSize(4);
571 // Get the fourth point
574 meshOps::findIsolatedPoint(baseFace, checkFace)
577 // Something's wrong with connectivity.
582 "void meshOps::writeVTK\n"
584 " const polyMesh& mesh,\n"
585 " const word& name,\n"
586 " const labelList& cList,\n"
587 " const label primitiveType,\n"
588 " const UList<point>& meshPoints,\n"
589 " const UList<edge>& edges,\n"
590 " const UList<face>& faces,\n"
591 " const UList<cell>& cells,\n"
592 " const UList<label>& owner\n"
593 " const UList<scalar>& scalField,\n"
594 " const UList<label>& lablField,\n"
595 " const UList<vector>& vectField\n"
598 << "Cell: " << cList[cellI]
600 << " has inconsistent connectivity."
601 << abort(FatalError);
604 // Write-out in order
605 label ownCell = owner[tCell[0]];
607 if (ownCell == cList[cellI])
609 cpList[nCells][0] = baseFace[2];
610 cpList[nCells][1] = baseFace[1];
611 cpList[nCells][2] = baseFace[0];
612 cpList[nCells][3] = apexPoint;
616 cpList[nCells][0] = baseFace[0];
617 cpList[nCells][1] = baseFace[1];
618 cpList[nCells][2] = baseFace[2];
619 cpList[nCells][3] = apexPoint;
625 if (tCell.size() > 4)
627 // Point ordering for polyhedra
630 // First obtain the face count
635 npF += faces[tCell[faceI]].size();
639 cpList[nCells].setSize(tCell.size() + npF + 1);
641 // Fill in facePoints
644 // Fill in the number of faces
645 cpList[nCells][nP++] = tCell.size();
649 const face& checkFace = faces[tCell[faceI]];
651 if (checkFace.empty())
653 FatalErrorIn("void meshOps::writeVTK()")
654 << " Empty face: " << tCell[faceI]
655 << abort(FatalError);
658 // First fill in face size
659 cpList[nCells][nP++] = checkFace.size();
661 // Next fill in points in order
662 if (owner[tCell[faceI]] == cList[cellI])
664 forAll(checkFace, pI)
666 cpList[nCells][nP++] = checkFace[pI];
671 forAllReverse(checkFace, pI)
673 cpList[nCells][nP++] = checkFace[pI];
678 nTotalCells += (tCell.size() + npF + 1);
682 FatalErrorIn("void meshOps::writeVTK()")
683 << " Wrong cell size: " << tCell << nl
684 << " Index: " << cList[cellI] << nl
685 << abort(FatalError);
690 // No face information.
691 // Assume cell-to-node information.
692 if (tCell.size() == 4)
694 // Build a face out of first three points.
697 meshPoints[tCell[0]],
698 meshPoints[tCell[1]],
702 // Fetch fourth point
703 const point& d = meshPoints[tCell[3]];
705 scalar dDotN = ((d - tpr.a()) & tpr.normal());
708 cpList[nCells].setSize(4);
712 // Correct orientation
713 cpList[nCells][0] = tCell[0];
714 cpList[nCells][1] = tCell[1];
715 cpList[nCells][2] = tCell[2];
716 cpList[nCells][3] = tCell[3];
721 cpList[nCells][0] = tCell[2];
722 cpList[nCells][1] = tCell[1];
723 cpList[nCells][2] = tCell[0];
724 cpList[nCells][3] = tCell[3];
731 FatalErrorIn("void meshOps::writeVTK()")
732 << " Wrong cell size: " << tCell << nl
733 << " Index: " << cList[cellI] << nl
734 << abort(FatalError);
743 FatalErrorIn("void meshOps::writeVTK() const")
744 << " Incorrect primitiveType: "
746 << abort(FatalError);
750 // Renumber to local ordering
753 // Polyhedra also contain face sizes, so use a different scheme
756 // Fetch number of faces
757 label npF = cpList[nCells][pCtr++];
759 for (label i = 0; i < npF; i++)
761 // Fetch number of points
762 label npP = cpList[nCells][pCtr++];
764 // Read points in order
765 for (label j = 0; j < npP; j++)
767 // Check if this point was added to the map
768 if (!pointMap.found(cpList[nCells][pCtr]))
770 // Resize pointField if necessary
771 if (nPoints >= points.size())
773 points.setSize(2 * nPoints);
776 // Point was not found, so add it
777 points[nPoints] = meshPoints[cpList[nCells][pCtr]];
780 pointMap.insert(cpList[nCells][pCtr], nPoints);
781 reversePointMap.insert(nPoints, cpList[nCells][pCtr]);
783 // Increment the number of points
788 cpList[nCells][pCtr] = pointMap[cpList[nCells][pCtr]];
790 // Increment point counter
797 forAll(cpList[nCells], pointI)
799 // Check if this point was added to the map
800 if (!pointMap.found(cpList[nCells][pointI]))
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][pointI]];
812 pointMap.insert(cpList[nCells][pointI], nPoints);
813 reversePointMap.insert(nPoints, cpList[nCells][pointI]);
815 // Increment the number of points
820 cpList[nCells][pointI] = pointMap[cpList[nCells][pointI]];
824 // Update the cell map.
825 reverseCellMap.insert(nCells, cList[cellI]);
830 // Finally write it out
850 // Actual routine to write out the VTK file
853 const polyMesh& mesh,
857 const label nTotalCells,
858 const vectorField& points,
859 const labelListList& cpList,
860 const label primitiveType,
861 const Map<label>& reversePointMap,
862 const Map<label>& reverseCellMap,
863 const UList<scalar>& scalField,
864 const UList<label>& lablField,
865 const UList<vector>& vectField
868 // Make the directory
869 fileName dirName(mesh.time().path()/"VTK"/mesh.time().timeName());
873 // Open stream for output
874 OFstream file(dirName/name+".vtk");
876 // Write out the header
877 file << "# vtk DataFile Version 2.0" << nl
878 << name << ".vtk" << nl
880 << "DATASET UNSTRUCTURED_GRID" << nl
881 << "POINTS " << nPoints << " double" << nl;
883 for (label i = 0; i < nPoints; i++)
885 file << setprecision(15)
886 << points[i].x() << ' '
887 << points[i].y() << ' '
888 << points[i].z() << ' '
892 file << "CELLS " << nCells << " " << nTotalCells + nCells << endl;
898 if (cpList[i].size())
900 file << cpList[i].size() << ' ';
904 file << cpList[i][j] << ' ';
914 for (label i = 0; i < nPoints; i++)
916 file << 1 << ' ' << i << nl;
920 file << "CELL_TYPES " << nCells << endl;
926 switch (primitiveType)
942 // General Polygonal Face
952 if (cpList[i].size() == 4)
958 if (cpList[i].size() > 4)
969 FatalErrorIn("void meshOps::writeVTK() const")
970 << " Incorrect primitiveType: "
972 << abort(FatalError);
980 for (label i = 0; i < nPoints; i++)
987 label nCellFields = 0, nPointFields = 0;
989 if (reverseCellMap.size())
994 if (scalField.size() == nCells && scalField.size())
999 if (lablField.size() == nCells && lablField.size())
1004 if (vectField.size() == nCells && vectField.size())
1009 // Write out indices for visualization.
1012 file << "CELL_DATA " << nCells << endl;
1014 file << "FIELD CellFields " << nCellFields << endl;
1016 if (reverseCellMap.size())
1018 file << "CellIds 1 " << nCells << " int" << endl;
1020 for (label i = 0; i < nCells; i++)
1022 file << reverseCellMap[i] << ' ';
1028 if (scalField.size() == nCells)
1030 file << "CellScalars 1 " << nCells << " double" << endl;
1032 forAll(scalField, i)
1034 file << scalField[i] << ' ';
1040 if (lablField.size() == nCells)
1042 file << "CellLabels 1 " << nCells << " int" << endl;
1044 forAll(lablField, i)
1046 file << lablField[i] << ' ';
1052 if (vectField.size() == nCells)
1054 file << "CellVectors 3 " << nCells << " double" << endl;
1056 forAll(vectField, i)
1058 file << vectField[i].x() << ' '
1059 << vectField[i].y() << ' '
1060 << vectField[i].z() << ' ';
1067 if (reversePointMap.size())
1072 if (scalField.size() == nPoints && scalField.size())
1077 if (lablField.size() == nPoints && lablField.size())
1082 if (vectField.size() == nPoints && vectField.size())
1087 // Write out indices for visualization.
1090 file << "POINT_DATA " << nPoints << endl;
1092 file << "FIELD PointFields " << nPointFields << endl;
1094 if (reversePointMap.size())
1096 file << "PointIds 1 " << nPoints << " int" << endl;
1098 for (label i = 0; i < nPoints; i++)
1100 file << reversePointMap[i] << ' ';
1106 if (scalField.size() == nPoints)
1108 file << "PointScalars 1 " << nPoints << " double" << endl;
1110 forAll(scalField, i)
1112 file << scalField[i] << ' ';
1118 if (lablField.size() == nPoints)
1120 file << "PointLabels 1 " << nPoints << " int" << endl;
1122 forAll(lablField, i)
1124 file << lablField[i] << ' ';
1130 if (vectField.size() == nPoints)
1132 file << "PointVectors 3 " << nPoints << " double" << endl;
1134 forAll(vectField, i)
1136 file << vectField[i].x() << ' '
1137 << vectField[i].y() << ' '
1138 << vectField[i].z() << ' ';
1147 } // End namespace meshOps
1150 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
1152 } // End namespace Foam
1154 // ************************************************************************* //