/*---------------------------------------------------------------------------*\ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | Website: https://openfoam.org \\ / A nd | Copyright (C) 2011-2023 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 . Application decomposePar Description Automatically decomposes a mesh and fields of a case for parallel execution of OpenFOAM. Usage \b decomposePar [OPTION] Options: - \par -cellProc Write cell processor indices as a volScalarField::Internal for post-processing. - \par -region \ \n Decompose named region. Does not check for existence of processor*. - \par -allRegions \n Decompose all regions in regionSolvers. Does not check for existence of processor*. - \par -copyZero \n Copy \a 0 directory to processor* rather than decompose the fields. - \par -copyUniform \n Copy any \a uniform directories too. - \par -constant Decompose mesh and fields in the constant directory. - \par -time xxx:yyy \n Override controlDict settings and decompose selected times. - \par -fields \n Use existing geometry decomposition and convert fields only. - \par -noSets \n Skip decomposing cellSets, faceSets, pointSets. - \par -force \n Remove any existing \a processor subdirectories before decomposing the geometry. \*---------------------------------------------------------------------------*/ #include "argList.H" #include "timeSelector.H" #include "IOobjectList.H" #include "processorRunTimes.H" #include "multiDomainDecomposition.H" #include "decompositionMethod.H" #include "fvFieldDecomposer.H" #include "pointFieldDecomposer.H" #include "lagrangianFieldDecomposer.H" using namespace Foam; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // namespace Foam { bool haveUniform ( const processorRunTimes& runTimes, const word& regionDir = word::null ) { return fileHandler().isDir ( runTimes.completeTime().timePath()/regionDir/"uniform" ); } void decomposeUniform ( const bool copyUniform, const processorRunTimes& runTimes, const word& regionDir = word::null ) { const fileName uniformDir(regionDir/"uniform"); forAll(runTimes.procTimes(), proci) { const fileName procTimePath = fileHandler().filePath(runTimes.procTimes()[proci].timePath()); if (!fileHandler().isDir(procTimePath)) { fileHandler().mkDir(procTimePath); } if (copyUniform) { if (!fileHandler().exists(procTimePath/uniformDir)) { fileHandler().cp ( runTimes.completeTime().timePath()/uniformDir, procTimePath/uniformDir ); } } else { // Link with relative paths string parentPath = string("..")/".."; if (regionDir != word::null) { parentPath = parentPath/".."; } fileName currentDir(cwd()); chDir(procTimePath); if (!fileHandler().exists(uniformDir)) { fileHandler().ln ( parentPath/runTimes.completeTime().name()/uniformDir, uniformDir ); } chDir(currentDir); } } } void writeDecomposition(const domainDecomposition& meshes) { // Write as volScalarField::Internal for postprocessing. volScalarField::Internal cellProc ( IOobject ( "cellProc", meshes.completeMesh().time().name(), meshes.completeMesh(), IOobject::NO_READ, IOobject::AUTO_WRITE ), meshes.completeMesh(), dimless, scalarField(scalarList(meshes.cellProc())) ); cellProc.write(); Info<< "Wrote decomposition as volScalarField::Internal to " << cellProc.name() << " for use in postprocessing" << endl; } } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // namespace Foam { class delayedNewLine { mutable bool first_; public: delayedNewLine() : first_(true) {} friend Ostream& operator<<(Ostream& os, const delayedNewLine& dnl) { if (!dnl.first_) os << nl; dnl.first_ = false; return os; } }; } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // int main(int argc, char *argv[]) { argList::addNote ( "decompose a mesh and fields of a case for parallel execution" ); argList::noParallel(); #include "addRegionOption.H" #include "addAllRegionsOption.H" argList::addBoolOption ( "cellProc", "write cell processor indices as a volScalarField::Internal for " "post-processing" ); argList::addBoolOption ( "copyZero", "Copy \a 0 directory to processor* rather than decompose the fields" ); argList::addBoolOption ( "copyUniform", "copy any uniform/ directories too" ); argList::addBoolOption ( "fields", "use existing geometry decomposition and convert fields only" ); argList::addBoolOption ( "noFields", "opposite of -fields; only decompose geometry" ); argList::addBoolOption ( "noSets", "skip decomposing cellSets, faceSets, pointSets" ); argList::addBoolOption ( "force", "remove existing processor*/ subdirs before decomposing the geometry" ); // Include explicit constant option, execute from zero by default timeSelector::addOptions(true, false); #include "setRootCase.H" const bool region = args.optionFound("region"); const bool writeCellProc = args.optionFound("cellProc"); const bool copyZero = args.optionFound("copyZero"); const bool copyUniform = args.optionFound("copyUniform"); const bool decomposeFieldsOnly = args.optionFound("fields"); const bool decomposeGeomOnly = args.optionFound("noFields"); const bool decomposeSets = !args.optionFound("noSets"); const bool forceOverwrite = args.optionFound("force"); if (decomposeGeomOnly) { Info<< "Skipping decomposing fields" << nl << endl; if (decomposeFieldsOnly || copyZero) { FatalErrorInFunction << "Cannot combine geometry-only decomposition (-noFields)" << " with field decomposition (-fields or -copyZero)" << exit(FatalError); } } // Set time from database Info<< "Create time" << nl << endl; processorRunTimes runTimes(Foam::Time::controlDictName, args); const Time& runTime = runTimes.completeTime(); // Allow override of time const instantList times = runTimes.selectComplete(args); #include "setRegionNames.H" // Remove existing processor directories if requested if (forceOverwrite) { if (region) { FatalErrorInFunction << "Cannot force the decomposition of a single region" << exit(FatalError); } const label nProcs0 = fileHandler().nProcs(runTimes.completeTime().path()); Info<< "Removing " << nProcs0 << " existing processor directories" << nl << endl; // Remove existing processor directories const fileNameList dirs ( fileHandler().readDir ( runTimes.completeTime().path(), fileType::directory ) ); forAllReverse(dirs, diri) { const fileName& d = dirs[diri]; // Starts with 'processors' if (d.find("processors") == 0) { if (fileHandler().exists(d)) { fileHandler().rmDir(d); } } // Starts with 'processor' if (d.find("processor") == 0) { // Check that integer after processor fileName num(d.substr(9)); label proci = -1; if (Foam::read(num.c_str(), proci)) { if (fileHandler().exists(d)) { fileHandler().rmDir(d); } } } } // Flush file handler to clear any detected processor directories fileHandler().flush(); } // Check the specified number of processes is consistent with any existing // processor directories { const label nProcs0 = fileHandler().nProcs(runTimes.completeTime().path()); if (nProcs0 && nProcs0 != runTimes.nProcs()) { FatalErrorInFunction << "Case is already decomposed with " << nProcs0 << " domains, use the -force option or manually" << nl << "remove processor directories before decomposing. e.g.," << nl << " rm -rf " << runTimes.completeTime().path().c_str() << "/processor*" << nl << exit(FatalError); } } // Get the decomposition dictionary const dictionary decomposeParDict = decompositionMethod::decomposeParDict(runTimes.completeTime()); // Check existing decomposition forAll(regionNames, regioni) { const word& regionName = regionNames[regioni]; const word regionDir = regionName == polyMesh::defaultRegion ? word::null : regionName; // Determine the existing processor count directly const label nProcs = fileHandler().nProcs(runTimes.completeTime().path(), regionDir); // Get requested numberOfSubdomains const label nDomains = decomposeParDict.lookup