1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright (C) 2011 OpenFOAM Foundation
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
13 the Free Software Foundation, either version 3 of the License, or
14 (at your 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, 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);
237 scalar readMeshFormat(IFstream& inFile)
239 Info<< "Starting to read mesh format at line "
240 << inFile.lineNumber()
244 inFile.getLine(line);
245 IStringStream lineStr(line);
248 label asciiFlag, nBytes;
249 lineStr >> version >> asciiFlag >> nBytes;
251 Info<< "Read format version " << version << " ascii " << asciiFlag << endl;
255 FatalIOErrorIn("readMeshFormat(IFstream&)", inFile)
256 << "Can only read ascii msh files."
257 << exit(FatalIOError);
260 inFile.getLine(line);
261 IStringStream tagStr(line);
264 if (tag != "$EndMeshFormat")
266 FatalIOErrorIn("readMeshFormat(IFstream&)", inFile)
267 << "Did not find $ENDNOD tag on line "
268 << inFile.lineNumber() << exit(FatalIOError);
276 // Reads points and map
277 void readPoints(IFstream& inFile, pointField& points, Map<label>& mshToFoam)
279 Info<< "Starting to read points at line " << inFile.lineNumber() << endl;
282 inFile.getLine(line);
283 IStringStream lineStr(line);
288 Info<< "Vertices to be read:" << nVerts << endl;
290 points.setSize(nVerts);
291 mshToFoam.resize(2*nVerts);
293 for (label pointI = 0; pointI < nVerts; pointI++)
296 scalar xVal, yVal, zVal;
299 inFile.getLine(line);
300 IStringStream lineStr(line);
302 lineStr >> mshLabel >> xVal >> yVal >> zVal;
304 point& pt = points[pointI];
310 mshToFoam.insert(mshLabel, pointI);
313 Info<< "Vertices read:" << mshToFoam.size() << endl;
315 inFile.getLine(line);
316 IStringStream tagStr(line);
319 if (tag != "$ENDNOD" && tag != "$EndNodes")
321 FatalIOErrorIn("readPoints(..)", inFile)
322 << "Did not find $ENDNOD tag on line "
323 << inFile.lineNumber() << exit(FatalIOError);
329 // Reads physical names
330 void readPhysNames(IFstream& inFile, Map<word>& physicalNames)
332 Info<< "Starting to read physical names at line " << inFile.lineNumber()
336 inFile.getLine(line);
337 IStringStream lineStr(line);
342 Info<< "Physical names:" << nNames << endl;
344 physicalNames.resize(nNames);
346 for (label i = 0; i < nNames; i++)
352 inFile.getLine(line);
353 IStringStream lineStr(line);
354 label nSpaces = lineStr.str().count(' ');
358 lineStr >> regionI >> regionName;
360 Info<< " " << regionI << '\t'
361 << string::validate<word>(regionName) << endl;
363 else if (nSpaces == 2)
365 // >= Gmsh2.4 physical types has tag in front.
367 lineStr >> physType >> regionI >> regionName;
370 Info<< " " << "Line " << regionI << '\t'
371 << string::validate<word>(regionName) << endl;
373 else if (physType == 2)
375 Info<< " " << "Surface " << regionI << '\t'
376 << string::validate<word>(regionName) << endl;
378 else if (physType == 3)
380 Info<< " " << "Volume " << regionI << '\t'
381 << string::validate<word>(regionName) << endl;
385 physicalNames.insert(regionI, string::validate<word>(regionName));
388 inFile.getLine(line);
389 IStringStream tagStr(line);
392 if (tag != "$EndPhysicalNames")
394 FatalIOErrorIn("readPhysicalNames(..)", inFile)
395 << "Did not find $EndPhysicalNames tag on line "
396 << inFile.lineNumber() << exit(FatalIOError);
402 // Reads cells and patch faces
405 const scalar versionFormat,
406 const bool keepOrientation,
407 const pointField& points,
408 const Map<label>& mshToFoam,
410 cellShapeList& cells,
412 labelList& patchToPhys,
413 List<DynamicList<face> >& patchFaces,
415 labelList& zoneToPhys,
416 List<DynamicList<label> >& zoneCells
419 Info<< "Starting to read cells at line " << inFile.lineNumber() << endl;
421 const cellModel& hex = *(cellModeller::lookup("hex"));
422 const cellModel& prism = *(cellModeller::lookup("prism"));
423 const cellModel& pyr = *(cellModeller::lookup("pyr"));
424 const cellModel& tet = *(cellModeller::lookup("tet"));
428 labelList tetPoints(4);
429 labelList pyrPoints(5);
430 labelList prismPoints(6);
431 labelList hexPoints(8);
435 inFile.getLine(line);
436 IStringStream lineStr(line);
441 Info<< "Cells to be read:" << nElems << endl << endl;
444 // Storage for all cells. Too big. Shrink later
445 cells.setSize(nElems);
454 // From gmsh physical region to Foam patch
455 Map<label> physToPatch;
457 // From gmsh physical region to Foam cellZone
458 Map<label> physToZone;
461 for (label elemI = 0; elemI < nElems; elemI++)
464 inFile.getLine(line);
465 IStringStream lineStr(line);
467 label elmNumber, elmType, regPhys;
469 if (versionFormat >= 2)
471 lineStr >> elmNumber >> elmType;
478 // Assume the first tag is the physical surface
480 for (label i = 1; i < nTags; i++)
489 label regElem, nNodes;
490 lineStr >> elmNumber >> elmType >> regPhys >> regElem >> nNodes;
493 // regPhys on surface elements is region number.
495 if (elmType == MSHTRI)
497 lineStr >> triPoints[0] >> triPoints[1] >> triPoints[2];
499 renumber(mshToFoam, triPoints);
501 Map<label>::iterator regFnd = physToPatch.find(regPhys);
504 if (regFnd == physToPatch.end())
506 // New region. Allocate patch for it.
507 patchI = patchFaces.size();
509 patchFaces.setSize(patchI + 1);
510 patchToPhys.setSize(patchI + 1);
512 Info<< "Mapping region " << regPhys << " to Foam patch "
514 physToPatch.insert(regPhys, patchI);
515 patchToPhys[patchI] = regPhys;
519 // Existing patch for region
523 // Add triangle to correct patchFaces.
524 patchFaces[patchI].append(triPoints);
526 else if (elmType == MSHQUAD)
529 >> quadPoints[0] >> quadPoints[1] >> quadPoints[2]
532 renumber(mshToFoam, quadPoints);
534 Map<label>::iterator regFnd = physToPatch.find(regPhys);
537 if (regFnd == physToPatch.end())
539 // New region. Allocate patch for it.
540 patchI = patchFaces.size();
542 patchFaces.setSize(patchI + 1);
543 patchToPhys.setSize(patchI + 1);
545 Info<< "Mapping region " << regPhys << " to Foam patch "
547 physToPatch.insert(regPhys, patchI);
548 patchToPhys[patchI] = regPhys;
552 // Existing patch for region
556 // Add quad to correct patchFaces.
557 patchFaces[patchI].append(quadPoints);
559 else if (elmType == MSHTET)
571 >> tetPoints[0] >> tetPoints[1] >> tetPoints[2]
574 renumber(mshToFoam, tetPoints);
576 cells[cellI++] = cellShape(tet, tetPoints);
580 else if (elmType == MSHPYR)
592 >> pyrPoints[0] >> pyrPoints[1] >> pyrPoints[2]
593 >> pyrPoints[3] >> pyrPoints[4];
595 renumber(mshToFoam, pyrPoints);
597 cells[cellI++] = cellShape(pyr, pyrPoints);
601 else if (elmType == MSHPRISM)
613 >> prismPoints[0] >> prismPoints[1] >> prismPoints[2]
614 >> prismPoints[3] >> prismPoints[4] >> prismPoints[5];
616 renumber(mshToFoam, prismPoints);
618 cells[cellI] = cellShape(prism, prismPoints);
620 const cellShape& cell = cells[cellI];
622 if (!keepOrientation && !correctOrientation(points, cell))
624 Info<< "Inverting prism " << cellI << endl;
626 prismPoints[0] = cell[0];
627 prismPoints[1] = cell[2];
628 prismPoints[2] = cell[1];
629 prismPoints[3] = cell[3];
630 prismPoints[4] = cell[4];
631 prismPoints[5] = cell[5];
633 cells[cellI] = cellShape(prism, prismPoints);
640 else if (elmType == MSHHEX)
652 >> hexPoints[0] >> hexPoints[1]
653 >> hexPoints[2] >> hexPoints[3]
654 >> hexPoints[4] >> hexPoints[5]
655 >> hexPoints[6] >> hexPoints[7];
657 renumber(mshToFoam, hexPoints);
659 cells[cellI] = cellShape(hex, hexPoints);
661 const cellShape& cell = cells[cellI];
663 if (!keepOrientation && !correctOrientation(points, cell))
665 Info<< "Inverting hex " << cellI << endl;
667 hexPoints[0] = cell[4];
668 hexPoints[1] = cell[5];
669 hexPoints[2] = cell[6];
670 hexPoints[3] = cell[7];
671 hexPoints[4] = cell[0];
672 hexPoints[5] = cell[1];
673 hexPoints[6] = cell[2];
674 hexPoints[7] = cell[3];
676 cells[cellI] = cellShape(hex, hexPoints);
685 Info<< "Unhandled element " << elmType << " at line "
686 << inFile.lineNumber() << endl;
691 inFile.getLine(line);
692 IStringStream tagStr(line);
695 if (tag != "$ENDELM" && tag != "$EndElements")
697 FatalIOErrorIn("readCells(..)", inFile)
698 << "Did not find $ENDELM tag on line "
699 << inFile.lineNumber() << exit(FatalIOError);
703 cells.setSize(cellI);
705 forAll(patchFaces, patchI)
707 patchFaces[patchI].shrink();
711 Info<< "Cells:" << endl
712 << " total:" << cells.size() << endl
713 << " hex :" << nHex << endl
714 << " prism:" << nPrism << endl
715 << " pyr :" << nPyr << endl
716 << " tet :" << nTet << endl
719 if (cells.size() == 0)
721 FatalIOErrorIn("readCells(..)", inFile)
722 << "No cells read from file " << inFile.name() << nl
723 << "Does your file specify any 3D elements (hex=" << MSHHEX
724 << ", prism=" << MSHPRISM << ", pyramid=" << MSHPYR
725 << ", tet=" << MSHTET << ")?" << nl
726 << "Perhaps you have not exported the 3D elements?"
727 << exit(FatalIOError);
730 Info<< "CellZones:" << nl
731 << "Zone\tSize" << endl;
733 forAll(zoneCells, zoneI)
735 zoneCells[zoneI].shrink();
737 const labelList& zCells = zoneCells[zoneI];
741 Info<< " " << zoneI << '\t' << zCells.size() << endl;
750 int main(int argc, char *argv[])
752 argList::noParallel();
753 argList::validArgs.append(".msh file");
754 argList::addBoolOption
757 "retain raw orientation for prisms/hexs"
760 # include "addRegionOption.H"
762 # include "setRootCase.H"
763 # include "createTime.H"
765 Foam::word regionName;
767 if (args.optionReadIfPresent("region", regionName))
770 << "Creating polyMesh for region " << regionName << endl;
774 regionName = Foam::polyMesh::defaultRegion;
777 const bool keepOrientation = args.optionFound("keepOrientation");
778 IFstream inFile(args[1]);
780 // Storage for points
782 Map<label> mshToFoam;
784 // Storage for all cells.
787 // Map from patch to gmsh physical region
788 labelList patchToPhys;
789 // Storage for patch faces.
790 List<DynamicList<face> > patchFaces(0);
792 // Map from cellZone to gmsh physical region
793 labelList zoneToPhys;
794 // Storage for cell zones.
795 List<DynamicList<label> > zoneCells(0);
797 // Name per physical region
798 Map<word> physicalNames;
800 // Version 1 or 2 format
801 scalar versionFormat = 1;
804 while (inFile.good())
807 inFile.getLine(line);
808 IStringStream lineStr(line);
812 if (tag == "$MeshFormat")
814 versionFormat = readMeshFormat(inFile);
816 else if (tag == "$PhysicalNames")
818 readPhysNames(inFile, physicalNames);
820 else if (tag == "$NOD" || tag == "$Nodes")
822 readPoints(inFile, points, mshToFoam);
824 else if (tag == "$ELM" || tag == "$Elements")
842 Info<< "Skipping tag " << tag << " at line "
843 << inFile.lineNumber()
846 if (!skipSection(inFile))
854 label nValidCellZones = 0;
856 forAll(zoneCells, zoneI)
858 if (zoneCells[zoneI].size())
865 // Problem is that the orientation of the patchFaces does not have to
866 // be consistent with the outwards orientation of the mesh faces. So
867 // we have to construct the mesh in two stages:
868 // 1. define mesh with all boundary faces in one patch
869 // 2. use the read patchFaces to find the corresponding boundary face
873 // Create correct number of patches
874 // (but without any faces in it)
875 faceListList boundaryFaces(patchFaces.size());
877 wordList boundaryPatchNames(boundaryFaces.size());
879 forAll(boundaryPatchNames, patchI)
881 label physReg = patchToPhys[patchI];
883 Map<word>::const_iterator iter = physicalNames.find(physReg);
885 if (iter != physicalNames.end())
887 boundaryPatchNames[patchI] = iter();
891 boundaryPatchNames[patchI] = word("patch") + name(patchI);
893 Info<< "Patch " << patchI << " gets name "
894 << boundaryPatchNames[patchI] << endl;
898 wordList boundaryPatchTypes(boundaryFaces.size(), polyPatch::typeName);
899 word defaultFacesName = "defaultFaces";
900 word defaultFacesType = polyPatch::typeName;
901 wordList boundaryPatchPhysicalTypes
903 boundaryFaces.size(),
922 boundaryPatchPhysicalTypes
925 repatchPolyTopoChanger repatcher(mesh);
927 // Now use the patchFaces to patch up the outside faces of the mesh.
929 // Get the patch for all the outside faces (= default patch added as last)
930 const polyPatch& pp = mesh.boundaryMesh().last();
932 // Storage for faceZones.
933 List<DynamicList<label> > zoneFaces(patchFaces.size());
936 // Go through all the patchFaces and find corresponding face in pp.
937 forAll(patchFaces, patchI)
939 const DynamicList<face>& pFaces = patchFaces[patchI];
941 Info<< "Finding faces of patch " << patchI << endl;
945 const face& f = pFaces[i];
947 // Find face in pp using all vertices of f.
948 label patchFaceI = findFace(pp, f);
950 if (patchFaceI != -1)
952 label meshFaceI = pp.start() + patchFaceI;
954 repatcher.changePatchID(meshFaceI, patchI);
958 // Maybe internal face? If so add to faceZone with same index
959 // - might be useful.
960 label meshFaceI = findInternalFace(mesh, f);
964 zoneFaces[patchI].append(meshFaceI);
968 WarningIn(args.executable())
969 << "Could not match gmsh face " << f
970 << " to any of the interior or exterior faces"
971 << " that share the same 0th point" << endl;
979 label nValidFaceZones = 0;
981 Info<< "FaceZones:" << nl
982 << "Zone\tSize" << endl;
984 forAll(zoneFaces, zoneI)
986 zoneFaces[zoneI].shrink();
988 const labelList& zFaces = zoneFaces[zoneI];
994 Info<< " " << zoneI << '\t' << zFaces.size() << endl;
1000 //Get polyMesh to write to constant
1002 runTime.setTime(instant(runTime.constant()), 0);
1004 repatcher.repatch();
1009 // Construct and add the zones. Note that cell ordering does not change
1010 // because of repatch() and neither does internal faces so we can
1011 // use the zoneCells/zoneFaces as is.
1013 if (nValidCellZones > 0)
1015 cz.setSize(nValidCellZones);
1017 nValidCellZones = 0;
1019 forAll(zoneCells, zoneI)
1021 if (zoneCells[zoneI].size())
1023 label physReg = zoneToPhys[zoneI];
1025 Map<word>::const_iterator iter = physicalNames.find(physReg);
1027 word zoneName = "cellZone_" + name(zoneI);
1028 if (iter != physicalNames.end())
1033 Info<< "Writing zone " << zoneI << " to cellZone "
1034 << zoneName << " and cellSet"
1037 cellSet cset(mesh, zoneName, zoneCells[zoneI]);
1040 cz[nValidCellZones] = new cellZone
1052 if (nValidFaceZones > 0)
1054 fz.setSize(nValidFaceZones);
1056 nValidFaceZones = 0;
1058 forAll(zoneFaces, zoneI)
1060 if (zoneFaces[zoneI].size())
1062 label physReg = zoneToPhys[zoneI];
1064 Map<word>::const_iterator iter = physicalNames.find(physReg);
1066 word zoneName = "faceZone_" + name(zoneI);
1067 if (iter != physicalNames.end())
1072 Info<< "Writing zone " << zoneI << " to faceZone "
1073 << zoneName << " and faceSet"
1076 faceSet fset(mesh, zoneName, zoneFaces[zoneI]);
1079 fz[nValidFaceZones] = new faceZone
1083 boolList(zoneFaces[zoneI].size(), true),
1092 if (cz.size() || fz.size())
1094 mesh.addZones(List<pointZone*>(0), fz, cz);
1097 // Remove empty defaultFaces
1098 label defaultPatchID = mesh.boundaryMesh().findPatchID(defaultFacesName);
1099 if (mesh.boundaryMesh()[defaultPatchID].size() == 0)
1101 List<polyPatch*> newPatchPtrList((mesh.boundaryMesh().size() - 1));
1102 label newPatchI = 0;
1103 forAll(mesh.boundaryMesh(), patchI)
1105 if (patchI != defaultPatchID)
1107 const polyPatch& patch = mesh.boundaryMesh()[patchI];
1109 newPatchPtrList[newPatchI] = patch.clone
1111 mesh.boundaryMesh(),
1120 repatcher.changePatches(newPatchPtrList);
1125 Info<< "End\n" << endl;
1131 // ************************************************************************* //