FreeFOAM The Cross-Platform CFD Toolkit
Hosted by SourceForge:
Get FreeFOAM at SourceForge.net.
            Fast, secure and Free Open Source software downloads

tetgenToFoam.C

Go to the documentation of this file.
00001 /*---------------------------------------------------------------------------*\
00002   =========                 |
00003   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
00004    \\    /   O peration     |
00005     \\  /    A nd           | Copyright (C) 1991-2010 OpenCFD Ltd.
00006      \\/     M anipulation  |
00007 -------------------------------------------------------------------------------
00008 License
00009     This file is part of OpenFOAM.
00010 
00011     OpenFOAM is free software: you can redistribute it and/or modify it
00012     under the terms of the GNU General Public License as published by
00013     the Free Software Foundation, either version 3 of the License, or
00014     (at your option) any later version.
00015 
00016     OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
00017     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00018     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
00019     for more details.
00020 
00021     You should have received a copy of the GNU General Public License
00022     along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
00023 
00024 Application
00025     tetgenToFoam
00026 
00027 Description
00028     Converts .ele and .node and .face files, written by tetgen.
00029 
00030     Make sure to use add boundary attributes to the smesh file
00031     (5 fifth column in the element section)
00032     and run tetgen with -f option.
00033 
00034     Sample smesh file:
00035     @verbatim
00036         # cube.smesh -- A 10x10x10 cube
00037         8 3
00038         1   0 0 0
00039         2   0 10 0
00040         3   10 10 0
00041         4   10 0 0
00042         5   0 0 10
00043         6   0 10 10
00044         7   10 10 10
00045         8   10 0 10
00046         6 1                 # 1 for boundary info present
00047         4   1 2 3 4 11  # region number 11
00048         4   5 6 7 8 21  # region number 21
00049         4   1 2 6 5 3
00050         4   4 3 7 8 43
00051         4   1 5 8 4 5
00052         4   2 6 7 3 65
00053         0
00054         0
00055     @endverbatim
00056 
00057 NOTE:
00058     - for some reason boundary faces point inwards. I just reverse them
00059       always. Might use some geometric check instead.
00060     - marked faces might not actually be boundary faces of mesh.
00061       This is hopefully handled now by first constructing without boundaries
00062       and then reconstructing with boundary faces.
00063 
00064 Usage
00065 
00066     - tetgenToFoam [OPTIONS] <file prefix>
00067 
00068     @param <file prefix> \n
00069     @todo Detailed description of argument.
00070 
00071     @param -noFaceFile \n
00072     Ignore the face file.
00073 
00074     @param -case <dir>\n
00075     Case directory.
00076 
00077     @param -parallel \n
00078     Run in parallel.
00079 
00080     @param -help \n
00081     Display help message.
00082 
00083     @param -doc \n
00084     Display Doxygen API documentation page for this application.
00085 
00086     @param -srcDoc \n
00087     Display Doxygen source documentation page for this application.
00088 
00089 \*---------------------------------------------------------------------------*/
00090 
00091 #include <OpenFOAM/argList.H>
00092 #include <OpenFOAM/Time.H>
00093 #include <OpenFOAM/polyMesh.H>
00094 #include <OpenFOAM/IFstream.H>
00095 #include <OpenFOAM/cellModeller.H>
00096 
00097 using namespace Foam;
00098 
00099 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
00100 
00101 // Find label of face.
00102 label findFace(const primitiveMesh& mesh, const face& f)
00103 {
00104     const labelList& pFaces = mesh.pointFaces()[f[0]];
00105 
00106     forAll(pFaces, i)
00107     {
00108         label faceI = pFaces[i];
00109 
00110         if (mesh.faces()[faceI] == f)
00111         {
00112             return faceI;
00113         }
00114     }
00115 
00116     FatalErrorIn("findFace(const primitiveMesh&, const face&)")
00117         << "Cannot find face " << f << " in mesh." << abort(FatalError);
00118 
00119     return -1;
00120 }
00121 
00122 
00123 // Main program:
00124 
00125 int main(int argc, char *argv[])
00126 {
00127     argList::validArgs.append("file prefix");
00128     argList::validOptions.insert("noFaceFile", "");
00129 
00130 #   include <OpenFOAM/setRootCase.H>
00131 #   include <OpenFOAM/createTime.H>
00132 
00133 
00134     bool readFaceFile = !args.optionFound("noFaceFile");
00135 
00136     fileName prefix(args.additionalArgs()[0]);
00137 
00138     fileName nodeFile(prefix + ".node");
00139     fileName eleFile(prefix + ".ele");
00140     fileName faceFile(prefix + ".face");
00141 
00142     if (!readFaceFile)
00143     {
00144         Info<< "Files:" << endl
00145             << "    nodes : " << nodeFile << endl
00146             << "    elems : " << eleFile << endl
00147             << endl;
00148     }
00149     else
00150     {
00151         Info<< "Files:" << endl
00152             << "    nodes : " << nodeFile << endl
00153             << "    elems : " << eleFile << endl
00154             << "    faces : " << faceFile << endl
00155             << endl;
00156 
00157         Info<< "Reading .face file for boundary information" << nl << endl;
00158     }
00159 
00160     if (!isFile(nodeFile) || !isFile(eleFile))
00161     {
00162         FatalErrorIn(args.executable())
00163             << "Cannot read " << nodeFile << " or " << eleFile
00164             << exit(FatalError);
00165     }
00166 
00167     if (readFaceFile && !isFile(faceFile))
00168     {
00169         FatalErrorIn(args.executable())
00170             << "Cannot read " << faceFile << endl
00171             << "Did you run tetgen with -f option?" << endl
00172             << "If you don't want to read the .face file and thus not have"
00173             << " patches please\nrerun with the -noFaceFile option"
00174             << exit(FatalError);
00175     }
00176 
00177 
00178     IFstream nodeStream(nodeFile);
00179 
00180     //
00181     // Read nodes.
00182     //
00183 
00184     // Read header.
00185     string line;
00186 
00187     do
00188     {
00189         nodeStream.getLine(line);
00190     }
00191     while (line.size() && line[0] == '#');
00192 
00193     IStringStream nodeLine(line);
00194 
00195     label nNodes, nDims, nNodeAttr;
00196     bool hasRegion;
00197 
00198     nodeLine >> nNodes >> nDims >> nNodeAttr >> hasRegion;
00199 
00200 
00201     Info<< "Read .node header:" << endl
00202         << "    nodes     : " << nNodes << endl
00203         << "    nDims     : " << nDims << endl
00204         << "    nAttr     : " << nNodeAttr << endl
00205         << "    hasRegion : " << hasRegion << endl
00206         << endl;
00207 
00208     //
00209     // read points
00210     //
00211 
00212     pointField points(nNodes);
00213     Map<label> nodeToPoint(nNodes);
00214 
00215     {
00216         labelList pointIndex(nNodes);
00217 
00218         label pointI = 0;
00219 
00220         while (nodeStream.good())
00221         {
00222             nodeStream.getLine(line);
00223 
00224             if (line.size() && line[0] != '#')
00225             {
00226                 IStringStream nodeLine(line);
00227 
00228                 label nodeI;
00229                 scalar x, y, z;
00230                 label dummy;
00231 
00232                 nodeLine >> nodeI >> x >> y >> z;
00233 
00234                 for (label i = 0; i < nNodeAttr; i++)
00235                 {
00236                     nodeLine >> dummy;
00237                 }
00238 
00239                 if (hasRegion)
00240                 {
00241                     nodeLine >> dummy;
00242                 }
00243 
00244                 // Store point and node number.
00245                 points[pointI] = point(x, y, z);
00246                 nodeToPoint.insert(nodeI, pointI);
00247                 pointI++;
00248             }
00249         }
00250         if (pointI != nNodes)
00251         {
00252             FatalIOErrorIn(args.executable().c_str(), nodeStream)
00253                 << "Only " << pointI << " nodes present instead of " << nNodes
00254                 << " from header." << exit(FatalIOError);
00255         }
00256     }
00257 
00258     //
00259     // read elements
00260     //
00261 
00262     IFstream eleStream(eleFile);
00263 
00264     do
00265     {
00266         eleStream.getLine(line);
00267     }
00268     while (line.size() && line[0] == '#');
00269 
00270     IStringStream eleLine(line);
00271 
00272     label nTets, nPtsPerTet, nElemAttr;
00273 
00274     eleLine >> nTets >> nPtsPerTet >> nElemAttr;
00275 
00276 
00277     Info<< "Read .ele header:" << endl
00278         << "    tets         : " << nTets << endl
00279         << "    pointsPerTet : " << nPtsPerTet << endl
00280         << "    nAttr        : " << nElemAttr << endl
00281         << endl;
00282 
00283     if (nPtsPerTet != 4)
00284     {
00285         FatalIOErrorIn(args.executable().c_str(), eleStream)
00286             << "Cannot handle tets with "
00287             << nPtsPerTet << " points per tetrahedron in .ele file" << endl
00288             << "Can only handle tetrahedra with four points"
00289             << exit(FatalIOError);
00290     }
00291 
00292     if (nElemAttr != 0)
00293     {
00294         WarningIn(args.executable())
00295             << "Element attributes (third elemenent in .ele header)"
00296             << " not used" << endl;
00297     }
00298 
00299 
00300 
00301     const cellModel& tet = *(cellModeller::lookup("tet"));
00302 
00303     labelList tetPoints(4);
00304 
00305     cellShapeList cells(nTets);
00306     label cellI = 0;
00307 
00308     while (eleStream.good())
00309     {
00310         eleStream.getLine(line);
00311 
00312         if (line.size() && line[0] != '#')
00313         {
00314             IStringStream eleLine(line);
00315 
00316             label elemI;
00317             eleLine >> elemI;
00318 
00319             for (label i = 0; i < 4; i++)
00320             {
00321                 label nodeI;
00322                 eleLine >> nodeI;
00323                 tetPoints[i] = nodeToPoint[nodeI];
00324             }
00325 
00326             cells[cellI++] = cellShape(tet, tetPoints);
00327 
00328             // Skip attributes
00329             for (label i = 0; i < nElemAttr; i++)
00330             {
00331                 label dummy;
00332 
00333                 eleLine >> dummy;
00334             }
00335         }
00336     }
00337 
00338 
00339     //
00340     // Construct mesh with default boundary only
00341     //
00342 
00343     autoPtr<polyMesh> meshPtr
00344     (
00345         new polyMesh
00346         (
00347             IOobject
00348             (
00349                 polyMesh::defaultRegion,
00350                 runTime.constant(),
00351                 runTime
00352             ),
00353             xferCopy(points),
00354             cells,
00355             faceListList(0),
00356             wordList(0),    // boundaryPatchNames
00357             wordList(0),    // boundaryPatchTypes
00358             "defaultFaces",
00359             polyPatch::typeName,
00360             wordList(0)
00361         )
00362     );
00363     const polyMesh& mesh = meshPtr;
00364 
00365 
00366 
00367     if (readFaceFile)
00368     {
00369         label nPatches = 0;
00370 
00371         // List of Foam vertices per boundary face
00372         faceList boundaryFaces;
00373 
00374         // For each boundary faces the Foam patchID
00375         labelList boundaryPatch;
00376 
00377         //
00378         // read boundary faces
00379         //
00380 
00381         IFstream faceStream(faceFile);
00382 
00383         do
00384         {
00385             faceStream.getLine(line);
00386         }
00387         while (line.size() && line[0] == '#');
00388 
00389         IStringStream faceLine(line);
00390 
00391         label nFaces, nFaceAttr;
00392 
00393         faceLine >> nFaces >> nFaceAttr;
00394 
00395 
00396         Info<< "Read .face header:" << endl
00397             << "    faces : " << nFaces << endl
00398             << "    nAttr : " << nFaceAttr << endl
00399             << endl;
00400 
00401 
00402         if (nFaceAttr != 1)
00403         {
00404             FatalIOErrorIn(args.executable().c_str(), faceStream)
00405                 << "Expect boundary markers to be"
00406                 << " present in .face file." << endl
00407                 << "This is the second number in the header which is now:"
00408                 << nFaceAttr << exit(FatalIOError);
00409         }
00410 
00411         // List of Foam vertices per boundary face
00412         boundaryFaces.setSize(nFaces);
00413 
00414         // For each boundary faces the Foam patchID
00415         boundaryPatch.setSize(nFaces);
00416         boundaryPatch = -1;
00417 
00418         label faceI = 0;
00419 
00420         // Region to patch conversion
00421         Map<label> regionToPatch;
00422 
00423         face f(3);
00424 
00425         while (faceStream.good())
00426         {
00427             faceStream.getLine(line);
00428 
00429             if (line.size() && line[0] != '#')
00430             {
00431                 IStringStream faceLine(line);
00432 
00433                 label tetGenFaceI, dummy, region;
00434 
00435                 faceLine >> tetGenFaceI;
00436 
00437                 // Read face and reverse orientation (Foam needs outwards
00438                 // pointing)
00439                 for (label i = 0; i < 3; i++)
00440                 {
00441                     label nodeI;
00442                     faceLine >> nodeI;
00443                     f[2-i] = nodeToPoint[nodeI];
00444                 }
00445 
00446 
00447                 if (findFace(mesh, f) >= mesh.nInternalFaces())
00448                 {
00449                     boundaryFaces[faceI] = f;
00450 
00451                     if (nFaceAttr > 0)
00452                     {
00453                         // First attribute is the region number
00454                         faceLine >> region;
00455 
00456 
00457                         // Get Foam patchID and update region->patch table.
00458                         label patchI = 0;
00459 
00460                         Map<label>::iterator patchFind =
00461                             regionToPatch.find(region);
00462 
00463                         if (patchFind == regionToPatch.end())
00464                         {
00465                             patchI = nPatches;
00466 
00467                             Info<< "Mapping tetgen region " << region
00468                                 << " to Foam patch "
00469                                 << patchI << endl;
00470 
00471                             regionToPatch.insert(region, nPatches++);
00472                         }
00473                         else
00474                         {
00475                             patchI = patchFind();
00476                         }
00477 
00478                         boundaryPatch[faceI] = patchI;
00479 
00480                         // Skip remaining attributes
00481                         for (label i = 1; i < nFaceAttr; i++)
00482                         {
00483                             faceLine >> dummy;
00484                         }
00485                     }
00486 
00487                     faceI++;
00488                 }
00489             }
00490         }
00491 
00492 
00493         // Trim
00494         boundaryFaces.setSize(faceI);
00495         boundaryPatch.setSize(faceI);
00496 
00497 
00498          // Print region to patch mapping
00499         Info<< "Regions:" << endl;
00500 
00501         for
00502         (
00503             Map<label>::const_iterator iter = regionToPatch.begin();
00504             iter != regionToPatch.end();
00505             ++iter
00506         )
00507         {
00508             Info<< "    region:" << iter.key() << '\t' << "patch:"
00509                 << iter() << endl;
00510         }
00511         Info<< endl;
00512 
00513 
00514         // Storage for boundary faces
00515         faceListList patchFaces(nPatches);
00516         wordList patchNames(nPatches);
00517 
00518         forAll(patchNames, patchI)
00519         {
00520             patchNames[patchI] = word("patch") + name(patchI);
00521         }
00522 
00523         wordList patchTypes(nPatches, polyPatch::typeName);
00524         word defaultFacesName = "defaultFaces";
00525         word defaultFacesType = polyPatch::typeName;
00526         wordList patchPhysicalTypes(nPatches, polyPatch::typeName);
00527 
00528 
00529         // Sort boundaryFaces by patch using boundaryPatch.
00530         List<DynamicList<face> > allPatchFaces(nPatches);
00531 
00532         forAll(boundaryPatch, faceI)
00533         {
00534             label patchI = boundaryPatch[faceI];
00535 
00536             allPatchFaces[patchI].append(boundaryFaces[faceI]);
00537         }
00538 
00539         Info<< "Patch sizes:" << endl;
00540 
00541         forAll(allPatchFaces, patchI)
00542         {
00543             Info<< "    " << patchNames[patchI] << " : "
00544                 << allPatchFaces[patchI].size() << endl;
00545 
00546             patchFaces[patchI].transfer(allPatchFaces[patchI]);
00547         }
00548 
00549         Info<< endl;
00550 
00551 
00552         meshPtr.reset
00553         (
00554             new polyMesh
00555             (
00556                 IOobject
00557                 (
00558                     polyMesh::defaultRegion,
00559                     runTime.constant(),
00560                     runTime
00561                 ),
00562                 xferMove(points),
00563                 cells,
00564                 patchFaces,
00565                 patchNames,
00566                 patchTypes,
00567                 defaultFacesName,
00568                 defaultFacesType,
00569                 patchPhysicalTypes
00570             )
00571         );
00572     }
00573 
00574 
00575     Info<< "Writing mesh to " << runTime.constant() << endl << endl;
00576 
00577     meshPtr().write();
00578 
00579     Info<< "End\n" << endl;
00580 
00581     return 0;
00582 }
00583 
00584 
00585 // ************************ vim: set sw=4 sts=4 et: ************************ //
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines