/*---------------------------------------------------------------------------*\ ========= | \\ / 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) 2019-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 "domainDecomposition.H" #include "dictionary.H" #include "labelIOList.H" #include "processorPolyPatch.H" #include "processorCyclicPolyPatch.H" #include "fvMesh.H" #include "OSspecific.H" #include "Map.H" #include "DynamicList.H" #include "fvFieldDecomposer.H" #include "IOobjectList.H" #include "PtrDynList.H" #include "cellSet.H" #include "faceSet.H" #include "pointSet.H" #include "decompositionModel.H" #include "hexRef8Data.H" // For handling pointMeshes with additional patches #include "pointMesh.H" #include "meshPointPatch.H" #include "processorPointPatch.H" #include "DynamicField.H" // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // void Foam::domainDecomposition::mark ( const labelList& zoneElems, const label zoneI, labelList& elementToZone ) { for (const label pointi : zoneElems) { if (elementToZone[pointi] == -1) { // First occurrence elementToZone[pointi] = zoneI; } else if (elementToZone[pointi] >= 0) { // Multiple zones elementToZone[pointi] = -2; } } } // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // Foam::domainDecomposition::domainDecomposition ( const IOobject& io, const fileName& decompDictFile ) : fvMesh(io), facesInstancePointsPtr_ ( pointsInstance() != facesInstance() ? new pointIOField ( IOobject ( "points", facesInstance(), polyMesh::meshSubDir, *this, IOobject::MUST_READ, IOobject::NO_WRITE, IOobject::NO_REGISTER ) ) : nullptr ), decompDictFile_(decompDictFile), nProcs_ ( decompositionMethod::nDomains ( decompositionModel::New ( *this, decompDictFile ) ) ), distributed_(false), cellToProc_(nCells()), procPointAddressing_(nProcs_), procFaceAddressing_(nProcs_), procCellAddressing_(nProcs_), procPatchSize_(nProcs_), procPatchStartIndex_(nProcs_), procNeighbourProcessors_(nProcs_), procProcessorPatchSize_(nProcs_), procProcessorPatchStartIndex_(nProcs_), procProcessorPatchSubPatchIDs_(nProcs_), procProcessorPatchSubPatchStarts_(nProcs_) { updateParameters(this->model()); } // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // const Foam::decompositionModel& Foam::domainDecomposition::model() const { return decompositionModel::New(*this, decompDictFile_); } void Foam::domainDecomposition::updateParameters ( const dictionary& params ) { params.readIfPresent("distributed", distributed_); } bool Foam::domainDecomposition::writeDecomposition(const bool decomposeSets) { Info<< "\nConstructing processor meshes" << endl; // Mark point/faces/cells that are in zones. // -1 : not in zone // -2 : in multiple zones // >= 0 : in single given zone // This will give direct lookup of elements that are in a single zone // and we'll only have to revert back to searching through all zones // for the duplicate elements // Point zones labelList pointToZone(points().size(), -1); forAll(pointZones(), zonei) { mark(pointZones()[zonei], zonei, pointToZone); } // Face zones labelList faceToZone(faces().size(), -1); forAll(faceZones(), zonei) { mark(faceZones()[zonei], zonei, faceToZone); } // Cell zones labelList cellToZone(nCells(), -1); forAll(cellZones(), zonei) { mark(cellZones()[zonei], zonei, cellToZone); } PtrDynList cellSets; PtrDynList faceSets; PtrDynList pointSets; if (decomposeSets) { // Read sets IOobjectList objects(*this, facesInstance(), "polyMesh/sets"); for (const IOobject& io : objects.csorted()) { cellSets.emplace_back(io); } for (const IOobject& io : objects.csorted()) { faceSets.emplace_back(io); } for (const IOobject& io : objects.csorted()) { pointSets.emplace_back(io); } } // Load refinement data (if any) hexRef8Data baseMeshData ( IOobject ( "dummy", facesInstance(), polyMesh::meshSubDir, *this, IOobjectOption::READ_IF_PRESENT, IOobjectOption::NO_WRITE, IOobjectOption::NO_REGISTER ) ); label maxProcCells = 0; label maxProcFaces = 0; label totProcFaces = 0; label maxProcPatches = 0; label totProcPatches = 0; // Write out the meshes for (label proci = 0; proci < nProcs_; proci++) { // Create processor points const labelList& curPointLabels = procPointAddressing_[proci]; const pointField& meshPoints = points(); labelList pointLookup(nPoints(), -1); pointField procPoints(curPointLabels.size()); forAll(curPointLabels, pointi) { procPoints[pointi] = meshPoints[curPointLabels[pointi]]; pointLookup[curPointLabels[pointi]] = pointi; } // Create processor faces const labelList& curFaceLabels = procFaceAddressing_[proci]; const faceList& meshFaces = faces(); labelList faceLookup(nFaces(), -1); faceList procFaces(curFaceLabels.size()); forAll(curFaceLabels, facei) { // Mark the original face as used // Remember to decrement the index by one (turning index) label curF = mag(curFaceLabels[facei]) - 1; faceLookup[curF] = facei; // get the original face labelList origFaceLabels; if (curFaceLabels[facei] >= 0) { // face not turned origFaceLabels = meshFaces[curF]; } else { origFaceLabels = meshFaces[curF].reverseFace(); } // translate face labels into local point list face& procFaceLabels = procFaces[facei]; procFaceLabels.setSize(origFaceLabels.size()); forAll(origFaceLabels, pointi) { procFaceLabels[pointi] = pointLookup[origFaceLabels[pointi]]; } } // Create processor cells const labelList& curCellLabels = procCellAddressing_[proci]; const cellList& meshCells = cells(); cellList procCells(curCellLabels.size()); forAll(curCellLabels, celli) { const labelList& origCellLabels = meshCells[curCellLabels[celli]]; cell& curCell = procCells[celli]; curCell.setSize(origCellLabels.size()); forAll(origCellLabels, cellFacei) { curCell[cellFacei] = faceLookup[origCellLabels[cellFacei]]; } } // Create processor mesh without a boundary // create a database Time processorDb ( Time::controlDictName, time().rootPath(), time().caseName()/("processor" + Foam::name(proci)), false, // No function objects false // No extra controlDict libs ); processorDb.setTime(time()); // create the mesh. Two situations: // - points and faces come from the same time ('instance'). The mesh // will get constructed in the same instance. // - points come from a different time (moving mesh cases). // It will read the points belonging to the faces instance and // construct the procMesh with it which then gets handled as above. // (so with 'old' geometry). // Only at writing time will it additionally write the current // points. autoPtr procMeshPtr; if (facesInstancePointsPtr_) { // Construct mesh from facesInstance. pointField facesInstancePoints ( facesInstancePointsPtr_(), curPointLabels ); procMeshPtr = autoPtr::New ( IOobject ( this->polyMesh::name(), // region of undecomposed mesh facesInstance(), processorDb, IOobject::NO_READ, IOobject::AUTO_WRITE ), std::move(facesInstancePoints), std::move(procFaces), std::move(procCells) ); } else { procMeshPtr = autoPtr::New ( IOobject ( this->polyMesh::name(), // region of undecomposed mesh facesInstance(), processorDb, IOobject::NO_READ, IOobject::AUTO_WRITE ), std::move(procPoints), std::move(procFaces), std::move(procCells) ); } polyMesh& procMesh = procMeshPtr(); // Create processor boundary patches const labelList& curPatchSizes = procPatchSize_[proci]; const labelList& curPatchStarts = procPatchStartIndex_[proci]; const labelList& curNeighbourProcessors = procNeighbourProcessors_[proci]; const labelList& curProcessorPatchSizes = procProcessorPatchSize_[proci]; const labelList& curProcessorPatchStarts = procProcessorPatchStartIndex_[proci]; const labelListList& curSubPatchIDs = procProcessorPatchSubPatchIDs_[proci]; const labelListList& curSubStarts = procProcessorPatchSubPatchStarts_[proci]; const polyPatchList& meshPatches = boundaryMesh(); // Count the number of inter-proc patches label nInterProcPatches = 0; forAll(curSubPatchIDs, procPatchi) { nInterProcPatches += curSubPatchIDs[procPatchi].size(); } polyPatchList procPatches ( curPatchSizes.size() + nInterProcPatches ); label nPatches = 0; forAll(curPatchSizes, patchi) { // Get the face labels consistent with the field mapping // (reuse the patch field mappers) const polyPatch& meshPatch = meshPatches[patchi]; fvFieldDecomposer::patchFieldDecomposer patchMapper ( SubList