/*---------------------------------------------------------------------------*\ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2011-2012 OpenFOAM Foundation \\/ M anipulation | ------------------------------------------------------------------------------- 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 . 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" using namespace Foam; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // //template //void addPatchFields(const fvMesh& mesh, const word& patchFieldType) //{ // HashTable flds // ( // mesh.objectRegistry::lookupClass() // ); // // forAllConstIter(typename HashTable, flds, iter) // { // const GeoField& fld = *iter(); // // typename GeoField::GeometricBoundaryField& bfld = // const_cast // ( // fld.boundaryField() // ); // // label sz = bfld.size(); // // for (label i = 0; i < sz; i++) // { // bfld.set // ( // i, // bfld.clone(GeoField::PatchFieldType::New // ( // patchFieldType, // fld.mesh().boundary()[sz], // fld.dimensionedInternalField() // ) // ); // // // // Pout<< "fld:" << fld.name() << " had " << sz << " patches." << endl; // Pout<< "fld before:" << fld << endl; // Pout<< "adding on patch:" << fld.mesh().boundary()[sz].name() << endl; // // bfld.setSize(sz+1); // bfld.set // ( // sz, // GeoField::PatchFieldType::New // ( // patchFieldType, // fld.mesh().boundary()[sz], // fld.dimensionedInternalField() // ) // ); // // bfld[sz].operator=(pTraits::zero); // // Pout<< "fld:" << fld.name() << " now " << bfld.size() << " patches." // << endl; // // const typename GeoField::PatchFieldType& pfld = bfld[sz]; // Pout<< "pfld:" << pfld << endl; // // // Pout<< "fld value:" << fld << endl; // } //} // Remove last patch field template void trimPatchFields(fvMesh& mesh, const label nPatches) { HashTable flds ( mesh.objectRegistry::lookupClass() ); forAllConstIter(typename HashTable, flds, iter) { const GeoField& fld = *iter(); const_cast ( fld.boundaryField() ).setSize(nPatches); } } // Reorder patch field template void reorderPatchFields(fvMesh& mesh, const labelList& oldToNew) { HashTable flds ( mesh.objectRegistry::lookupClass() ); forAllConstIter(typename HashTable, flds, iter) { const GeoField& fld = *iter(); typename GeoField::GeometricBoundaryField& bfld = const_cast ( fld.boundaryField() ); bfld.reorder(oldToNew); } } //void addCalculatedPatchFields(const fvMesh& mesh) //{ // addPatchFields // ( // mesh, // calculatedFvPatchField::typeName // ); // addPatchFields // ( // mesh, // calculatedFvPatchField::typeName // ); // addPatchFields // ( // mesh, // calculatedFvPatchField::typeName // ); // addPatchFields // ( // mesh, // calculatedFvPatchField::typeName // ); // addPatchFields // ( // mesh, // calculatedFvPatchField::typeName // ); // // // Surface fields // // addPatchFields // ( // mesh, // calculatedFvPatchField::typeName // ); // addPatchFields // ( // mesh, // calculatedFvPatchField::typeName // ); // addPatchFields // ( // mesh, // calculatedFvPatchField::typeName // ); // addPatchFields // ( // mesh, // calculatedFvPatchField::typeName // ); // addPatchFields // ( // mesh, // calculatedFvPatchField::typeName // ); // // // Point fields // // addPatchFields // ( // mesh, // calculatedFvPatchField::typeName // ); // addPatchFields // ( // mesh, // calculatedFvPatchField::typeName // ); //} // // //void addAllPatchFields(fvMesh& mesh, const label insertPatchI) //{ // polyBoundaryMesh& polyPatches = // const_cast(mesh.boundaryMesh()); // fvBoundaryMesh& fvPatches = const_cast(mesh.boundary()); // // label sz = polyPatches.size(); // // addPatchFields // ( // mesh, // calculatedFvPatchField::typeName // ); // addPatchFields // ( // mesh, // calculatedFvPatchField::typeName // ); // addPatchFields // ( // mesh, // calculatedFvPatchField::typeName // ); // addPatchFields // ( // mesh, // calculatedFvPatchField::typeName // ); // addPatchFields // ( // mesh, // calculatedFvPatchField::typeName // ); // // // Surface fields // // addPatchFields // ( // mesh, // calculatedFvPatchField::typeName // ); // addPatchFields // ( // mesh, // calculatedFvPatchField::typeName // ); // addPatchFields // ( // mesh, // calculatedFvPatchField::typeName // ); // addPatchFields // ( // mesh, // calculatedFvPatchField::typeName // ); // addPatchFields // ( // mesh, // calculatedFvPatchField::typeName // ); // // // Create reordering list // // patches before insert position stay as is // labelList oldToNew(sz); // for (label i = 0; i < insertPatchI; i++) // { // oldToNew[i] = i; // } // // patches after insert position move one up // for (label i = insertPatchI; i < sz-1; i++) // { // oldToNew[i] = i+1; // } // // appended patch gets moved to insert position // oldToNew[sz-1] = insertPatchI; // // // Shuffle into place // polyPatches.reorder(oldToNew); // fvPatches.reorder(oldToNew); // // reorderPatchFields(mesh, oldToNew); // reorderPatchFields(mesh, oldToNew); // reorderPatchFields(mesh, oldToNew); // reorderPatchFields(mesh, oldToNew); // reorderPatchFields(mesh, oldToNew); // reorderPatchFields(mesh, oldToNew); // reorderPatchFields(mesh, oldToNew); // reorderPatchFields(mesh, oldToNew); // reorderPatchFields(mesh, oldToNew); // reorderPatchFields(mesh, oldToNew); //} //// Adds patch if not yet there. Returns patchID. //template //label addPatch(fvMesh& mesh, const word& patchName, const dictionary& dict) //{ // polyBoundaryMesh& polyPatches = // const_cast(mesh.boundaryMesh()); // // label patchI = polyPatches.findPatchID(patchName); // if (patchI != -1) // { // if (isA(polyPatches[patchI])) // { // // Already there // return patchI; // } // else // { // FatalErrorIn("addPatch(fvMesh&, const word&)") // << "Already have patch " << patchName // << " but of type " << PatchType::typeName // << exit(FatalError); // } // } // // // label insertPatchI = polyPatches.size(); // label startFaceI = mesh.nFaces(); // // forAll(polyPatches, patchI) // { // const polyPatch& pp = polyPatches[patchI]; // // if (isA(pp)) // { // insertPatchI = patchI; // startFaceI = pp.start(); // break; // } // } // // dictionary patchDict(dict); // patchDict.set("type", PatchType::typeName); // patchDict.set("nFaces", 0); // patchDict.set("startFace", startFaceI); // // // // Below is all quite a hack. Feel free to change once there is a better // // mechanism to insert and reorder patches. // // // Clear local fields and e.g. polyMesh parallelInfo. // mesh.clearOut(); // // label sz = polyPatches.size(); // // fvBoundaryMesh& fvPatches = const_cast(mesh.boundary()); // // // Add polyPatch at the end // polyPatches.setSize(sz+1); // polyPatches.set // ( // sz, // polyPatch::New // ( // patchName, // patchDict, // insertPatchI, // polyPatches // ) // ); // fvPatches.setSize(sz+1); // fvPatches.set // ( // sz, // fvPatch::New // ( // polyPatches[sz], // point to newly added polyPatch // mesh.boundary() // ) // ); // // addAllPatchFields(mesh, insertPatchI); // // return insertPatchI; //} // // //template //label addPatch(fvMesh& mesh, const word& patchName) //{ //Pout<< "addPatch:" << patchName << endl; // // polyBoundaryMesh& polyPatches = // const_cast(mesh.boundaryMesh()); // // label patchI = polyPatches.findPatchID(patchName); // if (patchI != -1) // { // if (isA(polyPatches[patchI])) // { // // Already there // return patchI; // } // else // { // FatalErrorIn("addPatch(fvMesh&, const word&)") // << "Already have patch " << patchName // << " but of type " << PatchType::typeName // << exit(FatalError); // } // } // // // label insertPatchI = polyPatches.size(); // label startFaceI = mesh.nFaces(); // // forAll(polyPatches, patchI) // { // const polyPatch& pp = polyPatches[patchI]; // // if (isA(pp)) // { // insertPatchI = patchI; // startFaceI = pp.start(); // break; // } // } // // // Below is all quite a hack. Feel free to change once there is a better // // mechanism to insert and reorder patches. // // // Clear local fields and e.g. polyMesh parallelInfo. // mesh.clearOut(); // // label sz = polyPatches.size(); // // fvBoundaryMesh& fvPatches = const_cast(mesh.boundary()); // // // Add polyPatch at the end // polyPatches.setSize(sz+1); // polyPatches.set // ( // sz, // polyPatch::New // ( // PatchType::typeName, // patchName, // 0, // size // startFaceI, // insertPatchI, // polyPatches // ) // ); // fvPatches.setSize(sz+1); // fvPatches.set // ( // sz, // fvPatch::New // ( // polyPatches[sz], // point to newly added polyPatch // mesh.boundary() // ) // ); // // addAllPatchFields(mesh, insertPatchI); // // return insertPatchI; //} label findPatchID(const List& newPatches, const word& name) { forAll(newPatches, i) { if (newPatches[i]->name() == name) { return i; } } return -1; } 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 { FatalErrorIn ( "addPatch(const polyBoundaryMesh&," " const word&, DynamicList)" ) << "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(); } 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 { FatalErrorIn ( "addPatch(const polyBoundaryMesh&," " const word&, DynamicList)" ) << "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(); } 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; } // Reorder and delete patches. void reorderPatches ( fvMesh& mesh, const labelList& oldToNew, const label nNewPatches ) { polyBoundaryMesh& polyPatches = const_cast(mesh.boundaryMesh()); fvBoundaryMesh& fvPatches = const_cast(mesh.boundary()); // Shuffle into place polyPatches.reorder(oldToNew); fvPatches.reorder(oldToNew); reorderPatchFields(mesh, oldToNew); reorderPatchFields(mesh, oldToNew); reorderPatchFields(mesh, oldToNew); reorderPatchFields(mesh, oldToNew); reorderPatchFields(mesh, oldToNew); reorderPatchFields(mesh, oldToNew); reorderPatchFields(mesh, oldToNew); reorderPatchFields(mesh, oldToNew); reorderPatchFields(mesh, oldToNew); reorderPatchFields(mesh, oldToNew); reorderPatchFields(mesh, oldToNew); reorderPatchFields(mesh, oldToNew); // Remove last. polyPatches.setSize(nNewPatches); fvPatches.setSize(nNewPatches); trimPatchFields(mesh, nNewPatches); trimPatchFields(mesh, nNewPatches); trimPatchFields(mesh, nNewPatches); trimPatchFields(mesh, nNewPatches); trimPatchFields(mesh, nNewPatches); trimPatchFields(mesh, nNewPatches); trimPatchFields(mesh, nNewPatches); trimPatchFields(mesh, nNewPatches); trimPatchFields(mesh, nNewPatches); trimPatchFields(mesh, nNewPatches); trimPatchFields(mesh, nNewPatches); trimPatchFields(mesh, nNewPatches); } // Remove zero-sized patches void deleteEmptyPatches(fvMesh& mesh) { const polyBoundaryMesh& patches = mesh.boundaryMesh(); wordList masterNames; if (Pstream::master()) { masterNames = patches.names(); } Pstream::scatter(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].size() == 0) { Pout<< "Deleting processor patch " << patchI << " name:" << patches[patchI].name() << endl; oldToNew[patchI] = --notUsedI; } else { oldToNew[patchI] = usedI++; } } else { // Common patch. if (returnReduce(patches[patchI].size(), sumOp