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

primitiveMeshCheck.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 \*---------------------------------------------------------------------------*/
00025 
00026 #include <OpenFOAM/primitiveMesh.H>
00027 #include <OpenFOAM/pyramidPointFaceRef.H>
00028 #include <OpenFOAM/ListOps.H>
00029 #include <OpenFOAM/mathematicalConstants.H>
00030 #include <OpenFOAM/SortableList.H>
00031 
00032 
00033 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
00034 
00035 Foam::scalar Foam::primitiveMesh::closedThreshold_  = 1.0e-6;
00036 Foam::scalar Foam::primitiveMesh::aspectThreshold_  = 1000;
00037 Foam::scalar Foam::primitiveMesh::nonOrthThreshold_ = 70;    // deg
00038 Foam::scalar Foam::primitiveMesh::skewThreshold_    = 4;
00039 
00040 
00041 // * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
00042 
00043 bool Foam::primitiveMesh::checkClosedBoundary(const bool report) const
00044 {
00045     if (debug)
00046     {
00047         Info<< "bool primitiveMesh::checkClosedBoundary("
00048             << "const bool) const: "
00049             << "checking whether the boundary is closed" << endl;
00050     }
00051 
00052     // Loop through all boundary faces and sum up the face area vectors.
00053     // For a closed boundary, this should be zero in all vector components
00054 
00055     vector sumClosed(vector::zero);
00056     scalar sumMagClosedBoundary = 0;
00057 
00058     const vectorField& areas = faceAreas();
00059 
00060     for (label faceI = nInternalFaces(); faceI < areas.size(); faceI++)
00061     {
00062         sumClosed += areas[faceI];
00063         sumMagClosedBoundary += mag(areas[faceI]);
00064     }
00065 
00066     reduce(sumClosed, sumOp<vector>());
00067     reduce(sumMagClosedBoundary, sumOp<scalar>());
00068 
00069     vector openness = sumClosed/(sumMagClosedBoundary + VSMALL);
00070 
00071     if (cmptMax(cmptMag(openness)) > closedThreshold_)
00072     {
00073         if (debug || report)
00074         {
00075             Info<< " ***Boundary openness " << openness
00076                 << " possible hole in boundary description."
00077                 << endl;
00078         }
00079 
00080         return true;
00081     }
00082     else
00083     {
00084         if (debug || report)
00085         {
00086             Info<< "    Boundary openness " << openness << " OK."
00087                 << endl;
00088         }
00089 
00090         return false;
00091     }
00092 }
00093 
00094 
00095 bool Foam::primitiveMesh::checkClosedCells
00096 (
00097     const bool report,
00098     labelHashSet* setPtr,
00099     labelHashSet* aspectSetPtr,
00100     const Vector<label>& meshD
00101 ) const
00102 {
00103     if (debug)
00104     {
00105         Info<< "bool primitiveMesh::checkClosedCells("
00106             << "const bool, labelHashSet*, labelHashSet*"
00107             << ", const Vector<label>&) const: "
00108             << "checking whether cells are closed" << endl;
00109     }
00110 
00111     // Check that all cells labels are valid
00112     const cellList& c = cells();
00113 
00114     label nErrorClosed = 0;
00115 
00116     forAll (c, cI)
00117     {
00118         const cell& curCell = c[cI];
00119 
00120         if (min(curCell) < 0 || max(curCell) > nFaces())
00121         {
00122             if (setPtr)
00123             {
00124                 setPtr->insert(cI);
00125             }
00126 
00127             nErrorClosed++;
00128         }
00129     }
00130 
00131     if (nErrorClosed > 0)
00132     {
00133         if (debug || report)
00134         {
00135             Info<< " ***Cells with invalid face labels found, number of cells "
00136                 << nErrorClosed << endl;
00137         }
00138 
00139         return true;
00140     }
00141 
00142     // Loop through cell faces and sum up the face area vectors for each cell.
00143     // This should be zero in all vector components
00144 
00145     vectorField sumClosed(nCells(), vector::zero);
00146     vectorField sumMagClosed(nCells(), vector::zero);
00147 
00148     const labelList& own = faceOwner();
00149     const labelList& nei = faceNeighbour();
00150     const vectorField& areas = faceAreas();
00151 
00152     forAll (own, faceI)
00153     {
00154         // Add to owner
00155         sumClosed[own[faceI]] += areas[faceI];
00156         sumMagClosed[own[faceI]] += cmptMag(areas[faceI]);
00157     }
00158 
00159     forAll (nei, faceI)
00160     {
00161         // Subtract from neighbour
00162         sumClosed[nei[faceI]] -= areas[faceI];
00163         sumMagClosed[nei[faceI]] += cmptMag(areas[faceI]);
00164     }
00165 
00166     label nOpen = 0;
00167     scalar maxOpennessCell = 0;
00168 
00169     label nAspect = 0;
00170     scalar maxAspectRatio = 0;
00171 
00172     const scalarField& vols = cellVolumes();
00173 
00174     label nDims = 0;
00175     for (direction dir = 0; dir < vector::nComponents; dir++)
00176     {
00177         if (meshD[dir] == 1)
00178         {
00179             nDims++;
00180         }
00181     }
00182 
00183 
00184     // Check the sums
00185     forAll(sumClosed, cellI)
00186     {
00187         scalar maxOpenness = 0;
00188 
00189         for(direction cmpt=0; cmpt<vector::nComponents; cmpt++)
00190         {
00191             maxOpenness = max
00192             (
00193                 maxOpenness,
00194                 mag(sumClosed[cellI][cmpt])
00195                /(sumMagClosed[cellI][cmpt] + VSMALL)
00196             );
00197         }
00198 
00199         maxOpennessCell = max(maxOpennessCell, maxOpenness);
00200 
00201         if (maxOpenness > closedThreshold_)
00202         {
00203             if (setPtr)
00204             {
00205                 setPtr->insert(cellI);
00206             }
00207 
00208             nOpen++;
00209         }
00210 
00211         // Calculate the aspect ration as the maximum of Cartesian component
00212         // aspect ratio to the total area hydraulic area aspect ratio
00213         scalar minCmpt = VGREAT;
00214         scalar maxCmpt = -VGREAT;
00215         for (direction dir = 0; dir < vector::nComponents; dir++)
00216         {
00217             if (meshD[dir] == 1)
00218             {
00219                 minCmpt = min(minCmpt, sumMagClosed[cellI][dir]);
00220                 maxCmpt = max(maxCmpt, sumMagClosed[cellI][dir]);
00221             }
00222         }
00223 
00224         scalar aspectRatio = maxCmpt/(minCmpt + VSMALL);
00225         if (nDims == 3)
00226         {
00227             aspectRatio = max
00228             (
00229                 aspectRatio,
00230                 1.0/6.0*cmptSum(sumMagClosed[cellI])/pow(vols[cellI], 2.0/3.0)
00231             );
00232         }
00233 
00234         maxAspectRatio = max(maxAspectRatio, aspectRatio);
00235 
00236         if (aspectRatio > aspectThreshold_)
00237         {
00238             if (aspectSetPtr)
00239             {
00240                 aspectSetPtr->insert(cellI);
00241             }
00242 
00243             nAspect++;
00244         }
00245     }
00246 
00247     reduce(nOpen, sumOp<label>());
00248     reduce(maxOpennessCell, maxOp<scalar>());
00249 
00250     reduce(nAspect, sumOp<label>());
00251     reduce(maxAspectRatio, maxOp<scalar>());
00252 
00253 
00254     if (nOpen > 0)
00255     {
00256         if (debug || report)
00257         {
00258             Info<< " ***Open cells found, max cell openness: "
00259                 << maxOpennessCell << ", number of open cells " << nOpen
00260                 << endl;
00261         }
00262 
00263         return true;
00264     }
00265 
00266     if (nAspect > 0)
00267     {
00268         if (debug || report)
00269         {
00270             Info<< " ***High aspect ratio cells found, Max aspect ratio: "
00271                 << maxAspectRatio
00272                 << ", number of cells " << nAspect
00273                 << endl;
00274         }
00275 
00276         return true;
00277     }
00278 
00279     if (debug || report)
00280     {
00281         Info<< "    Max cell openness = " << maxOpennessCell << " OK." << nl
00282             << "    Max aspect ratio = " << maxAspectRatio << " OK."
00283             << endl;
00284     }
00285 
00286     return false;
00287 }
00288 
00289 
00290 bool Foam::primitiveMesh::checkFaceAreas
00291 (
00292     const bool report,
00293     labelHashSet* setPtr
00294 ) const
00295 {
00296     if (debug)
00297     {
00298         Info<< "bool primitiveMesh::checkFaceAreas("
00299             << "const bool, labelHashSet*) const: "
00300             << "checking face area magnitudes" << endl;
00301     }
00302 
00303     const scalarField magFaceAreas = mag(faceAreas());
00304 
00305     scalar minArea = GREAT;
00306     scalar maxArea = -GREAT;
00307 
00308     forAll (magFaceAreas, faceI)
00309     {
00310         if (magFaceAreas[faceI] < VSMALL)
00311         {
00312             if (setPtr)
00313             {
00314                 setPtr->insert(faceI);
00315             }
00316         }
00317 
00318         minArea = min(minArea, magFaceAreas[faceI]);
00319         maxArea = max(maxArea, magFaceAreas[faceI]);
00320     }
00321 
00322     reduce(minArea, minOp<scalar>());
00323     reduce(maxArea, maxOp<scalar>());
00324 
00325     if (minArea < VSMALL)
00326     {
00327         if (debug || report)
00328         {
00329             Info<< " ***Zero or negative face area detected.  "
00330                 "Minimum area: " << minArea << endl;
00331         }
00332 
00333         return true;
00334     }
00335     else
00336     {
00337         if (debug || report)
00338         {
00339             Info<< "    Minumum face area = " << minArea
00340                 << ". Maximum face area = " << maxArea
00341                 << ".  Face area magnitudes OK." << endl;
00342         }
00343 
00344         return false;
00345     }
00346 }
00347 
00348 
00349 bool Foam::primitiveMesh::checkCellVolumes
00350 (
00351     const bool report,
00352     labelHashSet* setPtr
00353 ) const
00354 {
00355     if (debug)
00356     {
00357         Info<< "bool primitiveMesh::checkCellVolumes("
00358             << "const bool, labelHashSet*) const: "
00359             << "checking cell volumes" << endl;
00360     }
00361 
00362     const scalarField& vols = cellVolumes();
00363 
00364     scalar minVolume = GREAT;
00365     scalar maxVolume = -GREAT;
00366 
00367     label nNegVolCells = 0;
00368 
00369     forAll (vols, cellI)
00370     {
00371         if (vols[cellI] < VSMALL)
00372         {
00373             if (setPtr)
00374             {
00375                 setPtr->insert(cellI);
00376             }
00377 
00378             nNegVolCells++;
00379         }
00380 
00381         minVolume = min(minVolume, vols[cellI]);
00382         maxVolume = max(maxVolume, vols[cellI]);
00383     }
00384 
00385     reduce(minVolume, minOp<scalar>());
00386     reduce(maxVolume, maxOp<scalar>());
00387     reduce(nNegVolCells, sumOp<label>());
00388 
00389     if (minVolume < VSMALL)
00390     {
00391         if (debug || report)
00392         {
00393             Info<< " ***Zero or negative cell volume detected.  "
00394                 << "Minimum negative volume: " << minVolume
00395                 << ", Number of negative volume cells: " << nNegVolCells
00396                 << endl;
00397         }
00398 
00399         return true;
00400     }
00401     else
00402     {
00403         if (debug || report)
00404         {
00405             Info<< "    Min volume = " << minVolume
00406                 << ". Max volume = " << maxVolume
00407                 << ".  Total volume = " << gSum(vols)
00408                 << ".  Cell volumes OK." << endl;
00409         }
00410 
00411         return false;
00412     }
00413 }
00414 
00415 
00416 bool Foam::primitiveMesh::checkFaceOrthogonality
00417 (
00418     const bool report,
00419     labelHashSet* setPtr
00420 ) const
00421 {
00422     if (debug)
00423     {
00424         Info<< "bool primitiveMesh::checkFaceOrthogonality("
00425             << "const bool, labelHashSet*) const: "
00426             << "checking mesh non-orthogonality" << endl;
00427     }
00428 
00429     // for all internal faces check theat the d dot S product is positive
00430     const vectorField& centres = cellCentres();
00431     const vectorField& areas = faceAreas();
00432 
00433     const labelList& own = faceOwner();
00434     const labelList& nei = faceNeighbour();
00435 
00436     // Severe nonorthogonality threshold
00437     const scalar severeNonorthogonalityThreshold =
00438         ::cos(nonOrthThreshold_/180.0*mathematicalConstant::pi);
00439 
00440     scalar minDDotS = GREAT;
00441 
00442     scalar sumDDotS = 0;
00443 
00444     label severeNonOrth = 0;
00445 
00446     label errorNonOrth = 0;
00447 
00448     forAll (nei, faceI)
00449     {
00450         vector d = centres[nei[faceI]] - centres[own[faceI]];
00451         const vector& s = areas[faceI];
00452 
00453         scalar dDotS = (d & s)/(mag(d)*mag(s) + VSMALL);
00454 
00455         if (dDotS < severeNonorthogonalityThreshold)
00456         {
00457             if (dDotS > SMALL)
00458             {
00459                 if (setPtr)
00460                 {
00461                     setPtr->insert(faceI);
00462                 }
00463 
00464                 severeNonOrth++;
00465             }
00466             else
00467             {
00468                 if (setPtr)
00469                 {
00470                     setPtr->insert(faceI);
00471                 }
00472 
00473                 errorNonOrth++;
00474             }
00475         }
00476 
00477         if (dDotS < minDDotS)
00478         {
00479             minDDotS = dDotS;
00480         }
00481 
00482         sumDDotS += dDotS;
00483     }
00484 
00485     reduce(minDDotS, minOp<scalar>());
00486     reduce(sumDDotS, sumOp<scalar>());
00487     reduce(severeNonOrth, sumOp<label>());
00488     reduce(errorNonOrth, sumOp<label>());
00489 
00490     if (debug || report)
00491     {
00492         label neiSize = nei.size();
00493         reduce(neiSize, sumOp<label>());
00494 
00495         if (neiSize > 0)
00496         {
00497             if (debug || report)
00498             {
00499                 Info<< "    Mesh non-orthogonality Max: "
00500                     << ::acos(minDDotS)/mathematicalConstant::pi*180.0
00501                     << " average: " <<
00502                     ::acos(sumDDotS/neiSize)/mathematicalConstant::pi*180.0
00503                     << endl;
00504             }
00505         }
00506 
00507         if (severeNonOrth > 0)
00508         {
00509             Info<< "   *Number of severely non-orthogonal faces: "
00510                 << severeNonOrth << "." << endl;
00511         }
00512     }
00513 
00514     if (errorNonOrth > 0)
00515     {
00516         if (debug || report)
00517         {
00518             Info<< " ***Number of non-orthogonality errors: "
00519                 << errorNonOrth << "." << endl;
00520         }
00521 
00522         return true;
00523     }
00524     else
00525     {
00526         if (debug || report)
00527         {
00528             Info<< "    Non-orthogonality check OK." << endl;
00529         }
00530 
00531         return false;
00532     }
00533 }
00534 
00535 
00536 bool Foam::primitiveMesh::checkFacePyramids
00537 (
00538     const bool report,
00539     const scalar minPyrVol,
00540     labelHashSet* setPtr
00541 ) const
00542 {
00543     if (debug)
00544     {
00545         Info<< "bool primitiveMesh::checkFacePyramids("
00546             << "const bool, const scalar, labelHashSet*) const: "
00547             << "checking face orientation" << endl;
00548     }
00549 
00550     // check whether face area vector points to the cell with higher label
00551     const vectorField& ctrs = cellCentres();
00552 
00553     const labelList& own = faceOwner();
00554     const labelList& nei = faceNeighbour();
00555 
00556     const faceList& f = faces();
00557 
00558     const pointField& p = points();
00559 
00560     label nErrorPyrs = 0;
00561 
00562     forAll (f, faceI)
00563     {
00564         // Create the owner pyramid - it will have negative volume
00565         scalar pyrVol = pyramidPointFaceRef(f[faceI], ctrs[own[faceI]]).mag(p);
00566 
00567         if (pyrVol > -minPyrVol)
00568         {
00569             if (setPtr)
00570             {
00571                 setPtr->insert(faceI);
00572             }
00573 
00574             nErrorPyrs++;
00575         }
00576 
00577         if (isInternalFace(faceI))
00578         {
00579             // Create the neighbour pyramid - it will have positive volume
00580             scalar pyrVol =
00581                 pyramidPointFaceRef(f[faceI], ctrs[nei[faceI]]).mag(p);
00582 
00583             if (pyrVol < minPyrVol)
00584             {
00585                 if (setPtr)
00586                 {
00587                     setPtr->insert(faceI);
00588                 }
00589 
00590                 nErrorPyrs++;
00591             }
00592         }
00593     }
00594 
00595     reduce(nErrorPyrs, sumOp<label>());
00596 
00597     if (nErrorPyrs > 0)
00598     {
00599         if (debug || report)
00600         {
00601             Info<< " ***Error in face pyramids: "
00602                 << nErrorPyrs << " faces are incorrectly oriented."
00603                 << endl;
00604         }
00605 
00606         return true;
00607     }
00608     else
00609     {
00610         if (debug || report)
00611         {
00612             Info<< "    Face pyramids OK." << endl;
00613         }
00614 
00615         return false;
00616     }
00617 }
00618 
00619 
00620 bool Foam::primitiveMesh::checkFaceSkewness
00621 (
00622     const bool report,
00623     labelHashSet* setPtr
00624 ) const
00625 {
00626     if (debug)
00627     {
00628         Info<< "bool primitiveMesh::checkFaceSkewnesss("
00629             << "const bool, labelHashSet*) const: "
00630             << "checking face skewness" << endl;
00631     }
00632 
00633     // Warn if the skew correction vector is more than skewWarning times
00634     // larger than the face area vector
00635 
00636     const pointField& p = points();
00637     const faceList& fcs = faces();
00638 
00639     const labelList& own = faceOwner();
00640     const labelList& nei = faceNeighbour();
00641     const vectorField& cellCtrs = cellCentres();
00642     const vectorField& faceCtrs = faceCentres();
00643     const vectorField& fAreas = faceAreas();
00644 
00645     scalar maxSkew = 0;
00646     label nWarnSkew = 0;
00647 
00648     forAll(nei, faceI)
00649     {
00650         vector Cpf = faceCtrs[faceI] - cellCtrs[own[faceI]];
00651         vector d = cellCtrs[nei[faceI]] - cellCtrs[own[faceI]];
00652 
00653         // Skewness vector
00654         vector sv =
00655             Cpf - ((fAreas[faceI] & Cpf)/((fAreas[faceI] & d) + SMALL))*d;
00656         vector svHat = sv/(mag(sv) + VSMALL);
00657 
00658         // Normalisation distance calculated as the approximate distance
00659         // from the face centre to the edge of the face in the direction of
00660         // the skewness
00661         scalar fd = 0.2*mag(d) + VSMALL;
00662         const face& f = fcs[faceI];
00663         forAll(f, pi)
00664         {
00665             fd = max(fd, mag(svHat & (p[f[pi]] - faceCtrs[faceI])));
00666         }
00667 
00668         // Normalised skewness
00669         scalar skewness = mag(sv)/fd;
00670 
00671         // Check if the skewness vector is greater than the PN vector.
00672         // This does not cause trouble but is a good indication of a poor mesh.
00673         if (skewness > skewThreshold_)
00674         {
00675             if (setPtr)
00676             {
00677                 setPtr->insert(faceI);
00678             }
00679 
00680             nWarnSkew++;
00681         }
00682 
00683         if(skewness > maxSkew)
00684         {
00685             maxSkew = skewness;
00686         }
00687     }
00688 
00689 
00690     // Boundary faces: consider them to have only skewness error.
00691     // (i.e. treat as if mirror cell on other side)
00692 
00693     for (label faceI = nInternalFaces(); faceI < nFaces(); faceI++)
00694     {
00695         vector Cpf = faceCtrs[faceI] - cellCtrs[own[faceI]];
00696 
00697         vector normal = fAreas[faceI];
00698         normal /= mag(normal) + VSMALL;
00699         vector d = normal*(normal & Cpf);
00700 
00701 
00702         // Skewness vector
00703         vector sv = Cpf - ((fAreas[faceI]&Cpf)/((fAreas[faceI]&d)+VSMALL))*d;
00704         vector svHat = sv/(mag(sv) + VSMALL);
00705 
00706         // Normalisation distance calculated as the approximate distance
00707         // from the face centre to the edge of the face in the direction of
00708         // the skewness
00709         scalar fd = 0.4*mag(d) + VSMALL;
00710         const face& f = fcs[faceI];
00711         forAll(f, pi)
00712         {
00713             fd = max(fd, mag(svHat & (p[f[pi]] - faceCtrs[faceI])));
00714         }
00715 
00716         // Normalised skewness
00717         scalar skewness = mag(sv)/fd;
00718 
00719         // Check if the skewness vector is greater than the PN vector.
00720         // This does not cause trouble but is a good indication of a poor mesh.
00721         if (skewness > skewThreshold_)
00722         {
00723             if (setPtr)
00724             {
00725                 setPtr->insert(faceI);
00726             }
00727 
00728             nWarnSkew++;
00729         }
00730 
00731         if(skewness > maxSkew)
00732         {
00733             maxSkew = skewness;
00734         }
00735     }
00736 
00737 
00738     reduce(maxSkew, maxOp<scalar>());
00739     reduce(nWarnSkew, sumOp<label>());
00740 
00741     if (nWarnSkew > 0)
00742     {
00743         if (debug || report)
00744         {
00745             Info<< " ***Max skewness = " << maxSkew
00746                 << ", " << nWarnSkew << " highly skew faces detected"
00747                    " which may impair the quality of the results"
00748                 << endl;
00749         }
00750 
00751         return true;
00752     }
00753     else
00754     {
00755         if (debug || report)
00756         {
00757             Info<< "    Max skewness = " << maxSkew << " OK." << endl;
00758         }
00759 
00760         return false;
00761     }
00762 }
00763 
00764 
00765 bool Foam::primitiveMesh::checkPoints
00766 (
00767     const bool report,
00768     labelHashSet* setPtr
00769 ) const
00770 {
00771     if (debug)
00772     {
00773         Info<< "bool primitiveMesh::checkPoints"
00774             << "(const bool, labelHashSet*) const: "
00775             << "checking points" << endl;
00776     }
00777 
00778     label nFaceErrors = 0;
00779     label nCellErrors = 0;
00780 
00781     const labelListList& pf = pointFaces();
00782 
00783     forAll (pf, pointI)
00784     {
00785         if (pf[pointI].empty())
00786         {
00787             if (setPtr)
00788             {
00789                 setPtr->insert(pointI);
00790             }
00791 
00792             nFaceErrors++;
00793         }
00794     }
00795 
00796 
00797     forAll (pf, pointI)
00798     {
00799         const labelList& pc = pointCells(pointI);
00800 
00801         if (pc.empty())
00802         {
00803             if (setPtr)
00804             {
00805                 setPtr->insert(pointI);
00806             }
00807 
00808             nCellErrors++;
00809         }
00810     }
00811 
00812     reduce(nFaceErrors, sumOp<label>());
00813     reduce(nCellErrors, sumOp<label>());
00814 
00815     if (nFaceErrors > 0 || nCellErrors > 0)
00816     {
00817         if (debug || report)
00818         {
00819             Info<< " ***Unused points found in the mesh, "
00820                    "number unused by faces: " << nFaceErrors
00821                 << " number unused by cells: " << nCellErrors
00822                 << endl;
00823         }
00824 
00825         return true;
00826     }
00827     else
00828     {
00829         if (debug || report)
00830         {
00831             Info<< "    Point usage OK." << endl;
00832         }
00833 
00834         return false;
00835     }
00836 }
00837 
00838 
00839 // Check convexity of angles in a face. Allow a slight non-convexity.
00840 // E.g. maxDeg = 10 allows for angles < 190 (or 10 degrees concavity)
00841 // (if truly concave and points not visible from face centre the face-pyramid
00842 //  check in checkMesh will fail)
00843 bool Foam::primitiveMesh::checkFaceAngles
00844 (
00845     const bool report,
00846     const scalar maxDeg,
00847     labelHashSet* setPtr
00848 ) const
00849 {
00850     if (debug)
00851     {
00852         Info<< "bool primitiveMesh::checkFaceAngles"
00853             << "(const bool, const scalar, labelHashSet*) const: "
00854             << "checking face angles" << endl;
00855     }
00856 
00857     if (maxDeg < -SMALL || maxDeg > 180+SMALL)
00858     {
00859         FatalErrorIn
00860         (
00861             "primitiveMesh::checkFaceAngles"
00862             "(const bool, const scalar, labelHashSet*)"
00863         )   << "maxDeg should be [0..180] but is now " << maxDeg
00864             << exit(FatalError);
00865     }
00866 
00867     const scalar maxSin = Foam::sin(maxDeg/180.0*mathematicalConstant::pi);
00868 
00869     const pointField& p = points();
00870     const faceList& fcs = faces();
00871     vectorField faceNormals(faceAreas());
00872     faceNormals /= mag(faceNormals) + VSMALL;
00873 
00874     scalar maxEdgeSin = 0.0;
00875 
00876     label nConcave = 0;
00877 
00878     label errorFaceI = -1;
00879 
00880     forAll(fcs, faceI)
00881     {
00882         const face& f = fcs[faceI];
00883 
00884         // Get edge from f[0] to f[size-1];
00885         vector ePrev(p[f[0]] - p[f[f.size()-1]]);
00886         scalar magEPrev = mag(ePrev);
00887         ePrev /= magEPrev + VSMALL;
00888 
00889         forAll(f, fp0)
00890         {
00891             // Get vertex after fp
00892             label fp1 = f.fcIndex(fp0);
00893 
00894             // Normalized vector between two consecutive points
00895             vector e10(p[f[fp1]] - p[f[fp0]]);
00896             scalar magE10 = mag(e10);
00897             e10 /= magE10 + VSMALL;
00898 
00899             if (magEPrev > SMALL && magE10 > SMALL)
00900             {
00901                 vector edgeNormal = ePrev ^ e10;
00902                 scalar magEdgeNormal = mag(edgeNormal);
00903 
00904                 if (magEdgeNormal < maxSin)
00905                 {
00906                     // Edges (almost) aligned -> face is ok.
00907                 }
00908                 else
00909                 {
00910                     // Check normal
00911                     edgeNormal /= magEdgeNormal;
00912 
00913                     if ((edgeNormal & faceNormals[faceI]) < SMALL)
00914                     {
00915                         if (faceI != errorFaceI)
00916                         {
00917                             // Count only one error per face.
00918                             errorFaceI = faceI;
00919                             nConcave++;
00920                         }
00921 
00922                         if (setPtr)
00923                         {
00924                             setPtr->insert(faceI);
00925                         }
00926 
00927                         maxEdgeSin = max(maxEdgeSin, magEdgeNormal);
00928                     }
00929                 }
00930             }
00931 
00932             ePrev = e10;
00933             magEPrev = magE10;
00934         }
00935     }
00936 
00937     reduce(nConcave, sumOp<label>());
00938     reduce(maxEdgeSin, maxOp<scalar>());
00939 
00940     if (nConcave > 0)
00941     {
00942         scalar maxConcaveDegr =
00943             Foam::asin(Foam::min(1.0, maxEdgeSin))
00944            *180.0/mathematicalConstant::pi;
00945 
00946         if (debug || report)
00947         {
00948             Info<< "   *There are " << nConcave
00949                 << " faces with concave angles between consecutive"
00950                 << " edges. Max concave angle = " << maxConcaveDegr
00951                 << " degrees." << endl;
00952         }
00953 
00954         return true;
00955     }
00956     else
00957     {
00958         if (debug || report)
00959         {
00960             Info<< "    All angles in faces OK." << endl;
00961         }
00962 
00963         return false;
00964     }
00965 }
00966 
00967 
00968 // Check warpage of faces. Is calculated as the difference between areas of
00969 // individual triangles and the overall area of the face (which ifself is
00970 // is the average of the areas of the individual triangles).
00971 bool Foam::primitiveMesh::checkFaceFlatness
00972 (
00973     const bool report,
00974     const scalar warnFlatness,
00975     labelHashSet* setPtr
00976 ) const
00977 {
00978     if (debug)
00979     {
00980         Info<< "bool primitiveMesh::checkFaceFlatness"
00981             << "(const bool, const scalar, labelHashSet*) const: "
00982             << "checking face flatness" << endl;
00983     }
00984 
00985     if (warnFlatness < 0 || warnFlatness > 1)
00986     {
00987         FatalErrorIn
00988         (
00989             "primitiveMesh::checkFaceFlatness"
00990             "(const bool, const scalar, labelHashSet*)"
00991         )   << "warnFlatness should be [0..1] but is now " << warnFlatness
00992             << exit(FatalError);
00993     }
00994 
00995 
00996     const pointField& p = points();
00997     const faceList& fcs = faces();
00998     const pointField& fctrs = faceCentres();
00999 
01000     // Areas are calculated as the sum of areas. (see
01001     // primitiveMeshFaceCentresAndAreas.C)
01002     scalarField magAreas(mag(faceAreas()));
01003 
01004     label nWarped = 0;
01005 
01006     scalar minFlatness = GREAT;
01007     scalar sumFlatness = 0;
01008     label nSummed = 0;
01009 
01010     forAll(fcs, faceI)
01011     {
01012         const face& f = fcs[faceI];
01013 
01014         if (f.size() > 3 && magAreas[faceI] > VSMALL)
01015         {
01016             const point& fc = fctrs[faceI];
01017 
01018             // Calculate the sum of magnitude of areas and compare to magnitude
01019             // of sum of areas.
01020 
01021             scalar sumA = 0.0;
01022 
01023             forAll(f, fp)
01024             {
01025                 const point& thisPoint = p[f[fp]];
01026                 const point& nextPoint = p[f.nextLabel(fp)];
01027 
01028                 // Triangle around fc.
01029                 vector n = 0.5*((nextPoint - thisPoint)^(fc - thisPoint));
01030                 sumA += mag(n);
01031             }
01032 
01033             scalar flatness = magAreas[faceI] / (sumA+VSMALL);
01034 
01035             sumFlatness += flatness;
01036             nSummed++;
01037 
01038             minFlatness = min(minFlatness, flatness);
01039 
01040             if (flatness < warnFlatness)
01041             {
01042                 nWarped++;
01043 
01044                 if (setPtr)
01045                 {
01046                     setPtr->insert(faceI);
01047                 }
01048             }
01049         }
01050     }
01051 
01052 
01053     reduce(nWarped, sumOp<label>());
01054     reduce(minFlatness, minOp<scalar>());
01055 
01056     reduce(nSummed, sumOp<label>());
01057     reduce(sumFlatness, sumOp<scalar>());
01058 
01059     if (debug || report)
01060     {
01061         if (nSummed > 0)
01062         {
01063             Info<< "    Face flatness (1 = flat, 0 = butterfly) : average = "
01064                 << sumFlatness / nSummed << "  min = " << minFlatness << endl;
01065         }
01066     }
01067 
01068 
01069     if (nWarped> 0)
01070     {
01071         if (debug || report)
01072         {
01073             Info<< "   *There are " << nWarped
01074                 << " faces with ratio between projected and actual area < "
01075                 << warnFlatness << endl;
01076 
01077             Info<< "    Minimum ratio (minimum flatness, maximum warpage) = "
01078                 << minFlatness << endl;
01079         }
01080 
01081         return true;
01082     }
01083     else
01084     {
01085         if (debug || report)
01086         {
01087             Info<< "    All face flatness OK." << endl;
01088         }
01089 
01090         return false;
01091     }
01092 }
01093 
01094 
01095 // Check 1D/2Dness of edges. Gets passed the non-empty directions and
01096 // checks all edges in the mesh whether they:
01097 // - have no component in a non-empty direction or
01098 // - are only in a singe non-empty direction.
01099 // Empty direction info is passed in as a vector of labels (synchronised)
01100 // which are 1 if the direction is non-empty, 0 if it is.
01101 bool Foam::primitiveMesh::checkEdgeAlignment
01102 (
01103     const bool report,
01104     const Vector<label>& directions,
01105     labelHashSet* setPtr
01106 ) const
01107 {
01108     if (debug)
01109     {
01110         Info<< "bool primitiveMesh::checkEdgeAlignment("
01111             << "const bool, const Vector<label>&, labelHashSet*) const: "
01112             << "checking edge alignment" << endl;
01113     }
01114 
01115     label nDirs = 0;
01116     for (direction cmpt=0; cmpt<vector::nComponents; cmpt++)
01117     {
01118         if (directions[cmpt] == 1)
01119         {
01120             nDirs++;
01121         }
01122         else if (directions[cmpt] != 0)
01123         {
01124             FatalErrorIn
01125             (
01126                 "primitiveMesh::checkEdgeAlignment"
01127                 "(const bool, const Vector<label>&, labelHashSet*)"
01128             )   << "directions should contain 0 or 1 but is now " << directions
01129                 << exit(FatalError);
01130         }
01131     }
01132 
01133     if (nDirs == vector::nComponents)
01134     {
01135         return false;
01136     }
01137 
01138 
01139 
01140     const pointField& p = points();
01141     const faceList& fcs = faces();
01142 
01143     EdgeMap<label> edgesInError;
01144 
01145     forAll(fcs, faceI)
01146     {
01147         const face& f = fcs[faceI];
01148 
01149         forAll(f, fp)
01150         {
01151             label p0 = f[fp];
01152             label p1 = f.nextLabel(fp);
01153             if (p0 < p1)
01154             {
01155                 vector d(p[p1]-p[p0]);
01156                 scalar magD = mag(d);
01157 
01158                 if (magD > ROOTVSMALL)
01159                 {
01160                     d /= magD;
01161 
01162                     // Check how many empty directions are used by the edge.
01163                     label nEmptyDirs = 0;
01164                     label nNonEmptyDirs = 0;
01165                     for (direction cmpt=0; cmpt<vector::nComponents; cmpt++)
01166                     {
01167                         if (mag(d[cmpt]) > 1e-6)
01168                         {
01169                             if (directions[cmpt] == 0)
01170                             {
01171                                 nEmptyDirs++;
01172                             }
01173                             else
01174                             {
01175                                 nNonEmptyDirs++;
01176                             }
01177                         }
01178                     }
01179 
01180                     if (nEmptyDirs == 0)
01181                     {
01182                         // Purely in ok directions.
01183                     }
01184                     else if (nEmptyDirs == 1)
01185                     {
01186                         // Ok if purely in empty directions.
01187                         if (nNonEmptyDirs > 0)
01188                         {
01189                             edgesInError.insert(edge(p0, p1), faceI);
01190                         }
01191                     }
01192                     else if (nEmptyDirs > 1)
01193                     {
01194                         // Always an error
01195                         edgesInError.insert(edge(p0, p1), faceI);
01196                     }
01197                 }
01198             }
01199         }
01200     }
01201 
01202     label nErrorEdges = returnReduce(edgesInError.size(), sumOp<label>());
01203 
01204     if (nErrorEdges > 0)
01205     {
01206         if (debug || report)
01207         {
01208             Info<< " ***Number of edges not aligned with or perpendicular to "
01209                 << "non-empty directions: " << nErrorEdges << endl;
01210         }
01211 
01212         if (setPtr)
01213         {
01214             setPtr->resize(2*edgesInError.size());
01215             forAllConstIter(EdgeMap<label>, edgesInError, iter)
01216             {
01217                 setPtr->insert(iter.key()[0]);
01218                 setPtr->insert(iter.key()[1]);
01219             }
01220         }
01221 
01222         return true;
01223     }
01224     else
01225     {
01226         if (debug || report)
01227         {
01228             Info<< "    All edges aligned with or perpendicular to "
01229                 << "non-empty directions." << endl;
01230         }
01231         return false;
01232     }
01233 }
01234 
01235 
01236 bool Foam::primitiveMesh::checkUpperTriangular
01237 (
01238     const bool report,
01239     labelHashSet* setPtr
01240 ) const
01241 {
01242     if (debug)
01243     {
01244         Info<< "bool primitiveMesh::checkUpperTriangular("
01245             << "const bool, labelHashSet*) const: "
01246             << "checking face ordering" << endl;
01247     }
01248 
01249     // Check whether internal faces are ordered in the upper triangular order
01250     const labelList& own = faceOwner();
01251     const labelList& nei = faceNeighbour();
01252 
01253     const cellList& c = cells();
01254 
01255     label internal = nInternalFaces();
01256 
01257     // Has error occurred?
01258     bool error = false;
01259     // Have multiple faces been detected?
01260     label nMultipleCells = false;
01261 
01262     // Loop through faceCells once more and make sure that for internal cell
01263     // the first label is smaller
01264     for (label faceI = 0; faceI < internal; faceI++)
01265     {
01266         if (own[faceI] >= nei[faceI])
01267         {
01268             error  = true;
01269 
01270             if (setPtr)
01271             {
01272                 setPtr->insert(faceI);
01273             }
01274         }
01275     }
01276 
01277     // Loop through all cells. For each cell, find the face that is internal
01278     // and add it to the check list (upper triangular order).
01279     // Once the list is completed, check it against the faceCell list
01280 
01281     forAll (c, cellI)
01282     {
01283         const labelList& curFaces = c[cellI];
01284 
01285         // Neighbouring cells
01286         SortableList<label> nbr(curFaces.size());
01287 
01288         forAll(curFaces, i)
01289         {
01290             label faceI = curFaces[i];
01291 
01292             if (faceI >= nInternalFaces())
01293             {
01294                 // Sort last
01295                 nbr[i] = labelMax;
01296             }
01297             else
01298             {
01299                 label nbrCellI = nei[faceI];
01300 
01301                 if (nbrCellI == cellI)
01302                 {
01303                     nbrCellI = own[faceI];
01304                 }
01305 
01306                 if (cellI < nbrCellI)
01307                 {
01308                     // cellI is master
01309                     nbr[i] = nbrCellI;
01310                 }
01311                 else
01312                 {
01313                     // nbrCell is master. Let it handle this face.
01314                     nbr[i] = labelMax;
01315                 }
01316             }
01317         }
01318 
01319         nbr.sort();
01320 
01321         // Now nbr holds the cellCells in incremental order. Check:
01322         // - neighbouring cells appear only once. Since nbr is sorted this
01323         //   is simple check on consecutive elements
01324         // - faces indexed in same order as nbr are incrementing as well.
01325 
01326         label prevCell = nbr[0];
01327         label prevFace = curFaces[nbr.indices()[0]];
01328 
01329         bool hasMultipleFaces = false;
01330 
01331         for (label i = 1; i < nbr.size(); i++)
01332         {
01333             label thisCell = nbr[i];
01334             label thisFace = curFaces[nbr.indices()[i]];
01335 
01336             if (thisCell == labelMax)
01337             {
01338                 break;
01339             }
01340 
01341             if (thisCell == prevCell)
01342             {
01343                 hasMultipleFaces = true;
01344 
01345                 if (setPtr)
01346                 {
01347                     setPtr->insert(prevFace);
01348                     setPtr->insert(thisFace);
01349                 }
01350             }
01351             else if (thisFace < prevFace)
01352             {
01353                 error = true;
01354 
01355                 if (setPtr)
01356                 {
01357                     setPtr->insert(thisFace);
01358                 }
01359             }
01360 
01361             prevCell = thisCell;
01362             prevFace = thisFace;
01363         }
01364 
01365         if (hasMultipleFaces)
01366         {
01367             nMultipleCells++;
01368         }
01369     }
01370 
01371     reduce(error, orOp<bool>());
01372     reduce(nMultipleCells, sumOp<label>());
01373 
01374     if ((debug || report) && nMultipleCells > 0)
01375     {
01376         Info<< "  <<Found " << nMultipleCells
01377             << " neighbouring cells with multiple inbetween faces." << endl;
01378     }
01379 
01380     if (error)
01381     {
01382         if (debug || report)
01383         {
01384             Info<< " ***Faces not in upper triangular order." << endl;
01385         }
01386 
01387         return true;
01388     }
01389     else
01390     {
01391         if (debug || report)
01392         {
01393             Info<< "    Upper triangular ordering OK." << endl;
01394         }
01395 
01396         return false;
01397     }
01398 }
01399 
01400 
01401 bool Foam::primitiveMesh::checkCellsZipUp
01402 (
01403     const bool report,
01404     labelHashSet* setPtr
01405 ) const
01406 {
01407     if (debug)
01408     {
01409         Info<< "bool primitiveMesh::checkCellsZipUp("
01410             << "const bool, labelHashSet*) const: "
01411             << "checking topological cell openness" << endl;
01412     }
01413 
01414     label nOpenCells = 0;
01415 
01416     const faceList& f = faces();
01417     const cellList& c = cells();
01418 
01419     forAll (c, cellI)
01420     {
01421         const labelList& curFaces = c[cellI];
01422 
01423         const edgeList cellEdges = c[cellI].edges(f);
01424 
01425         labelList edgeUsage(cellEdges.size(), 0);
01426 
01427         forAll (curFaces, faceI)
01428         {
01429             edgeList curFaceEdges = f[curFaces[faceI]].edges();
01430 
01431             forAll (curFaceEdges, faceEdgeI)
01432             {
01433                 const edge& curEdge = curFaceEdges[faceEdgeI];
01434 
01435                 forAll (cellEdges, cellEdgeI)
01436                 {
01437                     if (cellEdges[cellEdgeI] == curEdge)
01438                     {
01439                         edgeUsage[cellEdgeI]++;
01440                         break;
01441                     }
01442                 }
01443             }
01444         }
01445 
01446         edgeList singleEdges(cellEdges.size());
01447         label nSingleEdges = 0;
01448 
01449         forAll (edgeUsage, edgeI)
01450         {
01451             if (edgeUsage[edgeI] == 1)
01452             {
01453                 singleEdges[nSingleEdges] = cellEdges[edgeI];
01454                 nSingleEdges++;
01455             }
01456             else if (edgeUsage[edgeI] != 2)
01457             {
01458                 if (setPtr)
01459                 {
01460                     setPtr->insert(cellI);
01461                 }
01462             }
01463         }
01464 
01465         if (nSingleEdges > 0)
01466         {
01467             if (setPtr)
01468             {
01469                 setPtr->insert(cellI);
01470             }
01471 
01472             nOpenCells++;
01473         }
01474     }
01475 
01476     reduce(nOpenCells, sumOp<label>());
01477 
01478     if (nOpenCells > 0)
01479     {
01480         if (debug || report)
01481         {
01482             Info<< " ***Open cells found, number of cells: " << nOpenCells
01483                 << ". This problem may be fixable using the zipUpMesh utility."
01484                 << endl;
01485         }
01486 
01487         return true;
01488     }
01489     else
01490     {
01491         if (debug || report)
01492         {
01493             Info<< "    Topological cell zip-up check OK." << endl;
01494         }
01495 
01496         return false;
01497     }
01498 }
01499 
01500 
01501 // Vertices of face within point range and unique.
01502 bool Foam::primitiveMesh::checkFaceVertices
01503 (
01504     const bool report,
01505     labelHashSet* setPtr
01506 ) const
01507 {
01508     if (debug)
01509     {
01510         Info<< "bool primitiveMesh::checkFaceVertices("
01511             << "const bool, labelHashSet*) const: "
01512             << "checking face vertices" << endl;
01513     }
01514 
01515     // Check that all vertex labels are valid
01516     const faceList& f = faces();
01517 
01518     label nErrorFaces = 0;
01519 
01520     forAll (f, fI)
01521     {
01522         const face& curFace = f[fI];
01523 
01524         if (min(curFace) < 0 || max(curFace) > nPoints())
01525         {
01526             if (setPtr)
01527             {
01528                 setPtr->insert(fI);
01529             }
01530 
01531             nErrorFaces++;
01532         }
01533 
01534         // Uniqueness of vertices
01535         labelHashSet facePoints(2*curFace.size());
01536 
01537         forAll(curFace, fp)
01538         {
01539             bool inserted = facePoints.insert(curFace[fp]);
01540 
01541             if (!inserted)
01542             {
01543                 if (setPtr)
01544                 {
01545                     setPtr->insert(fI);
01546                 }
01547 
01548                 nErrorFaces++;
01549             }
01550         }
01551     }
01552 
01553     reduce(nErrorFaces, sumOp<label>());
01554 
01555     if (nErrorFaces > 0)
01556     {
01557         if (debug || report)
01558         {
01559             Info<< "    Faces with invalid vertex labels found, "
01560                 << " number of faces: " << nErrorFaces << endl;
01561         }
01562 
01563         return true;
01564     }
01565     else
01566     {
01567         if (debug || report)
01568         {
01569             Info<< "    Face vertices OK." << endl;
01570         }
01571 
01572         return false;
01573     }
01574 }
01575 
01576 
01577 // Check if all points on face are shared between faces.
01578 bool Foam::primitiveMesh::checkDuplicateFaces
01579 (
01580     const label faceI,
01581     const Map<label>& nCommonPoints,
01582     label& nBaffleFaces,
01583     labelHashSet* setPtr
01584 ) const
01585 {
01586     bool error = false;
01587 
01588     forAllConstIter(Map<label>, nCommonPoints, iter)
01589     {
01590         label nbFaceI = iter.key();
01591         label nCommon = iter();
01592 
01593         const face& curFace = faces()[faceI];
01594         const face& nbFace = faces()[nbFaceI];
01595 
01596         if (nCommon == nbFace.size() || nCommon == curFace.size())
01597         {
01598             if (nbFace.size() != curFace.size())
01599             {
01600                 error = true;
01601             }
01602             else
01603             {
01604                 nBaffleFaces++;
01605             }
01606 
01607             if (setPtr)
01608             {
01609                 setPtr->insert(faceI);
01610                 setPtr->insert(nbFaceI);
01611             }
01612         }
01613     }
01614 
01615     return error;
01616 }
01617 
01618 
01619 // Check that shared points are in consecutive order.
01620 bool Foam::primitiveMesh::checkCommonOrder
01621 (
01622     const label faceI,
01623     const Map<label>& nCommonPoints,
01624     labelHashSet* setPtr
01625 ) const
01626 {
01627     bool error = false;
01628 
01629     forAllConstIter(Map<label>, nCommonPoints, iter)
01630     {
01631         label nbFaceI = iter.key();
01632         label nCommon = iter();
01633 
01634         const face& curFace = faces()[faceI];
01635         const face& nbFace = faces()[nbFaceI];
01636 
01637         if
01638         (
01639             nCommon >= 2
01640          && nCommon != nbFace.size()
01641          && nCommon != curFace.size()
01642         )
01643         {
01644             forAll(curFace, fp)
01645             {
01646                 // Get the index in the neighbouring face shared with curFace
01647                 label nb = findIndex(nbFace, curFace[fp]);
01648 
01649                 if (nb != -1)
01650                 {
01651 
01652                     // Check the whole face from nb onwards for shared vertices
01653                     // with neighbouring face. Rule is that any shared vertices
01654                     // should be consecutive on both faces i.e. if they are
01655                     // vertices fp,fp+1,fp+2 on one face they should be
01656                     // vertices nb, nb+1, nb+2 (or nb+2, nb+1, nb) on the
01657                     // other face.
01658 
01659 
01660                     // Vertices before and after on curFace
01661                     label fpPlus1 = curFace.fcIndex(fp);
01662                     label fpMin1  = curFace.rcIndex(fp);
01663 
01664                     // Vertices before and after on nbFace
01665                     label nbPlus1 = nbFace.fcIndex(nb);
01666                     label nbMin1  = nbFace.rcIndex(nb);
01667 
01668                     // Find order of walking by comparing next points on both
01669                     // faces.
01670                     label curInc = labelMax;
01671                     label nbInc = labelMax;
01672 
01673                     if (nbFace[nbPlus1] == curFace[fpPlus1])
01674                     {
01675                         curInc = 1;
01676                         nbInc = 1;
01677                     }
01678                     else if (nbFace[nbPlus1] == curFace[fpMin1])
01679                     {
01680                         curInc = -1;
01681                         nbInc = 1;
01682                     }
01683                     else if (nbFace[nbMin1] == curFace[fpMin1])
01684                     {
01685                         curInc = -1;
01686                         nbInc = -1;
01687                     }
01688                     else
01689                     {
01690                         curInc = 1;
01691                         nbInc = -1;
01692                     }
01693 
01694 
01695                     // Pass1: loop until start of common vertices found.
01696                     label curNb = nb;
01697                     label curFp = fp;
01698 
01699                     do
01700                     {
01701                         curFp += curInc;
01702 
01703                         if (curFp >= curFace.size())
01704                         {
01705                             curFp = 0;
01706                         }
01707                         else if (curFp < 0)
01708                         {
01709                             curFp = curFace.size()-1;
01710                         }
01711 
01712                         curNb += nbInc;
01713 
01714                         if (curNb >= nbFace.size())
01715                         {
01716                             curNb = 0;
01717                         }
01718                         else if (curNb < 0)
01719                         {
01720                             curNb = nbFace.size()-1;
01721                         }
01722                     } while (curFace[curFp] == nbFace[curNb]);
01723 
01724 
01725                     // Pass2: check equality walking from curFp, curNb
01726                     // in opposite order.
01727 
01728                     curInc = -curInc;
01729                     nbInc = -nbInc;
01730 
01731                     for (label commonI = 0; commonI < nCommon; commonI++)
01732                     {
01733                         curFp += curInc;
01734 
01735                         if (curFp >= curFace.size())
01736                         {
01737                             curFp = 0;
01738                         }
01739                         else if (curFp < 0)
01740                         {
01741                             curFp = curFace.size()-1;
01742                         }
01743 
01744                         curNb += nbInc;
01745 
01746                         if (curNb >= nbFace.size())
01747                         {
01748                             curNb = 0;
01749                         }
01750                         else if (curNb < 0)
01751                         {
01752                             curNb = nbFace.size()-1;
01753                         }
01754 
01755                         if (curFace[curFp] != nbFace[curNb])
01756                         {
01757                             if (setPtr)
01758                             {
01759                                 setPtr->insert(faceI);
01760                                 setPtr->insert(nbFaceI);
01761                             }
01762 
01763                             error = true;
01764 
01765                             break;
01766                         }
01767                     }
01768 
01769 
01770                     // Done the curFace - nbFace combination.
01771                     break;
01772                 }
01773             }
01774         }
01775     }
01776 
01777     return error;
01778 }
01779 
01780 
01781 // Checks common vertices between faces. If more than 2 they should be
01782 // consecutive on both faces.
01783 bool Foam::primitiveMesh::checkFaceFaces
01784 (
01785     const bool report,
01786     labelHashSet* setPtr
01787 ) const
01788 {
01789     if (debug)
01790     {
01791         Info<< "bool primitiveMesh::checkFaceFaces(const bool, labelHashSet*)"
01792             << " const: " << "checking face-face connectivity" << endl;
01793     }
01794 
01795     const labelListList& pf = pointFaces();
01796 
01797     label nBaffleFaces = 0;
01798     label nErrorDuplicate = 0;
01799     label nErrorOrder = 0;
01800     Map<label> nCommonPoints(100);
01801 
01802     for (label faceI = 0; faceI < nFaces(); faceI++)
01803     {
01804         const face& curFace = faces()[faceI];
01805 
01806         // Calculate number of common points between current faceI and
01807         // neighbouring face. Store on map.
01808         nCommonPoints.clear();
01809 
01810         forAll(curFace, fp)
01811         {
01812             label pointI = curFace[fp];
01813 
01814             const labelList& nbs = pf[pointI];
01815 
01816             forAll(nbs, nbI)
01817             {
01818                 label nbFaceI = nbs[nbI];
01819 
01820                 if (faceI < nbFaceI)
01821                 {
01822                     // Only check once for each combination of two faces.
01823 
01824                     Map<label>::iterator fnd = nCommonPoints.find(nbFaceI);
01825 
01826                     if (fnd == nCommonPoints.end())
01827                     {
01828                         // First common vertex found.
01829                         nCommonPoints.insert(nbFaceI, 1);
01830                     }
01831                     else
01832                     {
01833                         fnd()++;
01834                     }
01835                 }
01836             }
01837         }
01838 
01839         // Perform various checks on common points
01840 
01841         // Check all vertices shared (duplicate point)
01842         if (checkDuplicateFaces(faceI, nCommonPoints, nBaffleFaces, setPtr))
01843         {
01844             nErrorDuplicate++;
01845         }
01846 
01847         // Check common vertices are consecutive on both faces
01848         if (checkCommonOrder(faceI, nCommonPoints, setPtr))
01849         {
01850             nErrorOrder++;
01851         }
01852     }
01853 
01854     reduce(nBaffleFaces, sumOp<label>());
01855     reduce(nErrorDuplicate, sumOp<label>());
01856     reduce(nErrorOrder, sumOp<label>());
01857 
01858     if (nBaffleFaces)
01859     {
01860         Info<< "    Number of identical duplicate faces (baffle faces): "
01861             << nBaffleFaces << endl;
01862     }
01863 
01864     if (nErrorDuplicate > 0 || nErrorOrder > 0)
01865     {
01866         if (nErrorDuplicate > 0)
01867         {
01868             Info<< " ***Number of duplicate (not baffle) faces found: "
01869                 << nErrorDuplicate << endl;
01870         }
01871 
01872         if (nErrorOrder > 0)
01873         {
01874             Info<< " ***Number of faces with non-consecutive shared points: "
01875                 << nErrorOrder << endl;
01876         }
01877 
01878         return true;
01879     }
01880     else
01881     {
01882         if (debug || report)
01883         {
01884             Info<< "    Face-face connectivity OK." << endl;
01885         }
01886 
01887         return false;
01888     }
01889 }
01890 
01891 
01892 // Checks cells with 1 or less internal faces. Give numerical problems.
01893 bool Foam::primitiveMesh::checkCellDeterminant
01894 (
01895     const bool report,    // report,
01896     labelHashSet* setPtr, // setPtr
01897     const Vector<label>& meshD
01898 ) const
01899 {
01900     if (debug)
01901     {
01902         Info<< "bool primitiveMesh::checkCellDeterminant(const bool"
01903             << ", labelHashSet*) const: "
01904             << "checking for under-determined cells" << endl;
01905     }
01906 
01907     // Determine number of dimensions and (for 2D) missing dimension
01908     label nDims = 0;
01909     label twoD = -1;
01910     for (direction dir = 0; dir < vector::nComponents; dir++)
01911     {
01912         if (meshD[dir] == 1)
01913         {
01914             nDims++;
01915         }
01916         else
01917         {
01918             twoD = dir;
01919         }
01920     }
01921 
01922 
01923     const cellList& c = cells();
01924 
01925     label nErrorCells = 0;
01926 
01927     scalar minDet = GREAT;
01928     scalar sumDet = 0;
01929     label nSummed = 0;
01930 
01931     if (nDims == 1)
01932     {
01933         minDet = 1;
01934         sumDet = c.size()*minDet;
01935         nSummed = c.size();
01936     }
01937     else
01938     {
01939         forAll (c, cellI)
01940         {
01941             const labelList& curFaces = c[cellI];
01942 
01943             // Calculate local normalization factor
01944             scalar avgArea = 0;
01945 
01946             label nInternalFaces = 0;
01947 
01948             forAll(curFaces, i)
01949             {
01950                 if (isInternalFace(curFaces[i]))
01951                 {
01952                     avgArea += mag(faceAreas()[curFaces[i]]);
01953 
01954                     nInternalFaces++;
01955                 }
01956             }
01957 
01958             if (nInternalFaces == 0)
01959             {
01960                 if (setPtr)
01961                 {
01962                     setPtr->insert(cellI);
01963                 }
01964 
01965                 nErrorCells++;
01966             }
01967             else
01968             {
01969                 avgArea /= nInternalFaces;
01970 
01971                 symmTensor areaTensor(symmTensor::zero);
01972 
01973                 forAll(curFaces, i)
01974                 {
01975                     if (isInternalFace(curFaces[i]))
01976                     {
01977                         areaTensor += sqr(faceAreas()[curFaces[i]]/avgArea);
01978                     }
01979                 }
01980 
01981                 if (nDims == 2)
01982                 {
01983                     // Add the missing eigenvector (such that it does not
01984                     // affect the determinant)
01985                     if (twoD == 0)
01986                     {
01987                         areaTensor.xx() = 1;
01988                     }
01989                     else if (twoD == 1)
01990                     {
01991                         areaTensor.yy() = 1;
01992                     }
01993                     else
01994                     {
01995                         areaTensor.zz() = 1;
01996                     }
01997                 }
01998 
01999                 scalar determinant = mag(det(areaTensor));
02000 
02001                 minDet = min(determinant, minDet);
02002                 sumDet += determinant;
02003                 nSummed++;
02004 
02005                 if (determinant < 1e-3)
02006                 {
02007                     if (setPtr)
02008                     {
02009                         setPtr->insert(cellI);
02010                     }
02011 
02012                     nErrorCells++;
02013                 }
02014             }
02015         }
02016     }
02017 
02018     reduce(nErrorCells, sumOp<label>());
02019     reduce(minDet, minOp<scalar>());
02020     reduce(sumDet, sumOp<scalar>());
02021     reduce(nSummed, sumOp<label>());
02022 
02023     if (debug || report)
02024     {
02025         if (nSummed > 0)
02026         {
02027             Info<< "    Cell determinant (wellposedness) : minimum: " << minDet
02028                 << " average: " << sumDet/nSummed
02029                 << endl;
02030         }
02031     }
02032 
02033     if (nErrorCells > 0)
02034     {
02035         if (debug || report)
02036         {
02037             Info<< " ***Cells with small determinant found, number of cells: "
02038                 << nErrorCells << endl;
02039         }
02040 
02041         return true;
02042     }
02043     else
02044     {
02045         if (debug || report)
02046         {
02047             Info<< "    Cell determinant check OK." << endl;
02048         }
02049 
02050         return false;
02051     }
02052 
02053     return false;
02054 }
02055 
02056 
02057 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
02058 
02059 bool Foam::primitiveMesh::checkTopology(const bool report) const
02060 {
02061     label noFailedChecks = 0;
02062 
02063     if (checkPoints(report)) noFailedChecks++;
02064     if (checkUpperTriangular(report)) noFailedChecks++;
02065     if (checkCellsZipUp(report)) noFailedChecks++;
02066     if (checkFaceVertices(report)) noFailedChecks++;
02067     if (checkFaceFaces(report)) noFailedChecks++;
02068     //if (checkCellDeterminant(report)) noFailedChecks++;
02069 
02070     if (noFailedChecks == 0)
02071     {
02072         if (debug || report)
02073         {
02074             Info<< "    Mesh topology OK." << endl;
02075         }
02076 
02077         return false;
02078     }
02079     else
02080     {
02081         if (debug || report)
02082         {
02083             Info<< "    Failed " << noFailedChecks
02084                 << " mesh topology checks." << endl;
02085         }
02086 
02087         return true;
02088     }
02089 }
02090 
02091 
02092 bool Foam::primitiveMesh::checkGeometry(const bool report) const
02093 {
02094     label noFailedChecks = 0;
02095 
02096     if (checkClosedBoundary(report)) noFailedChecks++;
02097     if (checkClosedCells(report)) noFailedChecks++;
02098     if (checkFaceAreas(report)) noFailedChecks++;
02099     if (checkCellVolumes(report)) noFailedChecks++;
02100     if (checkFaceOrthogonality(report)) noFailedChecks++;
02101     if (checkFacePyramids(report)) noFailedChecks++;
02102     if (checkFaceSkewness(report)) noFailedChecks++;
02103 
02104     if (noFailedChecks == 0)
02105     {
02106         if (debug || report)
02107         {
02108             Info<< "    Mesh geometry OK." << endl;
02109         }
02110         return false;
02111     }
02112     else
02113     {
02114         if (debug || report)
02115         {
02116             Info<< "    Failed " << noFailedChecks
02117                 << " mesh geometry checks." << endl;
02118         }
02119 
02120         return true;
02121     }
02122 }
02123 
02124 
02125 bool Foam::primitiveMesh::checkMesh(const bool report) const
02126 {
02127     if (debug)
02128     {
02129         Info<< "bool primitiveMesh::checkMesh(const bool report) const: "
02130             << "checking primitiveMesh" << endl;
02131     }
02132 
02133     label noFailedChecks = checkTopology(report) + checkGeometry(report);
02134 
02135     if (noFailedChecks == 0)
02136     {
02137         if (debug || report)
02138         {
02139             Info<< "Mesh OK." << endl;
02140         }
02141 
02142         return false;
02143     }
02144     else
02145     {
02146         if (debug || report)
02147         {
02148             Info<< "    Failed " << noFailedChecks
02149                 << " mesh checks." << endl;
02150         }
02151 
02152         return true;
02153     }
02154 }
02155 
02156 
02157 Foam::scalar Foam::primitiveMesh::setClosedThreshold(const scalar val)
02158 {
02159     scalar prev = closedThreshold_;
02160     closedThreshold_ = val;
02161 
02162     return prev;
02163 }
02164 
02165 
02166 Foam::scalar Foam::primitiveMesh::setAspectThreshold(const scalar val)
02167 {
02168     scalar prev = aspectThreshold_;
02169     aspectThreshold_ = val;
02170 
02171     return prev;
02172 }
02173 
02174 
02175 Foam::scalar Foam::primitiveMesh::setNonOrthThreshold(const scalar val)
02176 {
02177     scalar prev = nonOrthThreshold_;
02178     nonOrthThreshold_ = val;
02179 
02180     return prev;
02181 }
02182 
02183 
02184 Foam::scalar Foam::primitiveMesh::setSkewThreshold(const scalar val)
02185 {
02186     scalar prev = skewThreshold_;
02187     skewThreshold_ = val;
02188 
02189     return prev;
02190 }
02191 
02192 
02193 // ************************ vim: set sw=4 sts=4 et: ************************ //
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines