/*---------------------------------------------------------------------------*\ ========= | \\ / 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-2021 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 extrudeMesh Group grpMeshGenerationUtilities Description Extrude mesh from existing patch (by default outwards facing normals; optional flips faces) or from patch read from file. Note: Merges close points so be careful. Type of extrusion prescribed by run-time selectable model. \*---------------------------------------------------------------------------*/ #include "argList.H" #include "Time.H" #include "polyTopoChange.H" #include "polyTopoChanger.H" #include "edgeCollapser.H" #include "perfectInterface.H" #include "addPatchCellLayer.H" #include "fvMesh.H" #include "MeshedSurfaces.H" #include "globalIndex.H" #include "cellSet.H" #include "fvMeshTools.H" #include "extrudedMesh.H" #include "extrudeModel.H" #include "wedge.H" #include "wedgePolyPatch.H" #include "planeExtrusion.H" #include "emptyPolyPatch.H" #include "processorMeshes.H" #include "hexRef8Data.H" using namespace Foam; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // enum ExtrudeMode { MESH, PATCH, SURFACE }; static const Enum ExtrudeModeNames { { ExtrudeMode::MESH, "mesh" }, { ExtrudeMode::PATCH, "patch" }, { ExtrudeMode::SURFACE, "surface" }, }; label findPatchID(const polyBoundaryMesh& patches, const word& name) { const label patchID = patches.findPatchID(name); if (patchID == -1) { FatalErrorInFunction << "Cannot find patch " << name << " in the source mesh.\n" << "Valid patch names are " << patches.names() << exit(FatalError); } return patchID; } labelList patchFaces(const polyBoundaryMesh& patches, const wordList& names) { label n = 0; forAll(names, i) { const polyPatch& pp = patches[findPatchID(patches, names[i])]; n += pp.size(); } labelList faceLabels(n); n = 0; forAll(names, i) { const polyPatch& pp = patches[findPatchID(patches, names[i])]; forAll(pp, j) { faceLabels[n++] = pp.start()+j; } } return faceLabels; } void zoneFaces ( const faceZoneMesh& fzs, const wordList& names, labelList& faceLabels, bitSet& faceFlip ) { label n = 0; forAll(names, i) { const auto& pp = fzs[fzs.findZoneID(names[i])]; n += pp.size(); } faceLabels.setSize(n); faceFlip.setSize(n); n = 0; forAll(names, i) { const auto& pp = fzs[fzs.findZoneID(names[i])]; const boolList& ppFlip = pp.flipMap(); forAll(pp, i) { faceLabels[n] = pp[i]; faceFlip[n] = ppFlip[i]; n++; } } } void updateFaceLabels(const mapPolyMesh& map, labelList& faceLabels) { const labelList& reverseMap = map.reverseFaceMap(); labelList newFaceLabels(faceLabels.size()); label newI = 0; forAll(faceLabels, i) { label oldFacei = faceLabels[i]; if (reverseMap[oldFacei] >= 0) { newFaceLabels[newI++] = reverseMap[oldFacei]; } } newFaceLabels.setSize(newI); faceLabels.transfer(newFaceLabels); } void updateCellSet(const mapPolyMesh& map, labelHashSet& cellLabels) { const labelList& reverseMap = map.reverseCellMap(); labelHashSet newCellLabels(2*cellLabels.size()); forAll(cellLabels, i) { label oldCelli = cellLabels[i]; if (reverseMap[oldCelli] >= 0) { newCellLabels.insert(reverseMap[oldCelli]); } } cellLabels.transfer(newCellLabels); } template void changeFrontBackPatches ( polyMesh& mesh, const word& frontPatchName, const word& backPatchName ) { const polyBoundaryMesh& patches = mesh.boundaryMesh(); label frontPatchi = findPatchID(patches, frontPatchName); label backPatchi = findPatchID(patches, backPatchName); DynamicList newPatches(patches.size()); forAll(patches, patchi) { const polyPatch& pp(patches[patchi]); if (patchi == frontPatchi || patchi == backPatchi) { newPatches.append ( new PatchType ( pp.name(), pp.size(), pp.start(), pp.index(), mesh.boundaryMesh(), PatchType::typeName ) ); } else { newPatches.append(pp.clone(mesh.boundaryMesh()).ptr()); } } // Edit patches mesh.removeBoundary(); mesh.addPatches(newPatches, true); } int main(int argc, char *argv[]) { argList::addNote ( "Extrude mesh from existing patch." ); #include "addRegionOption.H" argList::addOption("dict", "file", "Alternative extrudeMeshDict"); #include "setRootCase.H" #include "createTimeExtruded.H" // Get optional regionName word regionName; word regionDir; if (args.readIfPresent("region", regionName)) { regionDir = regionName; Info<< "Create mesh " << regionName << " for time = " << runTimeExtruded.timeName() << nl << endl; } else { regionName = fvMesh::defaultRegion; Info<< "Create mesh for time = " << runTimeExtruded.timeName() << nl << endl; } const IOdictionary dict ( IOobject::selectIO ( IOobject ( "extrudeMeshDict", runTimeExtruded.system(), runTimeExtruded, IOobject::MUST_READ_IF_MODIFIED, IOobject::NO_WRITE ), args.getOrDefault("dict", "") ) ); // Point generator autoPtr model(extrudeModel::New(dict)); // Whether to flip normals const bool flipNormals(dict.get("flipNormals")); // What to extrude const ExtrudeMode mode = ExtrudeModeNames.get("constructFrom", dict); // Any merging of small edges const scalar mergeTol(dict.getOrDefault("mergeTol", 1e-4)); Info<< "Extruding from " << ExtrudeModeNames[mode] << " using model " << model().type() << endl; if (flipNormals) { Info<< "Flipping normals before extruding" << endl; } if (mergeTol > 0) { Info<< "Collapsing edges < " << mergeTol << " of bounding box" << endl; } else { Info<< "Not collapsing any edges after extrusion" << endl; } Info<< endl; // Generated mesh (one of either) autoPtr meshFromMesh; autoPtr meshFromSurface; // Faces on front and back for stitching (in case of mergeFaces) word frontPatchName; labelList frontPatchFaces; word backPatchName; labelList backPatchFaces; // Optional added cells (get written to cellSet) labelHashSet addedCellsSet; // Optional refinement data autoPtr refDataPtr; if (mode == PATCH || mode == MESH) { if (flipNormals && mode == MESH) { FatalErrorInFunction << "Flipping normals not supported for extrusions from mesh." << exit(FatalError); } fileName sourceCasePath(dict.get("sourceCase").expand()); fileName sourceRootDir = sourceCasePath.path(); fileName sourceCaseDir = sourceCasePath.name(); if (Pstream::parRun()) { sourceCaseDir = sourceCaseDir/("processor" + Foam::name(Pstream::myProcNo())); } wordList sourcePatches; wordList sourceFaceZones; if ( dict.readIfPresent ( "sourcePatches", sourcePatches, keyType::LITERAL ) ) { if (sourcePatches.size() == 1) { frontPatchName = sourcePatches[0]; } Info<< "Extruding patches " << sourcePatches << " on mesh " << sourceCasePath << nl << endl; } else { dict.readEntry("sourceFaceZones", sourceFaceZones); Info<< "Extruding faceZones " << sourceFaceZones << " on mesh " << sourceCasePath << nl << endl; } Time runTime ( Time::controlDictName, sourceRootDir, sourceCaseDir ); #include "createNamedMesh.H" const polyBoundaryMesh& patches = mesh.boundaryMesh(); // Extrusion engine. Either adding to existing mesh or // creating separate mesh. addPatchCellLayer layerExtrude(mesh, (mode == MESH)); labelList meshFaces; bitSet faceFlips; if (sourceFaceZones.size()) { zoneFaces(mesh.faceZones(), sourceFaceZones, meshFaces, faceFlips); } else { meshFaces = patchFaces(patches, sourcePatches); faceFlips.setSize(meshFaces.size()); } if (mode == PATCH && flipNormals) { // Cheat. Flip patch faces in mesh. This invalidates the // mesh (open cells) but does produce the correct extrusion. polyTopoChange meshMod(mesh); forAll(meshFaces, i) { label meshFacei = meshFaces[i]; label patchi = patches.whichPatch(meshFacei); label own = mesh.faceOwner()[meshFacei]; label nei = -1; if (patchi == -1) { nei = mesh.faceNeighbour()[meshFacei]; } label zoneI = mesh.faceZones().whichZone(meshFacei); bool zoneFlip = false; if (zoneI != -1) { label index = mesh.faceZones()[zoneI].whichFace(meshFacei); zoneFlip = mesh.faceZones()[zoneI].flipMap()[index]; } meshMod.modifyFace ( mesh.faces()[meshFacei].reverseFace(), // modified face meshFacei, // label of face own, // owner nei, // neighbour true, // face flip patchi, // patch for face zoneI, // zone for face zoneFlip // face flip in zone ); } // Change the mesh. No inflation. autoPtr map = meshMod.changeMesh(mesh, false); // Update fields mesh.updateMesh(map()); // Move mesh (since morphing does not do this) if (map().hasMotionPoints()) { mesh.movePoints(map().preMotionPoints()); } } indirectPrimitivePatch extrudePatch ( IndirectList ( mesh.faces(), meshFaces ), mesh.points() ); // Determine extrudePatch normal pointField extrudePatchPointNormals ( PatchTools::pointNormals(mesh, extrudePatch, faceFlips) ); // Precalculate mesh edges for pp.edges. const labelList meshEdges ( extrudePatch.meshEdges ( mesh.edges(), mesh.pointEdges() ) ); // Global face indices engine const globalIndex globalFaces(mesh.nFaces()); // Determine extrudePatch.edgeFaces in global numbering (so across // coupled patches) labelListList edgeGlobalFaces ( addPatchCellLayer::globalEdgeFaces ( mesh, globalFaces, extrudePatch ) ); // Determine what patches boundary edges need to get extruded into. // This might actually cause edge-connected processors to become // face-connected so might need to introduce new processor boundaries. // Calculates: // - per pp.edge the patch to extrude into // - any additional processor boundaries (patchToNbrProc = map from // new patchID to neighbour processor) // - number of new patches (nPatches) labelList edgePatchID; labelList edgeZoneID; boolList edgeFlip; labelList inflateFaceID; label nPatches; Map