/*---------------------------------------------------------------------------*\ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2011-2014 OpenFOAM Foundation Copyright (C) 2015-2025 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. OpenFOAM is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. OpenFOAM is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenFOAM. If not, see . \*---------------------------------------------------------------------------*/ #include "meshRefinement.H" #include "fvMesh.H" #include "syncTools.H" #include "Time.H" #include "refinementSurfaces.H" #include "faceSet.H" #include "indirectPrimitivePatch.H" #include "polyTopoChange.H" #include "meshTools.H" #include "polyModifyFace.H" #include "polyModifyCell.H" #include "polyAddFace.H" #include "polyRemoveFace.H" #include "localPointRegion.H" #include "duplicatePoints.H" #include "regionSplit.H" #include "removeCells.H" #include "unitConversion.H" #include "OBJstream.H" #include "patchFaceOrientation.H" #include "PatchEdgeFaceWave.H" #include "edgeTopoDistanceData.H" #include "polyMeshAdder.H" #include "IOmanip.H" #include "refinementParameters.H" #include "shellSurfaces.H" #include "zeroGradientFvPatchFields.H" #include "volFields.H" #include "holeToFace.H" #include "FaceCellWave.H" #include "wallPoints.H" #include "searchableSurfaces.H" // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // Foam::label Foam::meshRefinement::createBaffle ( const label faceI, const label ownPatch, const label neiPatch, polyTopoChange& meshMod ) const { const face& f = mesh_.faces()[faceI]; label zoneID = mesh_.faceZones().whichZone(faceI); bool zoneFlip = false; if (zoneID >= 0) { const faceZone& fZone = mesh_.faceZones()[zoneID]; zoneFlip = fZone.flipMap()[fZone.whichFace(faceI)]; } meshMod.setAction ( polyModifyFace ( f, // modified face faceI, // label of face mesh_.faceOwner()[faceI], // owner -1, // neighbour false, // face flip ownPatch, // patch for face false, // remove from zone zoneID, // zone for face zoneFlip // face flip in zone ) ); label dupFaceI = -1; if (mesh_.isInternalFace(faceI)) { if (neiPatch == -1) { FatalErrorInFunction << "No neighbour patch for internal face " << faceI << " fc:" << mesh_.faceCentres()[faceI] << " ownPatch:" << ownPatch << abort(FatalError); } bool reverseFlip = false; if (zoneID >= 0) { reverseFlip = !zoneFlip; } dupFaceI = meshMod.setAction ( polyAddFace ( f.reverseFace(), // modified face mesh_.faceNeighbour()[faceI],// owner -1, // neighbour -1, // masterPointID -1, // masterEdgeID faceI, // masterFaceID, true, // face flip neiPatch, // patch for face zoneID, // zone for face reverseFlip // face flip in zone ) ); } return dupFaceI; } //// Check if we are a boundary face and normal of surface does //// not align with test vector. In this case there'd probably be //// a freestanding 'baffle' so we might as well not create it. //// Note that since it is not a proper baffle we cannot detect it //// afterwards so this code cannot be merged with the //// filterDuplicateFaces code. //bool Foam::meshRefinement::validBaffleTopology //( // const label faceI, // const vector& n1, // const vector& n2, // const vector& testDir //) const //{ // // label patchI = mesh_.boundaryMesh().whichPatch(faceI); // if (patchI == -1 || mesh_.boundaryMesh()[patchI].coupled()) // { // return true; // } // else if (mag(n1&n2) > cos(degToRad(30.0))) // { // // Both normals aligned. Check that test vector perpendicularish to // // surface normal // scalar magTestDir = mag(testDir); // if (magTestDir > VSMALL) // { // if (mag(n1&(testDir/magTestDir)) < cos(degToRad(45.0))) // { // //Pout<< "** disabling baffling face " // // << mesh_.faceCentres()[faceI] << endl; // return false; // } // } // } // return true; //} void Foam::meshRefinement::getIntersections ( const labelList& surfacesToTest, const pointField& neiCc, const labelList& testFaces, labelList& globalRegion1, labelList& globalRegion2 ) const { autoPtr str; if (debug&OBJINTERSECTIONS) { mkDir(mesh_.time().path()/timeName()); str.reset ( new OBJstream ( mesh_.time().path()/timeName()/"intersections.obj" ) ); Pout<< "getIntersections : Writing surface intersections to file " << str().name() << nl << endl; } globalRegion1.setSize(mesh_.nFaces()); globalRegion1 = -1; globalRegion2.setSize(mesh_.nFaces()); globalRegion2 = -1; // Collect segments // ~~~~~~~~~~~~~~~~ pointField start(testFaces.size()); pointField end(testFaces.size()); { labelList minLevel; calcCellCellRays ( neiCc, labelList(neiCc.size(), -1), testFaces, start, end, minLevel ); } // Do test for intersections // ~~~~~~~~~~~~~~~~~~~~~~~~~ labelList surface1; List hit1; labelList region1; labelList surface2; List hit2; labelList region2; surfaces_.findNearestIntersection ( surfacesToTest, start, end, surface1, hit1, region1, surface2, hit2, region2 ); forAll(testFaces, i) { label faceI = testFaces[i]; if (hit1[i].hit() && hit2[i].hit()) { if (str) { str().writeLine(start[i], hit1[i].point()); str().writeLine(hit1[i].point(), hit2[i].point()); str().writeLine(hit2[i].point(), end[i]); } // Pick up the patches globalRegion1[faceI] = surfaces_.globalRegion(surface1[i], region1[i]); globalRegion2[faceI] = surfaces_.globalRegion(surface2[i], region2[i]); if (globalRegion1[faceI] == -1 || globalRegion2[faceI] == -1) { FatalErrorInFunction << "problem." << abort(FatalError); } } } } void Foam::meshRefinement::getBafflePatches ( const label nErodeCellZones, const labelList& globalToMasterPatch, const pointField& locationsInMesh, const wordList& zonesInMesh, const pointField& locationsOutsideMesh, const bool exitIfLeakPath, const refPtr& leakPathFormatter, refPtr& surfFormatter, const labelList& neiLevel, const pointField& neiCc, labelList& ownPatch, labelList& neiPatch ) const { // This determines the patches for the intersected faces to // - remove the outside of the mesh // - introduce baffles for (non-faceZone) intersections // Any baffles for faceZones (faceType 'baffle'/'boundary') get introduced // later // 1. Determine intersections with unnamed surfaces and cell zones // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Notice that this also does hole-closure so the unnamed* is not just // the surface intersections. labelList cellToZone; labelList unnamedRegion1; labelList unnamedRegion2; labelList namedSurfaceRegion; { bitSet posOrientation; zonify ( true, // allowFreeStandingZoneFaces nErodeCellZones, -2, // zone to put unreached cells into locationsInMesh, zonesInMesh, locationsOutsideMesh, exitIfLeakPath, leakPathFormatter, surfFormatter, cellToZone, unnamedRegion1, unnamedRegion2, namedSurfaceRegion, posOrientation ); } // 2. Baffle all boundary faces // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // The logic is that all intersections with unnamed surfaces become // baffles except where we're inbetween a cellZone and background // or inbetween two different cellZones. labelList neiCellZone; syncTools::swapBoundaryCellList(mesh_, cellToZone, neiCellZone); ownPatch.setSize(mesh_.nFaces()); ownPatch = -1; neiPatch.setSize(mesh_.nFaces()); neiPatch = -1; forAll(ownPatch, faceI) { if (unnamedRegion1[faceI] != -1 || unnamedRegion2[faceI] != -1) { label ownMasterPatch = -1; if (unnamedRegion1[faceI] != -1) { ownMasterPatch = globalToMasterPatch[unnamedRegion1[faceI]]; } label neiMasterPatch = -1; if (unnamedRegion2[faceI] != -1) { neiMasterPatch = globalToMasterPatch[unnamedRegion2[faceI]]; } // Now we always want to produce a baffle except if // one side is a valid cellZone label ownZone = cellToZone[mesh_.faceOwner()[faceI]]; label neiZone = -1; if (mesh_.isInternalFace(faceI)) { neiZone = cellToZone[mesh_.faceNeighbour()[faceI]]; } else { neiZone = neiCellZone[faceI-mesh_.nInternalFaces()]; } if ( (ownZone != neiZone) && ( (ownZone >= 0 && neiZone != -2) || (neiZone >= 0 && ownZone != -2) ) && ( namedSurfaceRegion.size() == 0 || namedSurfaceRegion[faceI] == -1 ) ) { // One side is a valid cellZone and the neighbour is a different // one (or -1 but not -2). Do not baffle unless the user has // put both an unnamed and a named surface there. In that // case assume that the user wants both: baffle and faceZone. } else { ownPatch[faceI] = ownMasterPatch; neiPatch[faceI] = neiMasterPatch; } } } // No need to parallel sync since intersection data (surfaceIndex_ etc.) // already guaranteed to be synced... // However: // - owncc and neicc are reversed on different procs so might pick // up different regions reversed? No problem. Neighbour on one processor // might not be owner on the other processor but the neighbour is // not used when creating baffles from proc faces. // - tolerances issues occasionally crop up. syncTools::syncFaceList(mesh_, ownPatch, maxEqOp