Skip to content
Snippets Groups Projects
splitMeshRegions.C 50.8 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*---------------------------------------------------------------------------*\
      =========                 |
      \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
       \\    /   O peration     |
    
    Mark Olesen's avatar
    Mark Olesen committed
        \\  /    A nd           | Copyright (C) 1991-2009 OpenCFD Ltd.
    
         \\/     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 2 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, write to the Free Software Foundation,
        Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
    
    Description
    
        Splits mesh into multiple regions.
    
        Each region is defined as a domain whose cells can all be reached by
        cell-face-cell walking without crossing
    
        - boundary faces
        - additional faces from faceset (-blockedFaces faceSet).
        - any face inbetween differing cellZones (-cellZones)
    
        Output is:
    
        - mesh with multiple regions or
    
        - mesh with cells put into cellZones (-makeCellZones)
    
    
        - Should work in parallel.
        cellZones can differ on either side of processor boundaries in which case
        the faces get moved from processor patch to directMapped patch. Not
        ery well tested.
    
        - If a cell zone gets split into more than one region it can detect
        the largest matching region (-sloppyCellZones). This will accept any
        region that covers more than 50% of the zone. It has to be a subset
        so cannot have any cells in any other zone.
    
        - useCellZonesOnly does not do a walk and uses the cellZones only. Use
        this if you don't mind having disconnected domains in a single region.
    
    mattijs's avatar
    mattijs committed
        This option requires all cells to be in one (and one only) cellZone.
    
    
    \*---------------------------------------------------------------------------*/
    
    #include "SortableList.H"
    #include "argList.H"
    #include "regionSplit.H"
    #include "fvMeshSubset.H"
    #include "IOobjectList.H"
    #include "volFields.H"
    #include "faceSet.H"
    #include "cellSet.H"
    #include "polyTopoChange.H"
    #include "removeCells.H"
    #include "EdgeMap.H"
    #include "syncTools.H"
    #include "ReadFields.H"
    
    #include "directMappedWallPolyPatch.H"
    
    
    using namespace Foam;
    
    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
    
    template<class GeoField>
    void addPatchFields(fvMesh& mesh, const word& patchFieldType)
    {
        HashTable<const GeoField*> flds
        (
            mesh.objectRegistry::lookupClass<GeoField>()
        );
    
        for
        (
            typename HashTable<const GeoField*>::const_iterator iter = flds.begin();
            iter != flds.end();
            ++iter
        )
        {
            const GeoField& fld = *iter();
    
            typename GeoField::GeometricBoundaryField& bfld =
                const_cast<typename GeoField::GeometricBoundaryField&>
                (
                    fld.boundaryField()
                );
    
            label sz = bfld.size();
            bfld.setSize(sz+1);
            bfld.set
            (
                sz,
                GeoField::PatchFieldType::New
                (
                    patchFieldType,
                    mesh.boundary()[sz],
                    fld.dimensionedInternalField()
                )
            );
        }
    }
    
    
    // Remove last patch field
    template<class GeoField>
    void trimPatchFields(fvMesh& mesh, const label nPatches)
    {
        HashTable<const GeoField*> flds
        (
            mesh.objectRegistry::lookupClass<GeoField>()
        );
    
        for
        (
            typename HashTable<const GeoField*>::const_iterator iter = flds.begin();
            iter != flds.end();
            ++iter
        )
        {
            const GeoField& fld = *iter();
    
            const_cast<typename GeoField::GeometricBoundaryField&>
            (
                fld.boundaryField()
            ).setSize(nPatches);
        }
    }
    
    
    // Reorder patch field
    template<class GeoField>
    void reorderPatchFields(fvMesh& mesh, const labelList& oldToNew)
    {
        HashTable<const GeoField*> flds
        (
            mesh.objectRegistry::lookupClass<GeoField>()
        );
    
        for
        (
            typename HashTable<const GeoField*>::const_iterator iter = flds.begin();
            iter != flds.end();
            ++iter
        )
        {
            const GeoField& fld = *iter();
    
            typename GeoField::GeometricBoundaryField& bfld =
                const_cast<typename GeoField::GeometricBoundaryField&>
                (
                    fld.boundaryField()
                );
    
            bfld.reorder(oldToNew);
        }
    }
    
    
    // Adds patch if not yet there. Returns patchID.
    
    template<class PatchType>
    label addPatch(fvMesh& mesh, const word& patchName)
    
    {
        polyBoundaryMesh& polyPatches =
            const_cast<polyBoundaryMesh&>(mesh.boundaryMesh());
    
        label patchI = polyPatches.findPatchID(patchName);
        if (patchI != -1)
        {
    
            if (isA<PatchType>(polyPatches[patchI]))
    
            {
                // Already there
                return patchI;
            }
    
            else
            {
                FatalErrorIn("addPatch<PatchType>(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<processorPolyPatch>(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<fvBoundaryMesh&>(mesh.boundary());
    
        // Add polyPatch at the end
        polyPatches.setSize(sz+1);
        polyPatches.set
        (
            sz,
            polyPatch::New
            (
    
                patchName,
                0,              // size
                startFaceI,
                insertPatchI,
                polyPatches
            )
        );
        fvPatches.setSize(sz+1);
        fvPatches.set
        (
            sz,
            fvPatch::New
            (
                polyPatches[sz],  // point to newly added polyPatch
                mesh.boundary()
            )
        );
    
        addPatchFields<volScalarField>
        (
            mesh,
            calculatedFvPatchField<scalar>::typeName
        );
        addPatchFields<volVectorField>
        (
            mesh,
            calculatedFvPatchField<vector>::typeName
        );
        addPatchFields<volSphericalTensorField>
        (
            mesh,
            calculatedFvPatchField<sphericalTensor>::typeName
        );
        addPatchFields<volSymmTensorField>
        (
            mesh,
            calculatedFvPatchField<symmTensor>::typeName
        );
        addPatchFields<volTensorField>
        (
            mesh,
            calculatedFvPatchField<tensor>::typeName
        );
    
        // Surface fields
    
        addPatchFields<surfaceScalarField>
        (
            mesh,
            calculatedFvPatchField<scalar>::typeName
        );
        addPatchFields<surfaceVectorField>
        (
            mesh,
            calculatedFvPatchField<vector>::typeName
        );
        addPatchFields<surfaceSphericalTensorField>
        (
            mesh,
            calculatedFvPatchField<sphericalTensor>::typeName
        );
        addPatchFields<surfaceSymmTensorField>
        (
            mesh,
            calculatedFvPatchField<symmTensor>::typeName
        );
        addPatchFields<surfaceTensorField>
        (
            mesh,
            calculatedFvPatchField<tensor>::typeName
        );
    
        // Create reordering list
        // patches before insert position stay as is
        labelList oldToNew(sz+1);
        for (label i = 0; i < insertPatchI; i++)
        {
            oldToNew[i] = i;
        }
        // patches after insert position move one up
        for (label i = insertPatchI; i < sz; i++)
        {
            oldToNew[i] = i+1;
        }
        // appended patch gets moved to insert position
        oldToNew[sz] = insertPatchI;
    
        // Shuffle into place
        polyPatches.reorder(oldToNew);
        fvPatches.reorder(oldToNew);
    
        reorderPatchFields<volScalarField>(mesh, oldToNew);
        reorderPatchFields<volVectorField>(mesh, oldToNew);
        reorderPatchFields<volSphericalTensorField>(mesh, oldToNew);
        reorderPatchFields<volSymmTensorField>(mesh, oldToNew);
        reorderPatchFields<volTensorField>(mesh, oldToNew);
        reorderPatchFields<surfaceScalarField>(mesh, oldToNew);
        reorderPatchFields<surfaceVectorField>(mesh, oldToNew);
        reorderPatchFields<surfaceSphericalTensorField>(mesh, oldToNew);
        reorderPatchFields<surfaceSymmTensorField>(mesh, oldToNew);
        reorderPatchFields<surfaceTensorField>(mesh, oldToNew);
    
        return insertPatchI;
    }
    
    
    // Reorder and delete patches.
    void reorderPatches
    (
        fvMesh& mesh,
        const labelList& oldToNew,
        const label nNewPatches
    )
    {
        polyBoundaryMesh& polyPatches =
            const_cast<polyBoundaryMesh&>(mesh.boundaryMesh());
        fvBoundaryMesh& fvPatches = const_cast<fvBoundaryMesh&>(mesh.boundary());
    
        // Shuffle into place
        polyPatches.reorder(oldToNew);
        fvPatches.reorder(oldToNew);
    
        reorderPatchFields<volScalarField>(mesh, oldToNew);
        reorderPatchFields<volVectorField>(mesh, oldToNew);
        reorderPatchFields<volSphericalTensorField>(mesh, oldToNew);
        reorderPatchFields<volSymmTensorField>(mesh, oldToNew);
        reorderPatchFields<volTensorField>(mesh, oldToNew);
        reorderPatchFields<surfaceScalarField>(mesh, oldToNew);
        reorderPatchFields<surfaceVectorField>(mesh, oldToNew);
        reorderPatchFields<surfaceSphericalTensorField>(mesh, oldToNew);
        reorderPatchFields<surfaceSymmTensorField>(mesh, oldToNew);
        reorderPatchFields<surfaceTensorField>(mesh, oldToNew);
    
        // Remove last.
        polyPatches.setSize(nNewPatches);
        fvPatches.setSize(nNewPatches);
        trimPatchFields<volScalarField>(mesh, nNewPatches);
        trimPatchFields<volVectorField>(mesh, nNewPatches);
        trimPatchFields<volSphericalTensorField>(mesh, nNewPatches);
        trimPatchFields<volSymmTensorField>(mesh, nNewPatches);
        trimPatchFields<volTensorField>(mesh, nNewPatches);
        trimPatchFields<surfaceScalarField>(mesh, nNewPatches);
        trimPatchFields<surfaceVectorField>(mesh, nNewPatches);
        trimPatchFields<surfaceSphericalTensorField>(mesh, nNewPatches);
        trimPatchFields<surfaceSymmTensorField>(mesh, nNewPatches);
        trimPatchFields<surfaceTensorField>(mesh, nNewPatches);
    }
    
    
    template<class GeoField>
    void subsetVolFields
    (
        const fvMesh& mesh,
        const fvMesh& subMesh,
        const labelList& cellMap,
        const labelList& faceMap
    )
    {
        const labelList patchMap(identity(mesh.boundaryMesh().size()));
    
        HashTable<const GeoField*> fields
        (
            mesh.objectRegistry::lookupClass<GeoField>()
        );
        forAllConstIter(typename HashTable<const GeoField*>, fields, iter)
        {
            const GeoField& fld = *iter();
    
            Info<< "Mapping field " << fld.name() << endl;
    
            tmp<GeoField> tSubFld
            (
                fvMeshSubset::interpolate
                (
                    fld,
                    subMesh,
                    patchMap,
                    cellMap,
                    faceMap
                )
            );
    
            // Hack: set value to 0 for introduced patches (since don't
            //       get initialised.
            forAll(tSubFld().boundaryField(), patchI)
            {
                const fvPatchField<typename GeoField::value_type>& pfld =
                    tSubFld().boundaryField()[patchI];
    
                if
                (
                    isA<calculatedFvPatchField<typename GeoField::value_type> >
                    (pfld)
                )
                {
                    tSubFld().boundaryField()[patchI] ==
                        pTraits<typename GeoField::value_type>::zero;
                }
            }
    
            // Store on subMesh
            GeoField* subFld = tSubFld.ptr();
            subFld->rename(fld.name());
            subFld->writeOpt() = IOobject::AUTO_WRITE;
            subFld->store();
        }
    }
    
    
    template<class GeoField>
    void subsetSurfaceFields
    (
        const fvMesh& mesh,
        const fvMesh& subMesh,
        const labelList& faceMap
    )
    {
        const labelList patchMap(identity(mesh.boundaryMesh().size()));
    
        HashTable<const GeoField*> fields
        (
            mesh.objectRegistry::lookupClass<GeoField>()
        );
        forAllConstIter(typename HashTable<const GeoField*>, fields, iter)
        {
            const GeoField& fld = *iter();
    
            Info<< "Mapping field " << fld.name() << endl;
    
            tmp<GeoField> tSubFld
            (
                fvMeshSubset::interpolate
                (
                    fld,
                    subMesh,
                    patchMap,
                    faceMap
                )
            );
    
            // Hack: set value to 0 for introduced patches (since don't
            //       get initialised.
            forAll(tSubFld().boundaryField(), patchI)
            {
                const fvsPatchField<typename GeoField::value_type>& pfld =
                    tSubFld().boundaryField()[patchI];
    
                if
                (
                    isA<calculatedFvsPatchField<typename GeoField::value_type> >
                    (pfld)
                )
                {
                    tSubFld().boundaryField()[patchI] ==
                        pTraits<typename GeoField::value_type>::zero;
                }
            }
    
            // Store on subMesh
            GeoField* subFld = tSubFld.ptr();
            subFld->rename(fld.name());
            subFld->writeOpt() = IOobject::AUTO_WRITE;
            subFld->store();
        }
    }
    
    // Select all cells not in the region
    labelList getNonRegionCells(const labelList& cellRegion, const label regionI)
    {
        DynamicList<label> nonRegionCells(cellRegion.size());
        forAll(cellRegion, cellI)
        {
            if (cellRegion[cellI] != regionI)
            {
                nonRegionCells.append(cellI);
            }
        }
        return nonRegionCells.shrink();
    }
    
    
    
    mattijs's avatar
    mattijs committed
    // Get per region-region interface the sizes. If sumParallel sums sizes.
    // Returns interfaces as straight list for looping in identical order.
    void getInterfaceSizes
    
    (
        const polyMesh& mesh,
        const labelList& cellRegion,
    
    mattijs's avatar
    mattijs committed
        const bool sumParallel,
    
        edgeList& interfaces,
        EdgeMap<label>& interfaceSizes
    
    
        // Internal faces
        // ~~~~~~~~~~~~~~
    
    
        forAll(mesh.faceNeighbour(), faceI)
        {
            label ownRegion = cellRegion[mesh.faceOwner()[faceI]];
            label neiRegion = cellRegion[mesh.faceNeighbour()[faceI]];
    
            if (ownRegion != neiRegion)
            {
                edge interface
                (
                    min(ownRegion, neiRegion),
                    max(ownRegion, neiRegion)
                );
    
                EdgeMap<label>::iterator iter = interfaceSizes.find(interface);
    
                if (iter != interfaceSizes.end())
                {
                    iter()++;
                }
                else
                {
                    interfaceSizes.insert(interface, 1);
                }
            }
        }
    
    
        // Boundary faces
        // ~~~~~~~~~~~~~~
    
        // Neighbour cellRegion.
        labelList coupledRegion(mesh.nFaces()-mesh.nInternalFaces());
    
        forAll(coupledRegion, i)
        {
            label cellI = mesh.faceOwner()[i+mesh.nInternalFaces()];
            coupledRegion[i] = cellRegion[cellI];
        }
        syncTools::swapBoundaryFaceList(mesh, coupledRegion, false);
    
        forAll(coupledRegion, i)
        {
            label faceI = i+mesh.nInternalFaces();
            label ownRegion = cellRegion[mesh.faceOwner()[faceI]];
            label neiRegion = coupledRegion[i];
    
            if (ownRegion != neiRegion)
            {
                edge interface
                (
                    min(ownRegion, neiRegion),
                    max(ownRegion, neiRegion)
                );
    
                EdgeMap<label>::iterator iter = interfaceSizes.find(interface);
    
                if (iter != interfaceSizes.end())
                {
                    iter()++;
                }
                else
                {
                    interfaceSizes.insert(interface, 1);
                }
            }
        }
    
    
    
        if (sumParallel && Pstream::parRun())
        {
            if (Pstream::master())
            {
                // Receive and add to my sizes
                for
                (
                    int slave=Pstream::firstSlave();
                    slave<=Pstream::lastSlave();
                    slave++
                )
                {
                    IPstream fromSlave(Pstream::blocking, slave);
    
                    EdgeMap<label> slaveSizes(fromSlave);
    
                    forAllConstIter(EdgeMap<label>, slaveSizes, slaveIter)
                    {
                        EdgeMap<label>::iterator masterIter =
                            interfaceSizes.find(slaveIter.key());
    
                        if (masterIter != interfaceSizes.end())
                        {
                            masterIter() += slaveIter();
                        }
                        else
                        {
                            interfaceSizes.insert(slaveIter.key(), slaveIter());
                        }
                    }
                }
    
                // Distribute
                for
                (
                    int slave=Pstream::firstSlave();
                    slave<=Pstream::lastSlave();
                    slave++
                )
                {
                    // Receive the edges using shared points from the slave.
                    OPstream toSlave(Pstream::blocking, slave);
                    toSlave << interfaceSizes;
                }
            }
            else
            {
                // Send to master
                {
    
    mattijs's avatar
    mattijs committed
                    OPstream toMaster(Pstream::blocking, Pstream::masterNo());
    
                    toMaster << interfaceSizes;
                }
                // Receive from master
                {
    
    mattijs's avatar
    mattijs committed
                    IPstream fromMaster(Pstream::blocking, Pstream::masterNo());
    
                    fromMaster >> interfaceSizes;
                }
            }
        }
    
    
    mattijs's avatar
    mattijs committed
        // Make sure all processors have interfaces in same order
        interfaces = interfaceSizes.toc();
        if (sumParallel)
        {
            Pstream::scatter(interfaces);
        }
    
    }
    
    
    // Create mesh for region.
    autoPtr<mapPolyMesh> createRegionMesh
    (
    
        const labelList& cellRegion,
    
        const EdgeMap<label>& interfaceToPatch,
        const fvMesh& mesh,
        const label regionI,
        const word& regionName,
        autoPtr<fvMesh>& newMesh
    )
    {
        // Create dummy system/fv*
        {
            IOobject io
            (
                "fvSchemes",
                mesh.time().system(),
                regionName,
    
    mattijs's avatar
    mattijs committed
                mesh,
    
                IOobject::NO_READ,
                IOobject::NO_WRITE,
                false
            );
    
            Info<< "Testing:" << io.objectPath() << endl;
    
            if (!io.headerOk())
    
            // if (!exists(io.objectPath()))
    
            {
                Info<< "Writing dummy " << regionName/io.name() << endl;
                dictionary dummyDict;
                dictionary divDict;
                dummyDict.add("divSchemes", divDict);
                dictionary gradDict;
                dummyDict.add("gradSchemes", gradDict);
                dictionary laplDict;
                dummyDict.add("laplacianSchemes", laplDict);
    
                IOdictionary(io, dummyDict).regIOobject::write();
            }
        }
        {
            IOobject io
            (
                "fvSolution",
                mesh.time().system(),
                regionName,
    
    mattijs's avatar
    mattijs committed
                mesh,
    
                IOobject::NO_READ,
                IOobject::NO_WRITE,
                false
            );
    
            if (!io.headerOk())
            //if (!exists(io.objectPath()))
            {
                Info<< "Writing dummy " << regionName/io.name() << endl;
                dictionary dummyDict;
                IOdictionary(io, dummyDict).regIOobject::write();
            }
        }
    
    
    
        // Neighbour cellRegion.
        labelList coupledRegion(mesh.nFaces()-mesh.nInternalFaces());
    
        forAll(coupledRegion, i)
        {
            label cellI = mesh.faceOwner()[i+mesh.nInternalFaces()];
            coupledRegion[i] = cellRegion[cellI];
        }
        syncTools::swapBoundaryFaceList(mesh, coupledRegion, false);
    
    
    
        // Topology change container. Start off from existing mesh.
        polyTopoChange meshMod(mesh);
    
        // Cell remover engine
        removeCells cellRemover(mesh);
    
        // Select all but region cells
        labelList cellsToRemove(getNonRegionCells(cellRegion, regionI));
    
        // Find out which faces will get exposed. Note that this
        // gets faces in mesh face order. So both regions will get same
        // face in same order (important!)
        labelList exposedFaces = cellRemover.getExposedFaces(cellsToRemove);
    
        labelList exposedPatchIDs(exposedFaces.size());
        forAll(exposedFaces, i)
        {
            label faceI = exposedFaces[i];
    
    
            label ownRegion = cellRegion[mesh.faceOwner()[faceI]];
            label neiRegion = -1;
    
            if (mesh.isInternalFace(faceI))
    
                neiRegion = cellRegion[mesh.faceNeighbour()[faceI]];
            }
            else
            {
                neiRegion = coupledRegion[faceI-mesh.nInternalFaces()];
    
            }
    
            label otherRegion = -1;
    
            if (ownRegion == regionI && neiRegion != regionI)
            {
                otherRegion = neiRegion;
            }
            else if (ownRegion != regionI && neiRegion == regionI)
            {
                otherRegion = ownRegion;
            }
            else
            {
                FatalErrorIn("createRegionMesh(..)")
                    << "Exposed face:" << faceI
                    << " fc:" << mesh.faceCentres()[faceI]
                    << " has owner region " << ownRegion
                    << " and neighbour region " << neiRegion
                    << " when handling region:" << regionI
                    << exit(FatalError);
            }
    
            if (otherRegion != -1)
            {
    
    mattijs's avatar
    mattijs committed
                edge interface(regionI, otherRegion);
    
    
                // Find the patch.
                if (regionI < otherRegion)
                {
                    exposedPatchIDs[i] = interfaceToPatch[interface];
                }
                else
                {
                    exposedPatchIDs[i] = interfaceToPatch[interface]+1;
                }
    
    Andrew Heather's avatar
    Andrew Heather committed
            }
    
        }
    
        // Remove faces
        cellRemover.setRefinement
        (
            cellsToRemove,
            exposedFaces,
            exposedPatchIDs,
            meshMod
        );
    
        autoPtr<mapPolyMesh> map = meshMod.makeMesh
        (
            newMesh,
            IOobject
            (
                regionName,
                mesh.time().timeName(),
                mesh.time(),
                IOobject::NO_READ,
                IOobject::AUTO_WRITE
            ),
            mesh
        );
    
        return map;
    }
    
    
    void createAndWriteRegion
    (
        const fvMesh& mesh,
    
        const labelList& cellRegion,
    
        const wordList& regionNames,
        const EdgeMap<label>& interfaceToPatch,
    
    mattijs's avatar
    mattijs committed
        const label regionI,
        const word& newMeshInstance
    
    )
    {
        Info<< "Creating mesh for region " << regionI
            << ' ' << regionNames[regionI] << endl;
    
        autoPtr<fvMesh> newMesh;
        autoPtr<mapPolyMesh> map = createRegionMesh
        (
            cellRegion,
            interfaceToPatch,
            mesh,
            regionI,
            regionNames[regionI],
            newMesh
        );
    
        Info<< "Mapping fields" << endl;
    
        // Map existing fields
        newMesh().updateMesh(map());
    
        // Add subsetted fields
        subsetVolFields<volScalarField>
        (
            mesh,
            newMesh(),
            map().cellMap(),
            map().faceMap()
        );
        subsetVolFields<volVectorField>
        (
            mesh,
            newMesh(),
            map().cellMap(),
            map().faceMap()
        );
        subsetVolFields<volSphericalTensorField>
        (
            mesh,
            newMesh(),
            map().cellMap(),
            map().faceMap()
        );
        subsetVolFields<volSymmTensorField>
        (
            mesh,
            newMesh(),
            map().cellMap(),
            map().faceMap()
        );
        subsetVolFields<volTensorField>
        (
            mesh,
            newMesh(),
            map().cellMap(),
            map().faceMap()
        );
    
        subsetSurfaceFields<surfaceScalarField>
        (
            mesh,
            newMesh(),
            map().faceMap()
        );
        subsetSurfaceFields<surfaceVectorField>
        (
            mesh,
            newMesh(),
            map().faceMap()
        );
        subsetSurfaceFields<surfaceSphericalTensorField>
        (
            mesh,
            newMesh(),
            map().faceMap()
        );
        subsetSurfaceFields<surfaceSymmTensorField>
        (
            mesh,
            newMesh(),
            map().faceMap()
        );
        subsetSurfaceFields<surfaceTensorField>
        (
            mesh,
            newMesh(),
            map().faceMap()
        );
    
    
        const polyBoundaryMesh& newPatches = newMesh().boundaryMesh();
    
    mattijs's avatar
    mattijs committed
        newPatches.checkParallelSync(true);
    
    
        // Delete empty patches
        // ~~~~~~~~~~~~~~~~~~~~
    
        // Create reordering list to move patches-to-be-deleted to end
        labelList oldToNew(newPatches.size(), -1);
        label newI = 0;
    
        Info<< "Deleting empty patches" << endl;
    
        // Assumes all non-proc boundaries are on all processors!
        forAll(newPatches, patchI)
        {
            const polyPatch& pp = newPatches[patchI];
    
    
    mattijs's avatar
    mattijs committed
            if (!isA<processorPolyPatch>(pp))
    
    mattijs's avatar
    mattijs committed
                if (returnReduce(pp.size(), sumOp<label>()) > 0)
                {
                    oldToNew[patchI] = newI++;
                }
    
            }
        }
    
        // Same for processor patches (but need no reduction)
        forAll(newPatches, patchI)
        {
            const polyPatch& pp = newPatches[patchI];
    
    
            if (isA<processorPolyPatch>(pp) && pp.size())
    
            {
                oldToNew[patchI] = newI++;
            }
        }
    
        const label nNewPatches = newI;
    
        // Move all deleteable patches to the end
        forAll(oldToNew, patchI)
        {
            if (oldToNew[patchI] == -1)
            {
                oldToNew[patchI] = newI++;
            }
        }
    
        reorderPatches(newMesh(), oldToNew, nNewPatches);
    
    
        Info<< "Writing new mesh" << endl;
    
    
    mattijs's avatar
    mattijs committed
        newMesh().setInstance(newMeshInstance);
    
        newMesh().write();
    
        // Write addressing files like decomposePar
        Info<< "Writing addressing to base mesh" << endl;
    
        labelIOList pointProcAddressing
        (
            IOobject
            (
                "pointRegionAddressing",
                newMesh().facesInstance(),
                newMesh().meshSubDir,
                newMesh(),
                IOobject::NO_READ,
                IOobject::NO_WRITE,
                false
            ),
            map().pointMap()
        );
        Info<< "Writing map " << pointProcAddressing.name()
            << " from region" << regionI
            << " points back to base mesh." << endl;