/*---------------------------------------------------------------------------*\ ========= | \\ / 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 - \par -time xxx:yyy \n Override controlDict settings and decompose selected times. Does not re-decompose the mesh i.e. does not handle moving mesh or changing mesh cases. - \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 "processorRunTimes.H" #include "domainDecomposition.H" #include "decompositionMethod.H" #include "argList.H" #include "timeSelector.H" #include "labelIOField.H" #include "labelFieldIOField.H" #include "scalarIOField.H" #include "scalarFieldIOField.H" #include "vectorIOField.H" #include "vectorFieldIOField.H" #include "sphericalTensorIOField.H" #include "sphericalTensorFieldIOField.H" #include "symmTensorIOField.H" #include "symmTensorFieldIOField.H" #include "tensorIOField.H" #include "tensorFieldIOField.H" #include "readFields.H" #include "dimFieldDecomposer.H" #include "fvFieldDecomposer.H" #include "pointFieldDecomposer.H" #include "lagrangianFieldDecomposer.H" using namespace Foam; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // namespace Foam { void decomposeUniform ( const bool copyUniform, const bool distributeUniform, const Time& runTime, const Time& procRunTime, const word& regionDir = word::null ) { const fileName uniformDir(regionDir/"uniform"); if (fileHandler().isDir(runTime.timePath()/uniformDir)) { Info<< "Detected additional non-decomposed files in " << runTime.timePath()/uniformDir << endl; const fileName timePath = fileHandler().filePath(procRunTime.timePath()); if (copyUniform || distributeUniform) { if (!fileHandler().exists(timePath/uniformDir)) { fileHandler().cp ( runTime.timePath()/uniformDir, timePath/uniformDir ); } } else { // link with relative paths string parentPath = string("..")/".."; if (regionDir != word::null) { parentPath = parentPath/".."; } fileName currentDir(cwd()); chDir(timePath); if (!fileHandler().exists(uniformDir)) { fileHandler().ln ( parentPath/runTime.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." << nl << endl; } } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 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 options, have zero from time range timeSelector::addOptions(true, false); #include "setRootCase.H" bool region = args.optionFound("region"); bool writeCellProc = args.optionFound("cellProc"); bool copyZero = args.optionFound("copyZero"); bool copyUniform = args.optionFound("copyUniform"); bool decomposeFieldsOnly = args.optionFound("fields"); bool decomposeGeomOnly = args.optionFound("noFields"); bool decomposeSets = !args.optionFound("noSets"); 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\n" << endl; processorRunTimes runTimes(Foam::Time::controlDictName, args); // Allow override of time const instantList times = runTimes.selectComplete(args); const Time& runTime = runTimes.completeTime(); #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" << 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()); // Decompose all regions forAll(regionNames, regioni) { const word& regionName = regionNames[regioni]; const word& regionDir = regionName == polyMesh::defaultRegion ? word::null : regionName; Info<< "\n\nDecomposing mesh " << regionName << nl << endl; // Determine the existing processor count directly const label nProcs = fileHandler().nProcs(runTimes.completeTime().path(), regionDir); // Get requested numberOfSubdomains const label nDomains = decomposeParDict.lookup