/*---------------------------------------------------------------------------*\ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2011-2016 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 . Application extrudeToRegionMesh Group grpMeshGenerationUtilities Description Extrude faceZones (internal or boundary faces) or faceSets (boundary faces only) into a separate mesh (as a different region). - used to e.g. extrude baffles (extrude internal faces) or create liquid film regions. - if extruding internal faces: - create baffles in original mesh with mappedWall patches - if extruding boundary faces: - convert boundary faces to mappedWall patches - extrude edges of faceZone as a \_sidePatch - extrude edges inbetween different faceZones as a (nonuniformTransform)cyclic \_\ - extrudes into master direction (i.e. away from the owner cell if flipMap is false) \verbatim Internal face extrusion ----------------------- +-------------+ | | | | +---AAAAAAA---+ | | | | +-------------+ AAA=faceZone to extrude. For the case of no flipMap the extrusion starts at owner and extrudes into the space of the neighbour: +CCCCCCC+ | | <= extruded mesh +BBBBBBB+ +-------------+ | | | (neighbour) | |___CCCCCCC___| <= original mesh (with 'baffles' added) | BBBBBBB | |(owner side) | | | +-------------+ BBB=mapped between owner on original mesh and new extrusion. (zero offset) CCC=mapped between neighbour on original mesh and new extrusion (offset due to the thickness of the extruded mesh) For the case of flipMap the extrusion is the other way around: from the neighbour side into the owner side. Boundary face extrusion ----------------------- +--AAAAAAA--+ | | | | +-----------+ AAA=faceZone to extrude. E.g. slave side is owner side (no flipmap) becomes +CCCCCCC+ | | <= extruded mesh +BBBBBBB+ +--BBBBBBB--+ | | <= original mesh | | +-----------+ BBB=mapped between original mesh and new extrusion CCC=polypatch Notes: - when extruding cyclics with only one cell inbetween it does not detect this as a cyclic since the face is the same face. It will only work if the coupled edge extrudes a different face so if there are more than 1 cell inbetween. \endverbatim \*---------------------------------------------------------------------------*/ #include "argList.H" #include "fvMesh.H" #include "polyTopoChange.H" #include "OFstream.H" #include "meshTools.H" #include "mappedWallPolyPatch.H" #include "createShellMesh.H" #include "syncTools.H" #include "cyclicPolyPatch.H" #include "wedgePolyPatch.H" #include "nonuniformTransformCyclicPolyPatch.H" #include "extrudeModel.H" #include "globalIndex.H" #include "faceSet.H" #include "volFields.H" #include "surfaceFields.H" #include "pointFields.H" //#include "ReadFields.H" #include "fvMeshTools.H" #include "OBJstream.H" #include "PatchTools.H" #include "processorMeshes.H" using namespace Foam; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // label findPatchID(const UList& newPatches, const word& name) { forAll(newPatches, i) { if (newPatches[i]->name() == name) { return i; } } return -1; } #ifdef FULLDEBUG void printPatches(Ostream& os, const UList& newPatches) { for (const polyPatch* ppPtr : newPatches) { const polyPatch& pp = *ppPtr; os << " name:" << pp.name() << " index:" << pp.index() << " start:" << pp.start() << " size:" << pp.size() << " type:" << pp.type() << nl; } } #endif template label addPatch ( const polyBoundaryMesh& patches, const word& patchName, DynamicList& newPatches ) { label patchi = findPatchID(newPatches, patchName); if (patchi != -1) { if (isA(*newPatches[patchi])) { // Already there return patchi; } else { FatalErrorInFunction << "Already have patch " << patchName << " but of type " << newPatches[patchi]->type() << exit(FatalError); } } patchi = newPatches.size(); label startFacei = 0; if (patchi > 0) { const polyPatch& pp = *newPatches.last(); startFacei = pp.start()+pp.size(); } #ifdef FULLDEBUG Pout<< "addPatch : starting newPatches:" << " patch:" << patchi << " startFace:" << startFacei << nl; printPatches(Pout, newPatches); Pout<< "*** end of addPatch:" << endl; #endif newPatches.append ( polyPatch::New ( PatchType::typeName, patchName, 0, // size startFacei, // nFaces patchi, patches ).ptr() ); return patchi; } template label addPatch ( const polyBoundaryMesh& patches, const word& patchName, const dictionary& dict, DynamicList& newPatches ) { label patchi = findPatchID(newPatches, patchName); if (patchi != -1) { if (isA(*newPatches[patchi])) { // Already there return patchi; } else { FatalErrorInFunction << "Already have patch " << patchName << " but of type " << newPatches[patchi]->type() << exit(FatalError); } } patchi = newPatches.size(); label startFacei = 0; if (patchi > 0) { const polyPatch& pp = *newPatches.last(); startFacei = pp.start()+pp.size(); } #ifdef FULLDEBUG Pout<< "addPatch : starting newPatches:" << " patch:" << patchi << " startFace:" << startFacei << nl; printPatches(Pout, newPatches); Pout<< "*** end of addPatch:" << endl; #endif dictionary patchDict(dict); patchDict.set("type", PatchType::typeName); patchDict.set("nFaces", 0); patchDict.set("startFace", startFacei); newPatches.append ( polyPatch::New ( patchName, patchDict, patchi, patches ).ptr() ); return patchi; } // Remove zero-sized patches void deleteEmptyPatches(fvMesh& mesh) { const polyBoundaryMesh& patches = mesh.boundaryMesh(); wordList masterNames; if (Pstream::master()) { masterNames = patches.names(); } Pstream::broadcast(masterNames); labelList oldToNew(patches.size(), -1); label usedI = 0; label notUsedI = patches.size(); // Add all the non-empty, non-processor patches forAll(masterNames, masterI) { label patchi = patches.findPatchID(masterNames[masterI]); if (patchi != -1) { if (isA(patches[patchi])) { // Similar named processor patch? Not 'possible'. if (patches[patchi].empty()) { Pout<< "Deleting processor patch " << patchi << " name:" << patches[patchi].name() << endl; oldToNew[patchi] = --notUsedI; } else { oldToNew[patchi] = usedI++; } } else { // Common patch. if (returnReduceAnd(patches[patchi].empty())) { Pout<< "Deleting patch " << patchi << " name:" << patches[patchi].name() << endl; oldToNew[patchi] = --notUsedI; } else { oldToNew[patchi] = usedI++; } } } } // Add remaining patches at the end forAll(patches, patchi) { if (oldToNew[patchi] == -1) { // Unique to this processor. Note: could check that these are // only processor patches. if (patches[patchi].empty()) { Pout<< "Deleting processor patch " << patchi << " name:" << patches[patchi].name() << endl; oldToNew[patchi] = --notUsedI; } else { oldToNew[patchi] = usedI++; } } } fvMeshTools::reorderPatches(mesh, oldToNew, usedI, true); } // Check zone either all internal or all external faces void checkZoneInside ( const polyMesh& mesh, const wordList& zoneNames, const labelList& zoneID, const labelList& extrudeMeshFaces, const boolList& isInternal ) { forAll(zoneNames, i) { if (isInternal[i]) { Info<< "Zone " << zoneNames[i] << " has internal faces" << endl; } else { Info<< "Zone " << zoneNames[i] << " has boundary faces" << endl; } } forAll(extrudeMeshFaces, i) { label facei = extrudeMeshFaces[i]; label zoneI = zoneID[i]; if (isInternal[zoneI] != mesh.isInternalFace(facei)) { FatalErrorInFunction << "Zone " << zoneNames[zoneI] << " is not consistently all internal or all boundary faces." << " Face " << facei << " at " << mesh.faceCentres()[facei] << " is the first occurrence." << exit(FatalError); } } } // Calculate global pp faces per pp edge. labelListList globalEdgeFaces ( const polyMesh& mesh, const globalIndex& globalFaces, const primitiveFacePatch& pp, const labelList& ppMeshEdges ) { // From mesh edge to global pp face labels. labelListList globalEdgeFaces(ppMeshEdges.size()); const labelListList& edgeFaces = pp.edgeFaces(); forAll(edgeFaces, edgeI) { // Store pp face and processor as unique tag. globalEdgeFaces[edgeI] = globalFaces.toGlobal(edgeFaces[edgeI]); } // Synchronise across coupled edges. syncTools::syncEdgeList ( mesh, ppMeshEdges, globalEdgeFaces, ListOps::uniqueEqOp