diff --git a/src/postProcessing/functionObjects/jobControl/externalCoupled/externalCoupledFunctionObject.C b/src/postProcessing/functionObjects/jobControl/externalCoupled/externalCoupledFunctionObject.C index 66f11a9038501e3c64b91cedbf6a717e2b387ac0..fcfc0bd85503854b35b2e189fc7c3c10e976d41d 100644 --- a/src/postProcessing/functionObjects/jobControl/externalCoupled/externalCoupledFunctionObject.C +++ b/src/postProcessing/functionObjects/jobControl/externalCoupled/externalCoupledFunctionObject.C @@ -49,7 +49,54 @@ namespace Foam Foam::word Foam::externalCoupledFunctionObject::lockName = "OpenFOAM"; -Foam::string Foam::externalCoupledFunctionObject::patchKey = "# Patch: "; +Foam::string Foam::externalCoupledFunctionObject::patchKey = "// Patch:"; + +template<> +const char* Foam::NamedEnum +< + Foam::externalCoupledFunctionObject::stateEnd, + 2 +>::names[] = +{ + "remove", + "done" +}; + +const Foam::NamedEnum +< + Foam::externalCoupledFunctionObject::stateEnd, + 2 +> Foam::externalCoupledFunctionObject::stateEndNames_; + + +namespace Foam +{ +//! \cond fileScope +//- Write list content with size, bracket, content, bracket one-per-line. +// This makes for consistent for parsing, regardless of the list length. +template <class T> +static void writeList(Ostream& os, const string& header, const UList<T>& L) +{ + // Header string + os << header.c_str() << nl; + + // Write size and start delimiter + os << L.size() << nl + << token::BEGIN_LIST; + + // Write contents + forAll(L, i) + { + os << nl << L[i]; + } + + // Write end delimiter + os << nl << token::END_LIST << nl << endl; +} +//! \endcond + +} +// namespace Foam // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // @@ -88,7 +135,7 @@ Foam::fileName Foam::externalCoupledFunctionObject::lockFile() const } -void Foam::externalCoupledFunctionObject::createLockFile() const +void Foam::externalCoupledFunctionObject::useMaster() const { if (!Pstream::master()) { @@ -104,13 +151,13 @@ void Foam::externalCoupledFunctionObject::createLockFile() const if (log_) Info<< type() << ": creating lock file" << endl; OFstream os(fName); - os << "lock file"; + os << "status=openfoam\n"; os.flush(); } } -void Foam::externalCoupledFunctionObject::removeLockFile() const +void Foam::externalCoupledFunctionObject::useSlave() const { if (!Pstream::master()) { @@ -119,7 +166,35 @@ void Foam::externalCoupledFunctionObject::removeLockFile() const if (log_) Info<< type() << ": removing lock file" << endl; - rm(lockFile()); + Foam::rm(lockFile()); +} + + +void Foam::externalCoupledFunctionObject::cleanup() const +{ + if (!Pstream::master()) + { + return; + } + + const fileName lck(lockFile()); + switch (stateEnd_) + { + case REMOVE: + { + if (log_) Info<< type() << ": removing lock file" << endl; + Foam::rm(lck); + } + break; + case DONE: + { + if (log_) Info<< type() << ": lock file status=done" << endl; + OFstream os(lck); + os << "status=done\n"; + os.flush(); + } + break; + } } @@ -189,30 +264,29 @@ void Foam::externalCoupledFunctionObject::removeWriteFiles() const } -void Foam::externalCoupledFunctionObject::wait() const +void Foam::externalCoupledFunctionObject::waitForSlave() const { const fileName fName(lockFile()); - label found = 0; label totalTime = 0; + bool found = false; if (log_) Info<< type() << ": beginning wait for lock file " << fName << nl; - while (found == 0) + while (!found) { if (Pstream::master()) { if (totalTime > timeOut_) { FatalErrorInFunction - << "Wait time exceeded time out time of " << timeOut_ + << "Wait time exceeded timeout of " << timeOut_ << " s" << abort(FatalError); } IFstream is(fName); - if (is.good()) { - found++; + found = true; if (log_) { @@ -232,7 +306,7 @@ void Foam::externalCoupledFunctionObject::wait() const } // prevent other procs from racing ahead - reduce(found, sumOp<label>()); + reduce(found, orOp<bool>()); } } @@ -387,11 +461,6 @@ void Foam::externalCoupledFunctionObject::writeGeometry fileName dir(groupDir(commsDir, compositeName(regionNames), groupName)); - //if (log_) - { - Info<< typeName << ": writing geometry to " << dir << endl; - } - autoPtr<OFstream> osPointsPtr; autoPtr<OFstream> osFacesPtr; if (Pstream::master()) @@ -399,12 +468,20 @@ void Foam::externalCoupledFunctionObject::writeGeometry mkDir(dir); osPointsPtr.reset(new OFstream(dir/"patchPoints")); osFacesPtr.reset(new OFstream(dir/"patchFaces")); + + osPointsPtr() << "// Group: " << groupName << endl; + osFacesPtr() << "// Group: " << groupName << endl; + + Info<< typeName << ": writing geometry to " << dir << endl; } + // Individual region/patch entries - DynamicList<face> allMeshesFaces; - DynamicField<point> allMeshesPoints; + DynamicList<face> allFaces; + DynamicField<point> allPoints; + labelList pointToGlobal; + labelList uniquePointIDs; forAll(meshes, meshI) { const fvMesh& mesh = meshes[meshI]; @@ -417,109 +494,61 @@ void Foam::externalCoupledFunctionObject::writeGeometry ).sortedToc() ); - // Count faces - label nFaces = 0; - forAll(patchIDs, i) - { - nFaces += mesh.boundaryMesh()[patchIDs[i]].size(); - } - - // Collect faces - DynamicList<label> allFaceIDs(nFaces); forAll(patchIDs, i) { const polyPatch& p = mesh.boundaryMesh()[patchIDs[i]]; - forAll(p, pI) - { - allFaceIDs.append(p.start()+pI); - } - } - - // Construct overall patch - indirectPrimitivePatch allPatch - ( - IndirectList<face>(mesh.faces(), allFaceIDs), - mesh.points() - ); - - labelList pointToGlobal; - labelList uniquePointIDs; - mesh.globalData().mergePoints - ( - allPatch.meshPoints(), - allPatch.meshPointMap(), - pointToGlobal, - uniquePointIDs - ); - - label procI = Pstream::myProcNo(); - - List<pointField> collectedPoints(Pstream::nProcs()); - collectedPoints[procI] = pointField(mesh.points(), uniquePointIDs); - Pstream::gatherList(collectedPoints); + mesh.globalData().mergePoints + ( + p.meshPoints(), + p.meshPointMap(), + pointToGlobal, + uniquePointIDs + ); - List<faceList> collectedFaces(Pstream::nProcs()); - faceList& patchFaces = collectedFaces[procI]; - patchFaces = allPatch.localFaces(); - forAll(patchFaces, faceI) - { - inplaceRenumber(pointToGlobal, patchFaces[faceI]); - } - Pstream::gatherList(collectedFaces); + label procI = Pstream::myProcNo(); - if (Pstream::master()) - { - // Append and renumber - label nPoints = allMeshesPoints.size(); + List<pointField> collectedPoints(Pstream::nProcs()); + collectedPoints[procI] = pointField(mesh.points(), uniquePointIDs); + Pstream::gatherList(collectedPoints); - forAll(collectedPoints, procI) + List<faceList> collectedFaces(Pstream::nProcs()); + faceList& patchFaces = collectedFaces[procI]; + patchFaces = p.localFaces(); + forAll(patchFaces, faceI) { - allMeshesPoints.append(collectedPoints[procI]); - + inplaceRenumber(pointToGlobal, patchFaces[faceI]); } - face newFace; - forAll(collectedFaces, procI) + Pstream::gatherList(collectedFaces); + + if (Pstream::master()) { - const faceList& procFaces = collectedFaces[procI]; + allPoints.clear(); + allFaces.clear(); - forAll(procFaces, faceI) + for (label procI=0; procI < Pstream::nProcs(); ++procI) { - const face& f = procFaces[faceI]; - - newFace.setSize(f.size()); - forAll(f, fp) - { - newFace[fp] = f[fp]+nPoints; - } - allMeshesFaces.append(newFace); + allPoints.append(collectedPoints[procI]); + allFaces.append(collectedFaces[procI]); } - nPoints += collectedPoints[procI].size(); + Info<< typeName << ": mesh " << mesh.name() + << ", patch " << p.name() + << ": writing " << allPoints.size() << " points to " + << osPointsPtr().name() << nl + << typeName << ": mesh " << mesh.name() + << ", patch " << p.name() + << ": writing " << allFaces.size() << " faces to " + << osFacesPtr().name() << endl; + + // The entry name (region / patch) + const string entryHeader = + patchKey + ' ' + mesh.name() + ' ' + p.name(); + + writeList(osPointsPtr(), entryHeader, allPoints); + writeList(osFacesPtr(), entryHeader, allFaces); } } - - //if (log_) - { - Info<< typeName << ": for mesh " << mesh.name() - << " writing " << allMeshesPoints.size() << " points to " - << osPointsPtr().name() << endl; - Info<< typeName << ": for mesh " << mesh.name() - << " writing " << allMeshesFaces.size() << " faces to " - << osFacesPtr().name() << endl; - } - } - - // Write points - if (osPointsPtr.valid()) - { - osPointsPtr() << allMeshesPoints << endl; - } - - // Write faces - if (osFacesPtr.valid()) - { - osFacesPtr() << allMeshesFaces << endl; } } @@ -541,7 +570,7 @@ Foam::word Foam::externalCoupledFunctionObject::compositeName { // For compatibility with single region cases suppress single // region name - return word(""); + return word::null; } else { @@ -768,7 +797,7 @@ void Foam::externalCoupledFunctionObject::initialise() if (initByExternal_) { // Wait for initial data to be made available - wait(); + waitForSlave(); // Read data passed back from external source readData(); @@ -790,6 +819,7 @@ Foam::externalCoupledFunctionObject::externalCoupledFunctionObject functionObject(name), time_(runTime), enabled_(true), + stateEnd_(REMOVE), initialised_(false) { read(dict); @@ -801,7 +831,7 @@ Foam::externalCoupledFunctionObject::externalCoupledFunctionObject if (!initByExternal_) { - createLockFile(); + useMaster(); } } @@ -809,7 +839,12 @@ Foam::externalCoupledFunctionObject::externalCoupledFunctionObject // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * // Foam::externalCoupledFunctionObject::~externalCoupledFunctionObject() -{} +{ + if (enabled()) + { + cleanup(); + } +} // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // @@ -846,11 +881,12 @@ bool Foam::externalCoupledFunctionObject::execute(const bool forceWrite) // Write data for external source writeData(); - // remove lock file, signalling external source to execute - removeLockFile(); + // Signal external source to execute (by removing lock file) + // - Wait for slave to provide data + useSlave(); // Wait for response - wait(); + waitForSlave(); // Remove old data files from OpenFOAM removeWriteFiles(); @@ -858,8 +894,8 @@ bool Foam::externalCoupledFunctionObject::execute(const bool forceWrite) // Read data passed back from external source readData(); - // create lock file for external source - createLockFile(); + // Signal external source to wait (by creating the lock file) + useMaster(); return true; } @@ -877,7 +913,7 @@ bool Foam::externalCoupledFunctionObject::end() // Remove old data files removeReadFiles(); removeWriteFiles(); - removeLockFile(); + cleanup(); } return true; @@ -906,13 +942,19 @@ bool Foam::externalCoupledFunctionObject::read(const dictionary& dict) return true; } + calcFrequency_ = dict.lookupOrDefault("calcFrequency", 1); + dict.lookup("commsDir") >> commsDir_; commsDir_.expand(); + commsDir_.clean(); - waitInterval_ = dict.lookupOrDefault("waitInterval", 1); - timeOut_ = dict.lookupOrDefault("timeOut", 100*waitInterval_); - calcFrequency_ = dict.lookupOrDefault("calcFrequency", 1); + waitInterval_ = dict.lookupOrDefault("waitInterval", 1); + timeOut_ = dict.lookupOrDefault("timeOut", 100*waitInterval_); initByExternal_ = readBool(dict.lookup("initByExternal")); + // initByExternal_ = dict.lookupOrDefault<Switch>("initByExternal", false); + stateEnd_ = + stateEndNames_[dict.lookupOrDefault<word>("stateEnd", "remove")]; + log_ = dict.lookupOrDefault("log", false); @@ -920,7 +962,6 @@ bool Foam::externalCoupledFunctionObject::read(const dictionary& dict) wordList allRegionNames(time_.lookupClass<fvMesh>().sortedToc()); - const dictionary& allRegionsDict = dict.subDict("regions"); forAllConstIter(dictionary, allRegionsDict, iter) diff --git a/src/postProcessing/functionObjects/jobControl/externalCoupled/externalCoupledFunctionObject.H b/src/postProcessing/functionObjects/jobControl/externalCoupled/externalCoupledFunctionObject.H index 458296f99f7dfe828b607aca2b75a75478e87b4f..d24ac43a6008ac249b4a14d80a6fff08bce8f641 100644 --- a/src/postProcessing/functionObjects/jobControl/externalCoupled/externalCoupledFunctionObject.H +++ b/src/postProcessing/functionObjects/jobControl/externalCoupled/externalCoupledFunctionObject.H @@ -2,7 +2,7 @@ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | - \\ / A nd | Copyright (C) 2015 OpenCFD Ltd. + \\ / A nd | Copyright (C) 2015-2016 OpenCFD Ltd. \\/ M anipulation | ------------------------------------------------------------------------------- License @@ -82,6 +82,7 @@ Description log yes; commsDir "${FOAM_CASE}/comms"; initByExternal yes; + stateEnd remove; // (remove | done) regions { @@ -122,9 +123,11 @@ SourceFiles #include "DynamicList.H" #include "wordReList.H" #include "scalarField.H" +#include "NamedEnum.H" #include "Switch.H" #include "UPtrList.H" + // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // namespace Foam @@ -145,6 +148,23 @@ class externalCoupledFunctionObject : public functionObject { +public: + + // Public data types + + //- Lockfile state on termination + enum stateEnd + { + REMOVE, //!< Remove lock file on end + DONE //!< Lock file contains status=done on end + }; + +private: + + //- State end names + static const NamedEnum<stateEnd, 2> stateEndNames_; + + // Private data //- Reference to the time database @@ -168,6 +188,9 @@ class externalCoupledFunctionObject //- Flag to indicate values are initialised by external application bool initByExternal_; + //- Lockfile state on termination + stateEnd stateEnd_; + //- Log flag bool log_; @@ -209,11 +232,15 @@ class externalCoupledFunctionObject //- Return the file path to the lock file fileName lockFile() const; - //- Create lock file - void createLockFile() const; - //- Remove lock file - void removeLockFile() const; + //- Create lock file to indicate that OpenFOAM is in charge + void useMaster() const; + + //- Remove lock file to indicate that the external program is in charge + void useSlave() const; + + //- Remove lock file or status=done in lock. + void cleanup() const; //- Remove files written by OpenFOAM void removeWriteFiles() const; @@ -221,8 +248,9 @@ class externalCoupledFunctionObject //- Remove files written by external code void removeReadFiles() const; - //- Wait for response from external source - void wait() const; + //- Wait for indication that the external program has supplied input + // (ie, for the lock file to reappear). + void waitForSlave() const; //- Read data for a single region, single field @@ -291,10 +319,10 @@ public: //- Runtime type information TypeName("externalCoupled"); - //- Name of lock file + //- Name of lock file (normally 'OpenFOAM.lock') static word lockName; - //- Name of patch key, e.g. '# Patch:' when looking for start of patch data + //- Name of patch key, e.g. '// Patch:' when looking for start of patch data static string patchKey; @@ -363,7 +391,7 @@ public: // separated by '_' static word compositeName(const wordList&); - //- Write geometry for the group/patch + //- Write geometry for the group as region/patch static void writeGeometry ( const UPtrList<const fvMesh>& meshes, diff --git a/tutorials/heatTransfer/chtMultiRegionFoam/externalCoupledMultiRegionHeater/Allrun b/tutorials/heatTransfer/chtMultiRegionFoam/externalCoupledMultiRegionHeater/Allrun index b2f87f4120d40a02d84330232dcab13861722129..e6c35cda767da0c3fd7b35530d89ba2d74577a11 100755 --- a/tutorials/heatTransfer/chtMultiRegionFoam/externalCoupledMultiRegionHeater/Allrun +++ b/tutorials/heatTransfer/chtMultiRegionFoam/externalCoupledMultiRegionHeater/Allrun @@ -15,6 +15,12 @@ cd ${0%/*} || exit 1 # Run from this directory # Decompose runApplication decomposePar -allRegions +## Can verify parallel operation of createExternalCoupledPatchGeometry +# \rm -f log.createExternalCoupledPatchGeometry +# runParallel createExternalCoupledPatchGeometry \ +# -regions '(topAir heater)' coupleGroup \ +# -commsDir $PWD/comms + # Run OpenFOAM runParallel $(getApplication) & diff --git a/tutorials/heatTransfer/chtMultiRegionFoam/externalCoupledMultiRegionHeater/Allrun.pre b/tutorials/heatTransfer/chtMultiRegionFoam/externalCoupledMultiRegionHeater/Allrun.pre index d841e3cfe0bede051684a1ef20052e0a18d788a1..f6fd2f3950db44838f3601922e8570b950c667fa 100755 --- a/tutorials/heatTransfer/chtMultiRegionFoam/externalCoupledMultiRegionHeater/Allrun.pre +++ b/tutorials/heatTransfer/chtMultiRegionFoam/externalCoupledMultiRegionHeater/Allrun.pre @@ -28,6 +28,6 @@ runApplication createExternalCoupledPatchGeometry \ echo echo "creating files for paraview post-processing" echo -paraFoam -touchAll +paraFoam -touchAll 2>/dev/null # ----------------------------------------------------------------- end-of-file diff --git a/tutorials/heatTransfer/chtMultiRegionFoam/externalCoupledMultiRegionHeater/externalSolver b/tutorials/heatTransfer/chtMultiRegionFoam/externalCoupledMultiRegionHeater/externalSolver index 89c5f53cb9faa812c7ba8621394285c4f7491794..201bc166822609975480c46a439c6744dd16ba14 100755 --- a/tutorials/heatTransfer/chtMultiRegionFoam/externalCoupledMultiRegionHeater/externalSolver +++ b/tutorials/heatTransfer/chtMultiRegionFoam/externalCoupledMultiRegionHeater/externalSolver @@ -20,13 +20,17 @@ fieldName="T" lockFile="${commsDir}/OpenFOAM.lock" dataFile="${commsDir}/${regionGroupName}/${patchGroupName}/${fieldName}" -waitSec=1 -timeOut=10 -nSteps=200 # maximum number of time steps. Note: should be more than +waitSec=5 +timeOut=100 +nSteps=1000 # maximum number of time steps. Note: should be more than # number of iterations on the OpenFOAM side refGrad=0 valueFraction=1 + +# Remove any old junk +\rm -f $lockFile 2>/dev/null + log() { echo "External: $@" @@ -53,7 +57,7 @@ init() echo "$refValue2 $refGrad $valueFraction" >> "${dataFile}.in" done - # create lock file to pass control to OF + # Create lock file to pass control to OpenFOAM touch ${lockFile} } @@ -61,7 +65,6 @@ init() # create the comms directory mkdir -p ${commsDir}/${regionGroupName}/${patchGroupName} - # tutorial case employs the 'initByExternalOption', so we need to provide # the initial values init @@ -69,11 +72,24 @@ init totalWait=0 step=0 -while [ $step -lt $nSteps ]; do - if [ -f $lockFile ]; then - log "found lock file ${lockFile} - waiting" +while [ $step -lt $nSteps ] +do + if [ -f $lockFile ] + then + if grep -q "status=done" ${lockFile} + then + log "found lock file ${lockFile} with 'status=done' - finished" + break + elif [ -s $lockFile ] + then + log "found lock file ${lockFile} containing '$(cat $lockFile)' - waiting" + else + log "found lock file ${lockFile} - waiting" + fi + totalWait=$(expr $totalWait + $waitSec) - if [ $totalWait -gt $timeOut ]; then + if [ $totalWait -gt $timeOut ] + then log "timeout" break else @@ -91,7 +107,7 @@ while [ $step -lt $nSteps ]; do log "updating ${dataFile}.in from ${dataFile}.out" awk '{if( $1 != "#" ){print $1+1 " 0 1"}}' \ - ${dataFile}.out | tee ${dataFile}.in + ${dataFile}.out >| ${dataFile}.in log "creating lock file ${lockFile}" touch ${lockFile} @@ -100,5 +116,7 @@ done log "done" +# Remove the lock file too +\rm -f $lockFile 2>/dev/null #------------------------------------------------------------------------------ diff --git a/tutorials/heatTransfer/chtMultiRegionFoam/externalCoupledMultiRegionHeater/system/controlDict b/tutorials/heatTransfer/chtMultiRegionFoam/externalCoupledMultiRegionHeater/system/controlDict index 402a8fabdb0bfdb1eb8229719d0f1893963225b8..683320e59b4a04508a1d6a0b06e91e714e5c2598 100644 --- a/tutorials/heatTransfer/chtMultiRegionFoam/externalCoupledMultiRegionHeater/system/controlDict +++ b/tutorials/heatTransfer/chtMultiRegionFoam/externalCoupledMultiRegionHeater/system/controlDict @@ -26,11 +26,12 @@ startTime 0; stopAt endTime; -endTime 1; +endTime 0.5; deltaT 0.001; writeControl adjustableRunTime; + writeInterval 0.1; purgeWrite 0; @@ -56,40 +57,7 @@ adjustTimeStep yes; functions { - externalCoupled - { - // Where to load it from (if not already in solver) - functionObjectLibs ("libjobControl.so"); - - type externalCoupled; - - // Directory to use for communication - commsDir "${FOAM_CASE}/comms"; - - // Does external process start first - initByExternal true; - - // Additional output - log true; - - regions - { - // Region name (wildcards allowed) - "(topAir|heater)" - { - // In topAir adjust the minX patch (fixedValue) - - // Patch or patchGroup - coupleGroup - { - // Fields to output in commsDir - writeFields (T); - // Fields to read from commsDir - readFields (T); - } - } - } - } + #include "externalCoupled" } diff --git a/tutorials/heatTransfer/chtMultiRegionFoam/externalCoupledMultiRegionHeater/system/externalCoupled b/tutorials/heatTransfer/chtMultiRegionFoam/externalCoupledMultiRegionHeater/system/externalCoupled new file mode 100644 index 0000000000000000000000000000000000000000..03ec069af3719a2195b06044570122f4867058de --- /dev/null +++ b/tutorials/heatTransfer/chtMultiRegionFoam/externalCoupledMultiRegionHeater/system/externalCoupled @@ -0,0 +1,43 @@ +// -*- C++ -*- + +// control for external coupled simulation +externalCoupled +{ + // Where to load it from (if not already in solver) + functionObjectLibs ("libjobControl.so"); + + type externalCoupled; + + // Directory to use for communication + commsDir "${FOAM_CASE}/comms"; + + // Does external process start first + initByExternal true; + + // Cleanup behaviour on termination (remove|done) + stateEnd done; + + // Additional output + log true; + + regions + { + // Region name (wildcards allowed) + "(topAir|heater)" + { + // In topAir adjust the minX patch (fixedValue) + + // Patch or patchGroup + coupleGroup + { + // Fields to output in commsDir + writeFields (T); + // Fields to read from commsDir + readFields (T); + } + } + } +} + + +// ************************************************************************* //