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/>.
25 Reads .msh file as written by Gmsh.
27 Needs surface elements on mesh to be present and aligned with outside faces
28 of the mesh. I.e. if the mesh is hexes, the outside faces need to be quads
30 Note: There is something seriously wrong with the ordering written in the
31 .msh file. Normal operation is to check the ordering and invert prisms
32 and hexes if found to be wrong way round.
33 Use the -keepOrientation to keep the raw information.
35 Note: The code now uses the element (cell,face) physical region id number
36 to create cell zones and faces zones (similar to
37 fluentMeshWithInternalFaces).
39 A use of the cell zone information, is for field initialization with the
40 "setFields" utility. see the classes: topoSetSource, zoneToCell.
41 \*---------------------------------------------------------------------------*/
48 #include "cellModeller.H"
49 #include "repatchPolyTopoChanger.H"
55 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
57 // Element type numbers
58 static label MSHTRI = 2;
59 static label MSHQUAD = 3;
60 static label MSHTET = 4;
61 static label MSHPYR = 7;
62 static label MSHPRISM = 6;
63 static label MSHHEX = 5;
66 // Skips till end of section. Returns false if end of file.
67 bool skipSection(IFstream& inFile)
79 while (line.size() < 4 || line.substr(0, 4) != "$End");
87 const Map<label>& mshToFoam,
91 forAll(labels, labelI)
93 labels[labelI] = mshToFoam[labels[labelI]];
98 // Find face in pp which uses all vertices in meshF (in mesh point labels)
99 label findFace(const primitivePatch& pp, const labelList& meshF)
101 const Map<label>& meshPointMap = pp.meshPointMap();
103 // meshF[0] in pp labels.
104 if (!meshPointMap.found(meshF[0]))
106 Warning<< "Not using gmsh face " << meshF
107 << " since zero vertex is not on boundary of polyMesh" << endl;
111 // Find faces using first point
112 const labelList& pFaces = pp.pointFaces()[meshPointMap[meshF[0]]];
114 // Go through all these faces and check if there is one which uses all of
115 // meshF vertices (in any order ;-)
118 label faceI = pFaces[i];
120 const face& f = pp[faceI];
122 // Count uses of vertices of meshF for f
127 if (findIndex(meshF, f[fp]) != -1)
133 if (nMatched == meshF.size())
143 // Same but find internal face. Expensive addressing.
144 label findInternalFace(const primitiveMesh& mesh, const labelList& meshF)
146 const labelList& pFaces = mesh.pointFaces()[meshF[0]];
150 label faceI = pFaces[i];
152 const face& f = mesh.faces()[faceI];
154 // Count uses of vertices of meshF for f
159 if (findIndex(meshF, f[fp]) != -1)
165 if (nMatched == meshF.size())
174 // Determine whether cell is inside-out by checking for any wrong-oriented
176 bool correctOrientation(const pointField& points, const cellShape& shape)
178 // Get centre of shape.
179 point cc(shape.centre(points));
181 // Get outwards pointing faces.
182 faceList faces(shape.faces());
186 const face& f = faces[i];
188 vector n(f.normal(points));
190 // Check if vector from any point on face to cc points outwards
191 if (((points[f[0]] - cc) & n) < 0)
193 // Incorrectly oriented
206 Map<label>& physToZone,
208 labelList& zoneToPhys,
209 List<DynamicList<label> >& zoneCells
212 Map<label>::const_iterator zoneFnd = physToZone.find(regPhys);
214 if (zoneFnd == physToZone.end())
216 // New region. Allocate zone for it.
217 label zoneI = zoneCells.size();
218 zoneCells.setSize(zoneI+1);
219 zoneToPhys.setSize(zoneI+1);
221 Info<< "Mapping region " << regPhys << " to Foam cellZone "
223 physToZone.insert(regPhys, zoneI);
225 zoneToPhys[zoneI] = regPhys;
226 zoneCells[zoneI].append(cellI);
230 // Existing zone for region
231 zoneCells[zoneFnd()].append(cellI);
236 // Reads points and map
237 void readPoints(IFstream& inFile, pointField& points, Map<label>& mshToFoam)
239 Info<< "Starting to read points at line " << inFile.lineNumber() << endl;
242 inFile.getLine(line);
243 IStringStream lineStr(line);
248 Info<< "Vertices to be read:" << nVerts << endl;
250 points.setSize(nVerts);
251 mshToFoam.resize(2*nVerts);
253 for (label pointI = 0; pointI < nVerts; pointI++)
256 scalar xVal, yVal, zVal;
259 inFile.getLine(line);
260 IStringStream lineStr(line);
262 lineStr >> mshLabel >> xVal >> yVal >> zVal;
264 point& pt = points[pointI];
270 mshToFoam.insert(mshLabel, pointI);
273 Info<< "Vertices read:" << mshToFoam.size() << endl;
275 inFile.getLine(line);
276 IStringStream tagStr(line);
279 if (tag != "$ENDNOD" && tag != "$EndNodes")
281 FatalErrorIn("readPoints(..)")
282 << "Did not find $ENDNOD tag on line "
283 << inFile.lineNumber() << exit(FatalError);
289 // Reads physical names
290 void readPhysNames(IFstream& inFile, Map<word>& physicalNames)
292 Info<< "Starting to read physical names at line " << inFile.lineNumber()
296 inFile.getLine(line);
297 IStringStream lineStr(line);
302 Info<< "Physical names:" << nNames << endl;
304 physicalNames.resize(nNames);
306 for (label i = 0; i < nNames; i++)
312 inFile.getLine(line);
313 IStringStream lineStr(line);
314 label nSpaces = lineStr.str().count(' ');
318 lineStr >> regionI >> regionName;
320 Info<< " " << regionI << '\t'
321 << string::validate<word>(regionName) << endl;
323 else if(nSpaces == 2)
325 // >= Gmsh2.4 physical types has tag in front.
327 lineStr >> physType >> regionI >> regionName;
330 Info<< " " << "Line " << regionI << '\t'
331 << string::validate<word>(regionName) << endl;
333 else if (physType == 2)
335 Info<< " " << "Surface " << regionI << '\t'
336 << string::validate<word>(regionName) << endl;
338 else if (physType == 3)
340 Info<< " " << "Volume " << regionI << '\t'
341 << string::validate<word>(regionName) << endl;
345 physicalNames.insert(regionI, string::validate<word>(regionName));
348 inFile.getLine(line);
349 IStringStream tagStr(line);
352 if (tag != "$EndPhysicalNames")
354 FatalErrorIn("readPhysicalNames(..)")
355 << "Did not find $EndPhysicalNames tag on line "
356 << inFile.lineNumber() << exit(FatalError);
362 // Reads cells and patch faces
365 const bool version2Format,
366 const bool keepOrientation,
367 const pointField& points,
368 const Map<label>& mshToFoam,
370 cellShapeList& cells,
372 labelList& patchToPhys,
373 List<DynamicList<face> >& patchFaces,
375 labelList& zoneToPhys,
376 List<DynamicList<label> >& zoneCells
379 Info<< "Starting to read cells at line " << inFile.lineNumber() << endl;
381 const cellModel& hex = *(cellModeller::lookup("hex"));
382 const cellModel& prism = *(cellModeller::lookup("prism"));
383 const cellModel& pyr = *(cellModeller::lookup("pyr"));
384 const cellModel& tet = *(cellModeller::lookup("tet"));
388 labelList tetPoints(4);
389 labelList pyrPoints(5);
390 labelList prismPoints(6);
391 labelList hexPoints(8);
395 inFile.getLine(line);
396 IStringStream lineStr(line);
401 Info<< "Cells to be read:" << nElems << endl << endl;
404 // Storage for all cells. Too big. Shrink later
405 cells.setSize(nElems);
414 // From gmsh physical region to Foam patch
415 Map<label> physToPatch;
417 // From gmsh physical region to Foam cellZone
418 Map<label> physToZone;
421 for (label elemI = 0; elemI < nElems; elemI++)
424 inFile.getLine(line);
425 IStringStream lineStr(line);
427 label elmNumber, elmType, regPhys;
431 lineStr >> elmNumber >> elmType;
436 label regElem, partition;
440 lineStr >> regPhys >> regElem >> partition;
444 lineStr >> regPhys >> regElem;
449 for (label i = 0; i < nTags; i++)
458 label regElem, nNodes;
459 lineStr >> elmNumber >> elmType >> regPhys >> regElem >> nNodes;
462 // regPhys on surface elements is region number.
464 if (elmType == MSHTRI)
466 lineStr >> triPoints[0] >> triPoints[1] >> triPoints[2];
468 renumber(mshToFoam, triPoints);
470 Map<label>::iterator regFnd = physToPatch.find(regPhys);
473 if (regFnd == physToPatch.end())
475 // New region. Allocate patch for it.
476 patchI = patchFaces.size();
478 patchFaces.setSize(patchI + 1);
479 patchToPhys.setSize(patchI + 1);
481 Info<< "Mapping region " << regPhys << " to Foam patch "
483 physToPatch.insert(regPhys, patchI);
484 patchToPhys[patchI] = regPhys;
488 // Existing patch for region
492 // Add triangle to correct patchFaces.
493 patchFaces[patchI].append(triPoints);
495 else if (elmType == MSHQUAD)
498 >> quadPoints[0] >> quadPoints[1] >> quadPoints[2]
501 renumber(mshToFoam, quadPoints);
503 Map<label>::iterator regFnd = physToPatch.find(regPhys);
506 if (regFnd == physToPatch.end())
508 // New region. Allocate patch for it.
509 patchI = patchFaces.size();
511 patchFaces.setSize(patchI + 1);
512 patchToPhys.setSize(patchI + 1);
514 Info<< "Mapping region " << regPhys << " to Foam patch "
516 physToPatch.insert(regPhys, patchI);
517 patchToPhys[patchI] = regPhys;
521 // Existing patch for region
525 // Add quad to correct patchFaces.
526 patchFaces[patchI].append(quadPoints);
528 else if (elmType == MSHTET)
540 >> tetPoints[0] >> tetPoints[1] >> tetPoints[2]
543 renumber(mshToFoam, tetPoints);
545 cells[cellI++] = cellShape(tet, tetPoints);
549 else if (elmType == MSHPYR)
561 >> pyrPoints[0] >> pyrPoints[1] >> pyrPoints[2]
562 >> pyrPoints[3] >> pyrPoints[4];
564 renumber(mshToFoam, pyrPoints);
566 cells[cellI++] = cellShape(pyr, pyrPoints);
570 else if (elmType == MSHPRISM)
582 >> prismPoints[0] >> prismPoints[1] >> prismPoints[2]
583 >> prismPoints[3] >> prismPoints[4] >> prismPoints[5];
585 renumber(mshToFoam, prismPoints);
587 cells[cellI] = cellShape(prism, prismPoints);
589 const cellShape& cell = cells[cellI];
591 if (!keepOrientation && !correctOrientation(points, cell))
593 Info<< "Inverting prism " << cellI << endl;
595 prismPoints[0] = cell[0];
596 prismPoints[1] = cell[2];
597 prismPoints[2] = cell[1];
598 prismPoints[3] = cell[3];
599 prismPoints[4] = cell[4];
600 prismPoints[5] = cell[5];
602 cells[cellI] = cellShape(prism, prismPoints);
609 else if (elmType == MSHHEX)
621 >> hexPoints[0] >> hexPoints[1]
622 >> hexPoints[2] >> hexPoints[3]
623 >> hexPoints[4] >> hexPoints[5]
624 >> hexPoints[6] >> hexPoints[7];
626 renumber(mshToFoam, hexPoints);
628 cells[cellI] = cellShape(hex, hexPoints);
630 const cellShape& cell = cells[cellI];
632 if (!keepOrientation && !correctOrientation(points, cell))
634 Info<< "Inverting hex " << cellI << endl;
636 hexPoints[0] = cell[4];
637 hexPoints[1] = cell[5];
638 hexPoints[2] = cell[6];
639 hexPoints[3] = cell[7];
640 hexPoints[4] = cell[0];
641 hexPoints[5] = cell[1];
642 hexPoints[6] = cell[2];
643 hexPoints[7] = cell[3];
645 cells[cellI] = cellShape(hex, hexPoints);
654 Info<< "Unhandled element " << elmType << " at line "
655 << inFile.lineNumber() << endl;
660 inFile.getLine(line);
661 IStringStream tagStr(line);
664 if (tag != "$ENDELM" && tag != "$EndElements")
666 FatalErrorIn("readCells(..)")
667 << "Did not find $ENDELM tag on line "
668 << inFile.lineNumber() << exit(FatalError);
672 cells.setSize(cellI);
674 forAll(patchFaces, patchI)
676 patchFaces[patchI].shrink();
680 Info<< "Cells:" << endl
681 << " total:" << cells.size() << endl
682 << " hex :" << nHex << endl
683 << " prism:" << nPrism << endl
684 << " pyr :" << nPyr << endl
685 << " tet :" << nTet << endl
688 if (cells.size() == 0)
690 FatalErrorIn("readCells(..)")
691 << "No cells read from file " << inFile.name() << nl
692 << "Does your file specify any 3D elements (hex=" << MSHHEX
693 << ", prism=" << MSHPRISM << ", pyramid=" << MSHPYR
694 << ", tet=" << MSHTET << ")?" << nl
695 << "Perhaps you have not exported the 3D elements?"
699 Info<< "CellZones:" << nl
700 << "Zone\tSize" << endl;
702 forAll(zoneCells, zoneI)
704 zoneCells[zoneI].shrink();
706 const labelList& zCells = zoneCells[zoneI];
710 Info<< " " << zoneI << '\t' << zCells.size() << endl;
716 // Simplified version of the function from createPatch utility.
717 void removeEmptyPatches(polyMesh& mesh)
721 const polyBoundaryMesh& patches = mesh.boundaryMesh();
723 DynamicList<polyPatch*> nonEmptyPatches(patches.size());
727 const polyPatch& pp = patches[idx];
731 nonEmptyPatches.append
736 nonEmptyPatches.size(),
744 Info<< "Removing empty patch " << pp.name() << endl;
748 if (patches.size() != nonEmptyPatches.size())
750 nonEmptyPatches.shrink();
751 mesh.removeBoundary();
752 mesh.addPatches(nonEmptyPatches);
761 int main(int argc, char *argv[])
763 argList::noParallel();
764 argList::validArgs.append(".msh file");
765 argList::validOptions.insert("keepOrientation", "");
767 # include "setRootCase.H"
768 # include "createTime.H"
770 fileName mshName(args.additionalArgs()[0]);
772 bool keepOrientation = args.optionFound("keepOrientation");
774 // Storage for points
776 Map<label> mshToFoam;
778 // Storage for all cells.
781 // Map from patch to gmsh physical region
782 labelList patchToPhys;
783 // Storage for patch faces.
784 List<DynamicList<face> > patchFaces(0);
786 // Map from cellZone to gmsh physical region
787 labelList zoneToPhys;
788 // Storage for cell zones.
789 List<DynamicList<label> > zoneCells(0);
791 // Name per physical region
792 Map<word> physicalNames;
794 // Version 1 or 2 format
795 bool version2Format = false;
798 IFstream inFile(mshName);
800 while (inFile.good())
803 inFile.getLine(line);
804 IStringStream lineStr(line);
808 if (tag == "$MeshFormat")
810 Info<< "Found $MeshFormat tag; assuming version 2 file format."
812 version2Format = true;
814 if (!skipSection(inFile))
819 else if (tag == "$PhysicalNames")
821 readPhysNames(inFile, physicalNames);
823 else if (tag == "$NOD" || tag == "$Nodes")
825 readPoints(inFile, points, mshToFoam);
827 else if (tag == "$ELM" || tag == "$Elements")
845 Info<< "Skipping tag " << tag << " at line "
846 << inFile.lineNumber()
849 if (!skipSection(inFile))
857 label nValidCellZones = 0;
859 forAll(zoneCells, zoneI)
861 if (zoneCells[zoneI].size())
868 // Problem is that the orientation of the patchFaces does not have to
869 // be consistent with the outwards orientation of the mesh faces. So
870 // we have to construct the mesh in two stages:
871 // 1. define mesh with all boundary faces in one patch
872 // 2. use the read patchFaces to find the corresponding boundary face
876 // Create correct number of patches
877 // (but without any faces in it)
878 faceListList boundaryFaces(patchFaces.size());
880 wordList boundaryPatchNames(boundaryFaces.size());
882 forAll(boundaryPatchNames, patchI)
884 label physReg = patchToPhys[patchI];
886 Map<word>::const_iterator iter = physicalNames.find(physReg);
888 if (iter != physicalNames.end())
890 boundaryPatchNames[patchI] = iter();
894 boundaryPatchNames[patchI] = word("patch") + name(patchI);
896 Info<< "Patch " << patchI << " gets name "
897 << boundaryPatchNames[patchI] << endl;
901 wordList boundaryPatchTypes(boundaryFaces.size(), polyPatch::typeName);
902 word defaultFacesName = "defaultFaces";
903 word defaultFacesType = polyPatch::typeName;
904 wordList boundaryPatchPhysicalTypes
906 boundaryFaces.size(),
914 polyMesh::defaultRegion,
925 boundaryPatchPhysicalTypes
928 repatchPolyTopoChanger repatcher(mesh);
930 // Now use the patchFaces to patch up the outside faces of the mesh.
932 // Get the patch for all the outside faces (= default patch added as last)
933 const polyPatch& pp = mesh.boundaryMesh()[mesh.boundaryMesh().size()-1];
935 // Storage for faceZones.
936 List<DynamicList<label> > zoneFaces(patchFaces.size());
939 // Go through all the patchFaces and find corresponding face in pp.
940 forAll(patchFaces, patchI)
942 const DynamicList<face>& pFaces = patchFaces[patchI];
944 Info<< "Finding faces of patch " << patchI << endl;
948 const face& f = pFaces[i];
950 // Find face in pp using all vertices of f.
951 label patchFaceI = findFace(pp, f);
953 if (patchFaceI != -1)
955 label meshFaceI = pp.start() + patchFaceI;
957 repatcher.changePatchID(meshFaceI, patchI);
961 // Maybe internal face? If so add to faceZone with same index
962 // - might be useful.
963 label meshFaceI = findInternalFace(mesh, f);
967 zoneFaces[patchI].append(meshFaceI);
971 WarningIn(args.executable())
972 << "Could not match gmsh face " << f
973 << " to any of the interior or exterior faces"
974 << " that share the same 0th point" << endl;
982 label nValidFaceZones = 0;
984 Info<< "FaceZones:" << nl
985 << "Zone\tSize" << endl;
987 forAll(zoneFaces, zoneI)
989 zoneFaces[zoneI].shrink();
991 const labelList& zFaces = zoneFaces[zoneI];
997 Info<< " " << zoneI << '\t' << zFaces.size() << endl;
1003 //Get polyMesh to write to constant
1004 runTime.setTime(instant(runTime.constant()), 0);
1006 repatcher.repatch();
1011 // Construct and add the zones. Note that cell ordering does not change
1012 // because of repatch() and neither does internal faces so we can
1013 // use the zoneCells/zoneFaces as is.
1015 if (nValidCellZones > 0)
1017 cz.setSize(nValidCellZones);
1019 nValidCellZones = 0;
1021 forAll(zoneCells, zoneI)
1023 if (zoneCells[zoneI].size())
1025 label physReg = zoneToPhys[zoneI];
1027 Map<word>::const_iterator iter = physicalNames.find(physReg);
1029 word zoneName = "cellZone_" + name(zoneI);
1030 if (iter != physicalNames.end())
1035 Info<< "Writing zone " << zoneI << " to cellZone "
1036 << zoneName << " and cellSet"
1039 cellSet cset(mesh, zoneName, labelHashSet(zoneCells[zoneI]));
1042 cz[nValidCellZones] = new cellZone
1054 if (nValidFaceZones > 0)
1056 fz.setSize(nValidFaceZones);
1058 nValidFaceZones = 0;
1060 forAll(zoneFaces, zoneI)
1062 if (zoneFaces[zoneI].size())
1064 label physReg = zoneToPhys[zoneI];
1066 Map<word>::const_iterator iter = physicalNames.find(physReg);
1068 word zoneName = "faceZone_" + name(zoneI);
1069 if (iter != physicalNames.end())
1074 Info<< "Writing zone " << zoneI << " to faceZone "
1075 << zoneName << " and faceSet"
1078 faceSet fset(mesh, zoneName, labelHashSet(zoneFaces[zoneI]));
1081 fz[nValidFaceZones] = new faceZone
1085 boolList(zoneFaces[zoneI].size(), true),
1094 if (cz.size() || fz.size())
1096 mesh.addZones(List<pointZone*>(0), fz, cz);
1099 removeEmptyPatches(mesh);
1103 Info<< "End\n" << endl;
1109 // ************************************************************************* //