OpenFOAM logo
The Open Source CFD Toolbox
  Source Guide OpenCFD Solutions Contact OpenFOAM

triSurfaceMesh.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-2009 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 the
00013     Free Software Foundation; either version 2 of the License, or (at your
00014     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, write to the Free Software Foundation,
00023     Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
00024 
00025 \*---------------------------------------------------------------------------*/
00026 
00027 #include "triSurfaceMesh.H"
00028 #include "Random.H"
00029 #include "addToRunTimeSelectionTable.H"
00030 #include "EdgeMap.H"
00031 #include "triSurfaceFields.H"
00032 #include "Time.H"
00033 
00034 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
00035 
00036 namespace Foam
00037 {
00038 
00039 defineTypeNameAndDebug(triSurfaceMesh, 0);
00040 addToRunTimeSelectionTable(searchableSurface, triSurfaceMesh, dict);
00041 
00042 }
00043 
00044 // * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
00045 
00048 //Foam::word Foam::triSurfaceMesh::findRawInstance
00049 //(
00050 //    const Time& runTime,
00051 //    const fileName& dir,
00052 //    const word& name
00053 //)
00054 //{
00055 //    // Check current time first
00056 //    if (isFile(runTime.path()/runTime.timeName()/dir/name))
00057 //    {
00058 //        return runTime.timeName();
00059 //    }
00060 //    instantList ts = runTime.times();
00061 //    label instanceI;
00062 //
00063 //    for (instanceI = ts.size()-1; instanceI >= 0; --instanceI)
00064 //    {
00065 //        if (ts[instanceI].value() <= runTime.timeOutputValue())
00066 //        {
00067 //            break;
00068 //        }
00069 //    }
00070 //
00071 //    // continue searching from here
00072 //    for (; instanceI >= 0; --instanceI)
00073 //    {
00074 //        if (isFile(runTime.path()/ts[instanceI].name()/dir/name))
00075 //        {
00076 //            return ts[instanceI].name();
00077 //        }
00078 //    }
00079 //
00080 //
00081 //    // not in any of the time directories, try constant
00082 //
00083 //    // Note. This needs to be a hard-coded constant, rather than the
00084 //    // constant function of the time, because the latter points to
00085 //    // the case constant directory in parallel cases
00086 //
00087 //    if (isFile(runTime.path()/runTime.constant()/dir/name))
00088 //    {
00089 //        return runTime.constant();
00090 //    }
00091 //
00092 //    FatalErrorIn
00093 //    (
00094 //        "searchableSurfaces::findRawInstance"
00095 //        "(const Time&, const fileName&, const word&)"
00096 //    )   << "Cannot find file \"" << name << "\" in directory "
00097 //        << runTime.constant()/dir
00098 //        << exit(FatalError);
00099 //
00100 //    return runTime.constant();
00101 //}
00102 
00103 
00104 //- Check file existence
00105 const Foam::fileName& Foam::triSurfaceMesh::checkFile
00106 (
00107     const fileName& fName,
00108     const fileName& objectName
00109 )
00110 {
00111     if (fName.empty())
00112     {
00113         FatalErrorIn
00114         (
00115             "triSurfaceMesh::checkFile(const fileName&, const fileName&)"
00116         )   << "Cannot find triSurfaceMesh starting from "
00117             << objectName << exit(FatalError);
00118     }
00119     return fName;
00120 }
00121 
00122 
00123 bool Foam::triSurfaceMesh::addFaceToEdge
00124 (
00125     const edge& e,
00126     EdgeMap<label>& facesPerEdge
00127 )
00128 {
00129     EdgeMap<label>::iterator eFnd = facesPerEdge.find(e);
00130     if (eFnd != facesPerEdge.end())
00131     {
00132         if (eFnd() == 2)
00133         {
00134             return false;
00135         }
00136         eFnd()++;
00137     }
00138     else
00139     {
00140         facesPerEdge.insert(e, 1);
00141     }
00142     return true;
00143 }
00144 
00145 
00146 bool Foam::triSurfaceMesh::isSurfaceClosed() const
00147 {
00148     // Construct pointFaces. Let's hope surface has compact point
00149     // numbering ...
00150     labelListList pointFaces;
00151     invertManyToMany(points().size(), *this, pointFaces);
00152 
00153     // Loop over all faces surrounding point. Count edges emanating from point.
00154     // Every edge should be used by two faces exactly.
00155     // To prevent doing work twice per edge only look at edges to higher
00156     // point
00157     EdgeMap<label> facesPerEdge(100);
00158     forAll(pointFaces, pointI)
00159     {
00160         const labelList& pFaces = pointFaces[pointI];
00161 
00162         facesPerEdge.clear();
00163         forAll(pFaces, i)
00164         {
00165             const labelledTri& f = triSurface::operator[](pFaces[i]);
00166             label fp = findIndex(f, pointI);
00167 
00168             // Something weird: if I expand the code of addFaceToEdge in both
00169             // below instances it gives a segmentation violation on some
00170             // surfaces. Compiler (4.3.2) problem?
00171 
00172 
00173             // Forward edge
00174             label nextPointI = f[f.fcIndex(fp)];
00175 
00176             if (nextPointI > pointI)
00177             {
00178                 bool okFace = addFaceToEdge
00179                 (
00180                     edge(pointI, nextPointI),
00181                     facesPerEdge
00182                 );
00183 
00184                 if (!okFace)
00185                 {
00186                     return false;
00187                 }
00188             }
00189             // Reverse edge
00190             label prevPointI = f[f.rcIndex(fp)];
00191 
00192             if (prevPointI > pointI)
00193             {
00194                 bool okFace = addFaceToEdge
00195                 (
00196                     edge(pointI, prevPointI),
00197                     facesPerEdge
00198                 );
00199 
00200                 if (!okFace)
00201                 {
00202                     return false;
00203                 }
00204             }
00205         }
00206 
00207         // Check for any edges used only once.
00208         forAllConstIter(EdgeMap<label>, facesPerEdge, iter)
00209         {
00210             if (iter() != 2)
00211             {
00212                 return false;
00213             }
00214         }
00215     }
00216 
00217     return true;
00218 }
00219 
00220 
00221 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
00222 
00223 Foam::triSurfaceMesh::triSurfaceMesh(const IOobject& io, const triSurface& s)
00224 :
00225     searchableSurface(io),
00226     objectRegistry
00227     (
00228         IOobject
00229         (
00230             io.name(),
00231             io.instance(),
00232             io.local(),
00233             io.db(),
00234             io.readOpt(),
00235             io.writeOpt(),
00236             false       // searchableSurface already registered under name
00237         )
00238     ),
00239     triSurface(s),
00240     tolerance_(indexedOctree<treeDataTriSurface>::perturbTol()),
00241     surfaceClosed_(-1)
00242 {}
00243 
00244 
00245 Foam::triSurfaceMesh::triSurfaceMesh(const IOobject& io)
00246 :
00247     // Find instance for triSurfaceMesh
00248     searchableSurface(io),
00249     //(
00250     //    IOobject
00251     //    (
00252     //        io.name(),
00253     //        io.time().findInstance(io.local(), word::null),
00254     //        io.local(),
00255     //        io.db(),
00256     //        io.readOpt(),
00257     //        io.writeOpt(),
00258     //        io.registerObject()
00259     //    )
00260     //),
00261     // Reused found instance in objectRegistry
00262     objectRegistry
00263     (
00264         IOobject
00265         (
00266             io.name(),
00267             static_cast<const searchableSurface&>(*this).instance(),
00268             io.local(),
00269             io.db(),
00270             io.readOpt(),
00271             io.writeOpt(),
00272             false       // searchableSurface already registered under name
00273         )
00274     ),
00275     triSurface
00276     (
00277         checkFile
00278         (
00279             searchableSurface::filePath(),
00280             searchableSurface::objectPath()
00281         )
00282     ),
00283     tolerance_(indexedOctree<treeDataTriSurface>::perturbTol()),
00284     surfaceClosed_(-1)
00285 {}
00286 
00287 
00288 Foam::triSurfaceMesh::triSurfaceMesh
00289 (
00290     const IOobject& io,
00291     const dictionary& dict
00292 )
00293 :
00294     searchableSurface(io),
00295     //(
00296     //    IOobject
00297     //    (
00298     //        io.name(),
00299     //        io.time().findInstance(io.local(), word::null),
00300     //        io.local(),
00301     //        io.db(),
00302     //        io.readOpt(),
00303     //        io.writeOpt(),
00304     //        io.registerObject()
00305     //    )
00306     //),
00307     // Reused found instance in objectRegistry
00308     objectRegistry
00309     (
00310         IOobject
00311         (
00312             io.name(),
00313             static_cast<const searchableSurface&>(*this).instance(),
00314             io.local(),
00315             io.db(),
00316             io.readOpt(),
00317             io.writeOpt(),
00318             false       // searchableSurface already registered under name
00319         )
00320     ),
00321     triSurface
00322     (
00323         checkFile
00324         (
00325             searchableSurface::filePath(),
00326             searchableSurface::objectPath()
00327         )
00328     ),
00329     tolerance_(indexedOctree<treeDataTriSurface>::perturbTol()),
00330     surfaceClosed_(-1)
00331 {
00332     scalar scaleFactor = 0;
00333 
00334     // allow rescaling of the surface points
00335     // eg, CAD geometries are often done in millimeters
00336     if (dict.readIfPresent("scale", scaleFactor) && scaleFactor > 0)
00337     {
00338         Info<< searchableSurface::name() << " : using scale " << scaleFactor
00339             << endl;
00340         triSurface::scalePoints(scaleFactor);
00341     }
00342 
00343 
00344     // Have optional non-standard search tolerance for gappy surfaces.
00345     if (dict.readIfPresent("tolerance", tolerance_) && tolerance_ > 0)
00346     {
00347         Info<< searchableSurface::name() << " : using intersection tolerance "
00348             << tolerance_ << endl;
00349     }
00350 }
00351 
00352 
00353 // * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
00354 
00355 Foam::triSurfaceMesh::~triSurfaceMesh()
00356 {
00357     clearOut();
00358 }
00359 
00360 
00361 void Foam::triSurfaceMesh::clearOut()
00362 {
00363     tree_.clear();
00364     edgeTree_.clear();
00365     triSurface::clearOut();
00366 }
00367 
00368 
00369 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
00370 
00371 void Foam::triSurfaceMesh::movePoints(const pointField& newPoints)
00372 {
00373     tree_.clear();
00374     edgeTree_.clear();
00375     triSurface::movePoints(newPoints);
00376 }
00377 
00378 
00379 const Foam::indexedOctree<Foam::treeDataTriSurface>&
00380     Foam::triSurfaceMesh::tree() const
00381 {
00382     if (tree_.empty())
00383     {
00384         // Random number generator. Bit dodgy since not exactly random ;-)
00385         Random rndGen(65431);
00386 
00387         // Slightly extended bb. Slightly off-centred just so on symmetric
00388         // geometry there are less face/edge aligned items.
00389         treeBoundBox bb
00390         (
00391             treeBoundBox(points(), meshPoints()).extend(rndGen, 1E-4)
00392         );
00393         bb.min() -= point(ROOTVSMALL, ROOTVSMALL, ROOTVSMALL);
00394         bb.max() += point(ROOTVSMALL, ROOTVSMALL, ROOTVSMALL);
00395 
00396         scalar oldTol = indexedOctree<treeDataTriSurface>::perturbTol();
00397         indexedOctree<treeDataTriSurface>::perturbTol() = tolerance_;
00398 
00399         tree_.reset
00400         (
00401             new indexedOctree<treeDataTriSurface>
00402             (
00403                 treeDataTriSurface(*this),
00404                 bb,
00405                 10,     // maxLevel
00406                 10,     // leafsize
00407                 3.0     // duplicity
00408             )
00409         );
00410 
00411         indexedOctree<treeDataTriSurface>::perturbTol() = oldTol;
00412     }
00413 
00414     return tree_();
00415 }
00416 
00417 
00418 const Foam::indexedOctree<Foam::treeDataEdge>&
00419  Foam::triSurfaceMesh::edgeTree() const
00420 {
00421     if (edgeTree_.empty())
00422     {
00423         // Boundary edges
00424         labelList bEdges
00425         (
00426             identity
00427             (
00428                 nEdges()
00429                -nInternalEdges()
00430             )
00431           + nInternalEdges()
00432         );
00433 
00434         // Random number generator. Bit dodgy since not exactly random ;-)
00435         Random rndGen(65431);
00436 
00437         // Slightly extended bb. Slightly off-centred just so on symmetric
00438         // geometry there are less face/edge aligned items.
00439         treeBoundBox bb
00440         (
00441             treeBoundBox(points(), meshPoints()).extend(rndGen, 1E-4)
00442         );
00443         bb.min() -= point(ROOTVSMALL, ROOTVSMALL, ROOTVSMALL);
00444         bb.max() += point(ROOTVSMALL, ROOTVSMALL, ROOTVSMALL);
00445 
00446         edgeTree_.reset
00447         (
00448             new indexedOctree<treeDataEdge>
00449             (
00450                 treeDataEdge
00451                 (
00452                     false,          // cachebb
00453                     edges(),        // edges
00454                     localPoints(),  // points
00455                     bEdges          // selected edges
00456                 ),
00457                 bb,     // bb
00458                 8,      // maxLevel
00459                 10,     // leafsize
00460                 3.0     // duplicity
00461             )
00462         );
00463     }
00464     return edgeTree_();
00465 }
00466 
00467 
00468 const Foam::wordList& Foam::triSurfaceMesh::regions() const
00469 {
00470     if (regions_.empty())
00471     {
00472         regions_.setSize(patches().size());
00473         forAll(regions_, regionI)
00474         {
00475             regions_[regionI] = patches()[regionI].name();
00476         }
00477     }
00478     return regions_;
00479 }
00480 
00481 
00482 // Find out if surface is closed.
00483 bool Foam::triSurfaceMesh::hasVolumeType() const
00484 {
00485     if (surfaceClosed_ == -1)
00486     {
00487         if (isSurfaceClosed())
00488         {
00489             surfaceClosed_ = 1;
00490         }
00491         else
00492         {
00493             surfaceClosed_ = 0;
00494         }
00495     }
00496 
00497     return surfaceClosed_ == 1;
00498 }
00499 
00500 
00501 void Foam::triSurfaceMesh::findNearest
00502 (
00503     const pointField& samples,
00504     const scalarField& nearestDistSqr,
00505     List<pointIndexHit>& info
00506 ) const
00507 {
00508     const indexedOctree<treeDataTriSurface>& octree = tree();
00509 
00510     info.setSize(samples.size());
00511 
00512     scalar oldTol = indexedOctree<treeDataTriSurface>::perturbTol();
00513     indexedOctree<treeDataTriSurface>::perturbTol() = tolerance_;
00514 
00515     forAll(samples, i)
00516     {
00517         static_cast<pointIndexHit&>(info[i]) = octree.findNearest
00518         (
00519             samples[i],
00520             nearestDistSqr[i]
00521         );
00522     }
00523 
00524     indexedOctree<treeDataTriSurface>::perturbTol() = oldTol;
00525 }
00526 
00527 
00528 void Foam::triSurfaceMesh::findLine
00529 (
00530     const pointField& start,
00531     const pointField& end,
00532     List<pointIndexHit>& info
00533 ) const
00534 {
00535     const indexedOctree<treeDataTriSurface>& octree = tree();
00536 
00537     info.setSize(start.size());
00538 
00539     scalar oldTol = indexedOctree<treeDataTriSurface>::perturbTol();
00540     indexedOctree<treeDataTriSurface>::perturbTol() = tolerance_;
00541 
00542     forAll(start, i)
00543     {
00544         static_cast<pointIndexHit&>(info[i]) = octree.findLine
00545         (
00546             start[i],
00547             end[i]
00548         );
00549     }
00550 
00551     indexedOctree<treeDataTriSurface>::perturbTol() = oldTol;
00552 }
00553 
00554 
00555 void Foam::triSurfaceMesh::findLineAny
00556 (
00557     const pointField& start,
00558     const pointField& end,
00559     List<pointIndexHit>& info
00560 ) const
00561 {
00562     const indexedOctree<treeDataTriSurface>& octree = tree();
00563 
00564     info.setSize(start.size());
00565 
00566     scalar oldTol = indexedOctree<treeDataTriSurface>::perturbTol();
00567     indexedOctree<treeDataTriSurface>::perturbTol() = tolerance_;
00568 
00569     forAll(start, i)
00570     {
00571         static_cast<pointIndexHit&>(info[i]) = octree.findLineAny
00572         (
00573             start[i],
00574             end[i]
00575         );
00576     }
00577 
00578     indexedOctree<treeDataTriSurface>::perturbTol() = oldTol;
00579 }
00580 
00581 
00582 void Foam::triSurfaceMesh::findLineAll
00583 (
00584     const pointField& start,
00585     const pointField& end,
00586     List<List<pointIndexHit> >& info
00587 ) const
00588 {
00589     const indexedOctree<treeDataTriSurface>& octree = tree();
00590 
00591     info.setSize(start.size());
00592 
00593     scalar oldTol = indexedOctree<treeDataTriSurface>::perturbTol();
00594     indexedOctree<treeDataTriSurface>::perturbTol() = tolerance_;
00595 
00596     // Work array
00597     DynamicList<pointIndexHit, 1, 1> hits;
00598 
00599     // Tolerances:
00600     // To find all intersections we add a small vector to the last intersection
00601     // This is chosen such that
00602     // - it is significant (SMALL is smallest representative relative tolerance;
00603     //   we need something bigger since we're doing calculations)
00604     // - if the start-end vector is zero we still progress
00605     const vectorField dirVec(end-start);
00606     const scalarField magSqrDirVec(magSqr(dirVec));
00607     const vectorField smallVec
00608     (
00609         indexedOctree<treeDataTriSurface>::perturbTol()*dirVec
00610       + vector(ROOTVSMALL,ROOTVSMALL,ROOTVSMALL)
00611     );
00612 
00613     forAll(start, pointI)
00614     {
00615         // See if any intersection between pt and end
00616         pointIndexHit inter = octree.findLine(start[pointI], end[pointI]);
00617 
00618         if (inter.hit())
00619         {
00620             hits.clear();
00621             hits.append(inter);
00622 
00623             point pt = inter.hitPoint() + smallVec[pointI];
00624 
00625             while (((pt-start[pointI])&dirVec[pointI]) <= magSqrDirVec[pointI])
00626             {
00627                 // See if any intersection between pt and end
00628                 pointIndexHit inter = octree.findLine(pt, end[pointI]);
00629 
00630                 // Check for not hit or hit same triangle as before (can happen
00631                 // if vector along surface of triangle)
00632                 if
00633                 (
00634                     !inter.hit()
00635                  || (inter.index() == hits[hits.size()-1].index())
00636                 )
00637                 {
00638                     break;
00639                 }
00640                 hits.append(inter);
00641 
00642                 pt = inter.hitPoint() + smallVec[pointI];
00643             }
00644 
00645             info[pointI].transfer(hits);
00646         }
00647         else
00648         {
00649             info[pointI].clear();
00650         }
00651     }
00652 
00653     indexedOctree<treeDataTriSurface>::perturbTol() = oldTol;
00654 }
00655 
00656 
00657 void Foam::triSurfaceMesh::getRegion
00658 (
00659     const List<pointIndexHit>& info,
00660     labelList& region
00661 ) const
00662 {
00663     region.setSize(info.size());
00664     forAll(info, i)
00665     {
00666         if (info[i].hit())
00667         {
00668             region[i] = triSurface::operator[](info[i].index()).region();
00669         }
00670         else
00671         {
00672             region[i] = -1;
00673         }
00674     }
00675 }
00676 
00677 
00678 void Foam::triSurfaceMesh::getNormal
00679 (
00680     const List<pointIndexHit>& info,
00681     vectorField& normal
00682 ) const
00683 {
00684     normal.setSize(info.size());
00685 
00686     forAll(info, i)
00687     {
00688         if (info[i].hit())
00689         {
00690             label triI = info[i].index();
00691             //- Cached:
00692             //normal[i] = faceNormals()[triI];
00693 
00694             //- Uncached
00695             normal[i] = triSurface::operator[](triI).normal(points());
00696             normal[i] /= mag(normal[i]) + VSMALL;
00697         }
00698         else
00699         {
00700             // Set to what?
00701             normal[i] = vector::zero;
00702         }
00703     }
00704 }
00705 
00706 
00707 void Foam::triSurfaceMesh::getField
00708 (
00709     const word& fieldName,
00710     const List<pointIndexHit>& info,
00711     labelList& values
00712 ) const
00713 {
00714     const triSurfaceLabelField& fld = lookupObject<triSurfaceLabelField>
00715     (
00716         fieldName
00717     );
00718 
00719     values.setSize(info.size());
00720     forAll(info, i)
00721     {
00722         if (info[i].hit())
00723         {
00724             values[i] = fld[info[i].index()];
00725         }
00726     }
00727 }
00728 
00729 
00730 void Foam::triSurfaceMesh::getVolumeType
00731 (
00732     const pointField& points,
00733     List<volumeType>& volType
00734 ) const
00735 {
00736     volType.setSize(points.size());
00737 
00738     scalar oldTol = indexedOctree<treeDataTriSurface>::perturbTol();
00739     indexedOctree<treeDataTriSurface>::perturbTol() = tolerance_;
00740 
00741     forAll(points, pointI)
00742     {
00743         const point& pt = points[pointI];
00744 
00745         // - use cached volume type per each tree node
00746         // - cheat conversion since same values
00747         volType[pointI] = static_cast<volumeType>(tree().getVolumeType(pt));
00748     }
00749 
00750     indexedOctree<treeDataTriSurface>::perturbTol() = oldTol;
00751 }
00752 
00753 
00754 //- Write using given format, version and compression
00755 bool Foam::triSurfaceMesh::writeObject
00756 (
00757     IOstream::streamFormat fmt,
00758     IOstream::versionNumber ver,
00759     IOstream::compressionType cmp
00760 ) const
00761 {
00762     fileName fullPath(searchableSurface::objectPath());
00763 
00764     if (!mkDir(fullPath.path()))
00765     {
00766         return false;
00767     }
00768 
00769     triSurface::write(fullPath);
00770 
00771     if (!isFile(fullPath))
00772     {
00773         return false;
00774     }
00775 
00776     //return objectRegistry::writeObject(fmt, ver, cmp);
00777     return true;
00778 }
00779 
00780 
00781 // ************************************************************************* //
Copyright © 2000-2009 OpenCFD Ltd