1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright (C) 1991-2009 OpenCFD Ltd.
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
26 Reads .msh file as written by Gmsh.
28 Needs surface elements on mesh to be present and aligned with outside faces
29 of the mesh. I.e. if the mesh is hexes, the outside faces need to be quads
31 Note: There is something seriously wrong with the ordering written in the
32 .msh file. Normal operation is to check the ordering and invert prisms
33 and hexes if found to be wrong way round.
34 Use the -keepOrientation to keep the raw information.
36 Note: The code now uses the element (cell,face) physical region id number
37 to create cell zones and faces zones (similar to
38 fluentMeshWithInternalFaces).
40 A use of the cell zone information, is for field initialization with the
41 "setFields" utility. see the classes: topoSetSource, zoneToCell.
42 \*---------------------------------------------------------------------------*/
49 #include "cellModeller.H"
50 #include "repatchPolyTopoChanger.H"
56 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
58 // Element type numbers
59 static label MSHTRI = 2;
60 static label MSHQUAD = 3;
61 static label MSHTET = 4;
62 static label MSHPYR = 7;
63 static label MSHPRISM = 6;
64 static label MSHHEX = 5;
67 // Skips till end of section. Returns false if end of file.
68 bool skipSection(IFstream& inFile)
80 while (line.size() < 4 || line.substr(0, 4) != "$End");
88 const Map<label>& mshToFoam,
92 forAll(labels, labelI)
94 labels[labelI] = mshToFoam[labels[labelI]];
99 // Find face in pp which uses all vertices in meshF (in mesh point labels)
100 label findFace(const primitivePatch& pp, const labelList& meshF)
102 const Map<label>& meshPointMap = pp.meshPointMap();
104 // meshF[0] in pp labels.
105 if (!meshPointMap.found(meshF[0]))
107 Warning<< "Not using gmsh face " << meshF
108 << " since zero vertex is not on boundary of polyMesh" << endl;
112 // Find faces using first point
113 const labelList& pFaces = pp.pointFaces()[meshPointMap[meshF[0]]];
115 // Go through all these faces and check if there is one which uses all of
116 // meshF vertices (in any order ;-)
119 label faceI = pFaces[i];
121 const face& f = pp[faceI];
123 // Count uses of vertices of meshF for f
128 if (findIndex(meshF, f[fp]) != -1)
134 if (nMatched == meshF.size())
144 // Same but find internal face. Expensive addressing.
145 label findInternalFace(const primitiveMesh& mesh, const labelList& meshF)
147 const labelList& pFaces = mesh.pointFaces()[meshF[0]];
151 label faceI = pFaces[i];
153 const face& f = mesh.faces()[faceI];
155 // Count uses of vertices of meshF for f
160 if (findIndex(meshF, f[fp]) != -1)
166 if (nMatched == meshF.size())
175 // Determine whether cell is inside-out by checking for any wrong-oriented
177 bool correctOrientation(const pointField& points, const cellShape& shape)
179 // Get centre of shape.
180 point cc(shape.centre(points));
182 // Get outwards pointing faces.
183 faceList faces(shape.faces());
187 const face& f = faces[i];
189 vector n(f.normal(points));
191 // Check if vector from any point on face to cc points outwards
192 if (((points[f[0]] - cc) & n) < 0)
194 // Incorrectly oriented
207 Map<label>& physToZone,
209 labelList& zoneToPhys,
210 List<DynamicList<label> >& zoneCells
213 Map<label>::const_iterator zoneFnd = physToZone.find(regPhys);
215 if (zoneFnd == physToZone.end())
217 // New region. Allocate zone for it.
218 label zoneI = zoneCells.size();
219 zoneCells.setSize(zoneI+1);
220 zoneToPhys.setSize(zoneI+1);
222 Info<< "Mapping region " << regPhys << " to Foam cellZone "
224 physToZone.insert(regPhys, zoneI);
226 zoneToPhys[zoneI] = regPhys;
227 zoneCells[zoneI].append(cellI);
231 // Existing zone for region
232 zoneCells[zoneFnd()].append(cellI);
238 scalar readMeshFormat(IFstream& inFile)
240 Info<< "Starting to read mesh format at line " << inFile.lineNumber() << endl;
243 inFile.getLine(line);
244 IStringStream lineStr(line);
247 label asciiFlag, nBytes;
248 lineStr >> version >> asciiFlag >> nBytes;
250 Info<< "Read format version " << version << " ascii " << asciiFlag << endl;
254 FatalIOErrorIn("readMeshFormat(IFstream&)", inFile)
255 << "Can only read ascii msh files."
256 << exit(FatalIOError);
259 inFile.getLine(line);
260 IStringStream tagStr(line);
263 if (tag != "$EndMeshFormat")
265 FatalIOErrorIn("readMeshFormat(IFstream&)", inFile)
266 << "Did not find $ENDNOD tag on line "
267 << inFile.lineNumber() << exit(FatalIOError);
275 // Reads points and map
276 void readPoints(IFstream& inFile, pointField& points, Map<label>& mshToFoam)
278 Info<< "Starting to read points at line " << inFile.lineNumber() << endl;
281 inFile.getLine(line);
282 IStringStream lineStr(line);
287 Info<< "Vertices to be read:" << nVerts << endl;
289 points.setSize(nVerts);
290 mshToFoam.resize(2*nVerts);
292 for (label pointI = 0; pointI < nVerts; pointI++)
295 scalar xVal, yVal, zVal;
298 inFile.getLine(line);
299 IStringStream lineStr(line);
301 lineStr >> mshLabel >> xVal >> yVal >> zVal;
303 point& pt = points[pointI];
309 mshToFoam.insert(mshLabel, pointI);
312 Info<< "Vertices read:" << mshToFoam.size() << endl;
314 inFile.getLine(line);
315 IStringStream tagStr(line);
318 if (tag != "$ENDNOD" && tag != "$EndNodes")
320 FatalIOErrorIn("readPoints(..)", inFile)
321 << "Did not find $ENDNOD tag on line "
322 << inFile.lineNumber() << exit(FatalIOError);
328 // Reads physical names
329 void readPhysNames(IFstream& inFile, Map<word>& physicalNames)
331 Info<< "Starting to read physical names at line " << inFile.lineNumber()
335 inFile.getLine(line);
336 IStringStream lineStr(line);
341 Info<< "Physical names:" << nNames << endl;
343 physicalNames.resize(nNames);
345 for (label i = 0; i < nNames; i++)
351 inFile.getLine(line);
352 IStringStream lineStr(line);
353 label nSpaces = lineStr.str().count(' ');
357 lineStr >> regionI >> regionName;
359 Info<< " " << regionI << '\t'
360 << string::validate<word>(regionName) << endl;
362 else if(nSpaces == 2)
364 // >= Gmsh2.4 physical types has tag in front.
366 lineStr >> physType >> regionI >> regionName;
369 Info<< " " << "Line " << regionI << '\t'
370 << string::validate<word>(regionName) << endl;
372 else if (physType == 2)
374 Info<< " " << "Surface " << regionI << '\t'
375 << string::validate<word>(regionName) << endl;
377 else if (physType == 3)
379 Info<< " " << "Volume " << regionI << '\t'
380 << string::validate<word>(regionName) << endl;
384 physicalNames.insert(regionI, string::validate<word>(regionName));
387 inFile.getLine(line);
388 IStringStream tagStr(line);
391 if (tag != "$EndPhysicalNames")
393 FatalIOErrorIn("readPhysicalNames(..)", inFile)
394 << "Did not find $EndPhysicalNames tag on line "
395 << inFile.lineNumber() << exit(FatalIOError);
401 // Reads cells and patch faces
404 const scalar versionFormat,
405 const bool keepOrientation,
406 const pointField& points,
407 const Map<label>& mshToFoam,
409 cellShapeList& cells,
411 labelList& patchToPhys,
412 List<DynamicList<face> >& patchFaces,
414 labelList& zoneToPhys,
415 List<DynamicList<label> >& zoneCells
418 Info<< "Starting to read cells at line " << inFile.lineNumber() << endl;
420 const cellModel& hex = *(cellModeller::lookup("hex"));
421 const cellModel& prism = *(cellModeller::lookup("prism"));
422 const cellModel& pyr = *(cellModeller::lookup("pyr"));
423 const cellModel& tet = *(cellModeller::lookup("tet"));
427 labelList tetPoints(4);
428 labelList pyrPoints(5);
429 labelList prismPoints(6);
430 labelList hexPoints(8);
434 inFile.getLine(line);
435 IStringStream lineStr(line);
440 Info<< "Cells to be read:" << nElems << endl << endl;
443 // Storage for all cells. Too big. Shrink later
444 cells.setSize(nElems);
453 // From gmsh physical region to Foam patch
454 Map<label> physToPatch;
456 // From gmsh physical region to Foam cellZone
457 Map<label> physToZone;
460 for (label elemI = 0; elemI < nElems; elemI++)
463 inFile.getLine(line);
464 IStringStream lineStr(line);
466 label elmNumber, elmType, regPhys;
468 if (versionFormat >= 2)
470 lineStr >> elmNumber >> elmType;
477 // Assume the first tag is the physical surface
479 for (label i = 1; i < nTags; i++)
488 label regElem, nNodes;
489 lineStr >> elmNumber >> elmType >> regPhys >> regElem >> nNodes;
492 // regPhys on surface elements is region number.
494 if (elmType == MSHTRI)
496 lineStr >> triPoints[0] >> triPoints[1] >> triPoints[2];
498 renumber(mshToFoam, triPoints);
500 Map<label>::iterator regFnd = physToPatch.find(regPhys);
503 if (regFnd == physToPatch.end())
505 // New region. Allocate patch for it.
506 patchI = patchFaces.size();
508 patchFaces.setSize(patchI + 1);
509 patchToPhys.setSize(patchI + 1);
511 Info<< "Mapping region " << regPhys << " to Foam patch "
513 physToPatch.insert(regPhys, patchI);
514 patchToPhys[patchI] = regPhys;
518 // Existing patch for region
522 // Add triangle to correct patchFaces.
523 patchFaces[patchI].append(triPoints);
525 else if (elmType == MSHQUAD)
528 >> quadPoints[0] >> quadPoints[1] >> quadPoints[2]
531 renumber(mshToFoam, quadPoints);
533 Map<label>::iterator regFnd = physToPatch.find(regPhys);
536 if (regFnd == physToPatch.end())
538 // New region. Allocate patch for it.
539 patchI = patchFaces.size();
541 patchFaces.setSize(patchI + 1);
542 patchToPhys.setSize(patchI + 1);
544 Info<< "Mapping region " << regPhys << " to Foam patch "
546 physToPatch.insert(regPhys, patchI);
547 patchToPhys[patchI] = regPhys;
551 // Existing patch for region
555 // Add quad to correct patchFaces.
556 patchFaces[patchI].append(quadPoints);
558 else if (elmType == MSHTET)
570 >> tetPoints[0] >> tetPoints[1] >> tetPoints[2]
573 renumber(mshToFoam, tetPoints);
575 cells[cellI++] = cellShape(tet, tetPoints);
579 else if (elmType == MSHPYR)
591 >> pyrPoints[0] >> pyrPoints[1] >> pyrPoints[2]
592 >> pyrPoints[3] >> pyrPoints[4];
594 renumber(mshToFoam, pyrPoints);
596 cells[cellI++] = cellShape(pyr, pyrPoints);
600 else if (elmType == MSHPRISM)
612 >> prismPoints[0] >> prismPoints[1] >> prismPoints[2]
613 >> prismPoints[3] >> prismPoints[4] >> prismPoints[5];
615 renumber(mshToFoam, prismPoints);
617 cells[cellI] = cellShape(prism, prismPoints);
619 const cellShape& cell = cells[cellI];
621 if (!keepOrientation && !correctOrientation(points, cell))
623 Info<< "Inverting prism " << cellI << endl;
625 prismPoints[0] = cell[0];
626 prismPoints[1] = cell[2];
627 prismPoints[2] = cell[1];
628 prismPoints[3] = cell[3];
629 prismPoints[4] = cell[4];
630 prismPoints[5] = cell[5];
632 cells[cellI] = cellShape(prism, prismPoints);
639 else if (elmType == MSHHEX)
651 >> hexPoints[0] >> hexPoints[1]
652 >> hexPoints[2] >> hexPoints[3]
653 >> hexPoints[4] >> hexPoints[5]
654 >> hexPoints[6] >> hexPoints[7];
656 renumber(mshToFoam, hexPoints);
658 cells[cellI] = cellShape(hex, hexPoints);
660 const cellShape& cell = cells[cellI];
662 if (!keepOrientation && !correctOrientation(points, cell))
664 Info<< "Inverting hex " << cellI << endl;
666 hexPoints[0] = cell[4];
667 hexPoints[1] = cell[5];
668 hexPoints[2] = cell[6];
669 hexPoints[3] = cell[7];
670 hexPoints[4] = cell[0];
671 hexPoints[5] = cell[1];
672 hexPoints[6] = cell[2];
673 hexPoints[7] = cell[3];
675 cells[cellI] = cellShape(hex, hexPoints);
684 Info<< "Unhandled element " << elmType << " at line "
685 << inFile.lineNumber() << endl;
690 inFile.getLine(line);
691 IStringStream tagStr(line);
694 if (tag != "$ENDELM" && tag != "$EndElements")
696 FatalIOErrorIn("readCells(..)", inFile)
697 << "Did not find $ENDELM tag on line "
698 << inFile.lineNumber() << exit(FatalIOError);
702 cells.setSize(cellI);
704 forAll(patchFaces, patchI)
706 patchFaces[patchI].shrink();
710 Info<< "Cells:" << endl
711 << " total:" << cells.size() << endl
712 << " hex :" << nHex << endl
713 << " prism:" << nPrism << endl
714 << " pyr :" << nPyr << endl
715 << " tet :" << nTet << endl
718 if (cells.size() == 0)
720 FatalIOErrorIn("readCells(..)", inFile)
721 << "No cells read from file " << inFile.name() << nl
722 << "Does your file specify any 3D elements (hex=" << MSHHEX
723 << ", prism=" << MSHPRISM << ", pyramid=" << MSHPYR
724 << ", tet=" << MSHTET << ")?" << nl
725 << "Perhaps you have not exported the 3D elements?"
726 << exit(FatalIOError);
729 Info<< "CellZones:" << nl
730 << "Zone\tSize" << endl;
732 forAll(zoneCells, zoneI)
734 zoneCells[zoneI].shrink();
736 const labelList& zCells = zoneCells[zoneI];
740 Info<< " " << zoneI << '\t' << zCells.size() << endl;
749 int main(int argc, char *argv[])
751 argList::noParallel();
752 argList::validArgs.append(".msh file");
753 argList::validOptions.insert("keepOrientation", "");
755 # include "setRootCase.H"
756 # include "createTime.H"
758 fileName mshName(args.additionalArgs()[0]);
760 bool keepOrientation = args.optionFound("keepOrientation");
762 // Storage for points
764 Map<label> mshToFoam;
766 // Storage for all cells.
769 // Map from patch to gmsh physical region
770 labelList patchToPhys;
771 // Storage for patch faces.
772 List<DynamicList<face> > patchFaces(0);
774 // Map from cellZone to gmsh physical region
775 labelList zoneToPhys;
776 // Storage for cell zones.
777 List<DynamicList<label> > zoneCells(0);
779 // Name per physical region
780 Map<word> physicalNames;
782 // Version 1 or 2 format
783 scalar versionFormat = 1;
786 IFstream inFile(mshName);
788 while (inFile.good())
791 inFile.getLine(line);
792 IStringStream lineStr(line);
796 if (tag == "$MeshFormat")
798 Info<< "Found $MeshFormat tag; assuming version 2 file format."
800 versionFormat = readMeshFormat(inFile);
802 else if (tag == "$PhysicalNames")
804 readPhysNames(inFile, physicalNames);
806 else if (tag == "$NOD" || tag == "$Nodes")
808 readPoints(inFile, points, mshToFoam);
810 else if (tag == "$ELM" || tag == "$Elements")
828 Info<< "Skipping tag " << tag << " at line "
829 << inFile.lineNumber()
832 if (!skipSection(inFile))
840 label nValidCellZones = 0;
842 forAll(zoneCells, zoneI)
844 if (zoneCells[zoneI].size())
851 // Problem is that the orientation of the patchFaces does not have to
852 // be consistent with the outwards orientation of the mesh faces. So
853 // we have to construct the mesh in two stages:
854 // 1. define mesh with all boundary faces in one patch
855 // 2. use the read patchFaces to find the corresponding boundary face
859 // Create correct number of patches
860 // (but without any faces in it)
861 faceListList boundaryFaces(patchFaces.size());
863 wordList boundaryPatchNames(boundaryFaces.size());
865 forAll(boundaryPatchNames, patchI)
867 label physReg = patchToPhys[patchI];
869 Map<word>::const_iterator iter = physicalNames.find(physReg);
871 if (iter != physicalNames.end())
873 boundaryPatchNames[patchI] = iter();
877 boundaryPatchNames[patchI] = word("patch") + name(patchI);
879 Info<< "Patch " << patchI << " gets name "
880 << boundaryPatchNames[patchI] << endl;
884 wordList boundaryPatchTypes(boundaryFaces.size(), polyPatch::typeName);
885 word defaultFacesName = "defaultFaces";
886 word defaultFacesType = polyPatch::typeName;
887 wordList boundaryPatchPhysicalTypes
889 boundaryFaces.size(),
897 polyMesh::defaultRegion,
908 boundaryPatchPhysicalTypes
911 repatchPolyTopoChanger repatcher(mesh);
913 // Now use the patchFaces to patch up the outside faces of the mesh.
915 // Get the patch for all the outside faces (= default patch added as last)
916 const polyPatch& pp = mesh.boundaryMesh()[mesh.boundaryMesh().size()-1];
918 // Storage for faceZones.
919 List<DynamicList<label> > zoneFaces(patchFaces.size());
922 // Go through all the patchFaces and find corresponding face in pp.
923 forAll(patchFaces, patchI)
925 const DynamicList<face>& pFaces = patchFaces[patchI];
927 Info<< "Finding faces of patch " << patchI << endl;
931 const face& f = pFaces[i];
933 // Find face in pp using all vertices of f.
934 label patchFaceI = findFace(pp, f);
936 if (patchFaceI != -1)
938 label meshFaceI = pp.start() + patchFaceI;
940 repatcher.changePatchID(meshFaceI, patchI);
944 // Maybe internal face? If so add to faceZone with same index
945 // - might be useful.
946 label meshFaceI = findInternalFace(mesh, f);
950 zoneFaces[patchI].append(meshFaceI);
954 WarningIn(args.executable())
955 << "Could not match gmsh face " << f
956 << " to any of the interior or exterior faces"
957 << " that share the same 0th point" << endl;
965 label nValidFaceZones = 0;
967 Info<< "FaceZones:" << nl
968 << "Zone\tSize" << endl;
970 forAll(zoneFaces, zoneI)
972 zoneFaces[zoneI].shrink();
974 const labelList& zFaces = zoneFaces[zoneI];
980 Info<< " " << zoneI << '\t' << zFaces.size() << endl;
986 //Get polyMesh to write to constant
987 runTime.setTime(instant(runTime.constant()), 0);
994 // Construct and add the zones. Note that cell ordering does not change
995 // because of repatch() and neither does internal faces so we can
996 // use the zoneCells/zoneFaces as is.
998 if (nValidCellZones > 0)
1000 cz.setSize(nValidCellZones);
1002 nValidCellZones = 0;
1004 forAll(zoneCells, zoneI)
1006 if (zoneCells[zoneI].size())
1008 label physReg = zoneToPhys[zoneI];
1010 Map<word>::const_iterator iter = physicalNames.find(physReg);
1012 word zoneName = "cellZone_" + name(zoneI);
1013 if (iter != physicalNames.end())
1018 Info<< "Writing zone " << zoneI << " to cellZone "
1019 << zoneName << " and cellSet"
1022 cellSet cset(mesh, zoneName, zoneCells[zoneI]);
1025 cz[nValidCellZones] = new cellZone
1037 if (nValidFaceZones > 0)
1039 fz.setSize(nValidFaceZones);
1041 nValidFaceZones = 0;
1043 forAll(zoneFaces, zoneI)
1045 if (zoneFaces[zoneI].size())
1047 label physReg = zoneToPhys[zoneI];
1049 Map<word>::const_iterator iter = physicalNames.find(physReg);
1051 word zoneName = "faceZone_" + name(zoneI);
1052 if (iter != physicalNames.end())
1057 Info<< "Writing zone " << zoneI << " to faceZone "
1058 << zoneName << " and faceSet"
1061 faceSet fset(mesh, zoneName, zoneFaces[zoneI]);
1064 fz[nValidFaceZones] = new faceZone
1068 boolList(zoneFaces[zoneI].size(), true),
1077 if (cz.size() || fz.size())
1079 mesh.addZones(List<pointZone*>(0), fz, cz);
1084 Info<< "End\n" << endl;
1090 // ************************************************************************* //