From bb0fa65122fe8654b0d717a4c37038d883bc69d0 Mon Sep 17 00:00:00 2001 From: Mark Olesen <Mark.Olesen@esi-group.com> Date: Tue, 28 Nov 2017 12:02:18 +0100 Subject: [PATCH] ENH: respond to externalCoupled lock file contents - waitForSlave now return a Time::stopAtControls enumeration: unknown: when lockfile has no specially recognized content. endTime: when lockfile contains "status=done" writeNow: when lockfile contains "action=writeNow" nextWrite: when lockfile contains "action=nextWrite" noWriteNow: when lockfile contains "action=noWriteNow" These values can be used by the caller to terminate the master (OpenFOAM) as desired in response to information placed there by the slave process. --- .../general/coupling/externalFileCoupler.C | 102 +++++++++++++----- .../general/coupling/externalFileCoupler.H | 81 +++++++++----- .../field/externalCoupled/externalCoupled.C | 72 +++++++------ .../field/externalCoupled/externalCoupled.H | 5 +- ...edPointDisplacementPointPatchVectorField.C | 19 +++- 5 files changed, 186 insertions(+), 93 deletions(-) diff --git a/src/finiteVolume/cfdTools/general/coupling/externalFileCoupler.C b/src/finiteVolume/cfdTools/general/coupling/externalFileCoupler.C index 7d891b058f1..8bc524fdcc4 100644 --- a/src/finiteVolume/cfdTools/general/coupling/externalFileCoupler.C +++ b/src/finiteVolume/cfdTools/general/coupling/externalFileCoupler.C @@ -40,17 +40,51 @@ namespace Foam Foam::word Foam::externalFileCoupler::lockName = "OpenFOAM"; +namespace Foam +{ + // file-scope -// check file (must exist) for "status=done" content -static bool checkIsDone(const std::string& lck) +// Read file contents and return a stop control as follows: +// - contains "done" (should actually be status=done, but we are generous) : +// The master (OpenFOAM) has signalled that it is done. Report as <endTime> +// +// - action=writeNow, action=nextWrite action=noWriteNow : +// The slave has signalled that it is done and wants the master to exit with +// the specified type of action. Report as corresponding <action>. +// +// Anything else (empty file, no action=, etc) is reported as <unknown>. +// +static enum Time::stopAtControls getStopAction(const std::string& filename) { - std::string content; - std::ifstream is(lck); - is >> content; + // Slurp entire input file (must exist) as a single string + std::string fileContent; + + std::ifstream is(filename); + std::getline(is, fileContent, '\0'); + + if (fileContent.find("done") != std::string::npos) + { + return Time::stopAtControls::saEndTime; + } + + const auto equals = fileContent.find('='); + + if (equals != std::string::npos) + { + const word actionName(word::validate(fileContent.substr(equals+1))); + + return + Time::stopAtControlNames + ( + actionName, + Time::stopAtControls::saUnknown + ); + } - return (content.find("done") != std::string::npos); + return Time::stopAtControls::saUnknown; } +} // End namespace Foam // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // @@ -141,7 +175,8 @@ bool Foam::externalFileCoupler::readDict(const dictionary& dict) } -void Foam::externalFileCoupler::useMaster(const bool wait) const +enum Foam::Time::stopAtControls +Foam::externalFileCoupler::useMaster(const bool wait) const { const bool wasInit = initialized(); runState_ = MASTER; @@ -163,18 +198,20 @@ void Foam::externalFileCoupler::useMaster(const bool wait) const std::ofstream os(lck); os << "status=openfoam\n"; - os.flush(); } } if (wait) { - waitForMaster(); + return waitForMaster(); } + + return Time::stopAtControls::saUnknown; } -void Foam::externalFileCoupler::useSlave(const bool wait) const +enum Foam::Time::stopAtControls +Foam::externalFileCoupler::useSlave(const bool wait) const { const bool wasInit = initialized(); runState_ = SLAVE; @@ -194,19 +231,23 @@ void Foam::externalFileCoupler::useSlave(const bool wait) const if (wait) { - waitForSlave(); + return waitForSlave(); } + + return Time::stopAtControls::saUnknown; } -bool Foam::externalFileCoupler::waitForMaster() const +enum Foam::Time::stopAtControls +Foam::externalFileCoupler::waitForMaster() const { if (!initialized()) { useMaster(); // was not initialized } - bool isDone = false; + auto action = Time::stopAtControls::saUnknown; + if (Pstream::master()) { const fileName lck(lockFile()); @@ -221,9 +262,11 @@ bool Foam::externalFileCoupler::waitForMaster() const if (prevTime < modTime) { prevTime = modTime; - isDone = checkIsDone(lck); - if (isDone) + + if (Time::stopAtControls::saEndTime == getStopAction(lck)) { + // Found 'done' - slave should not wait for master + action = Time::stopAtControls::saEndTime; break; } } @@ -231,21 +274,24 @@ bool Foam::externalFileCoupler::waitForMaster() const } } - // MPI barrier - Pstream::scatter(isDone); + label intAction(action); + + Pstream::scatter(intAction); // Also acts as MPI barrier - return !isDone; + return Time::stopAtControls(intAction); } -bool Foam::externalFileCoupler::waitForSlave() const +enum Foam::Time::stopAtControls +Foam::externalFileCoupler::waitForSlave() const { if (!initialized()) { useSlave(); // was not initialized } - bool isDone = false; + auto action = Time::stopAtControls::saUnknown; + if (Pstream::master()) { const fileName lck(lockFile()); @@ -267,16 +313,16 @@ bool Foam::externalFileCoupler::waitForSlave() const Log << type() << ": wait time = " << totalTime << endl; } - // But check for status=done content in the file - isDone = checkIsDone(lck); + action = getStopAction(lck); Log << type() << ": found lock file " << lck << endl; } - // MPI barrier - Pstream::scatter(isDone); + label intAction(action); + + Pstream::scatter(intAction); // Also acts as MPI barrier - return !isDone; + return Time::stopAtControls(intAction); } @@ -308,12 +354,10 @@ void Foam::externalFileCoupler::shutdown() const { if (Pstream::master() && runState_ == MASTER && Foam::isDir(commsDir_)) { - const fileName lck(lockFile()); - Log << type() << ": lock file status=done" << endl; - std::ofstream os(lck); + + std::ofstream os(lockFile()); os << "status=done\n"; - os.flush(); } runState_ = DONE; // Avoid re-triggering in destructor diff --git a/src/finiteVolume/cfdTools/general/coupling/externalFileCoupler.H b/src/finiteVolume/cfdTools/general/coupling/externalFileCoupler.H index 7979a784e1a..6a4dfc303b1 100644 --- a/src/finiteVolume/cfdTools/general/coupling/externalFileCoupler.H +++ b/src/finiteVolume/cfdTools/general/coupling/externalFileCoupler.H @@ -60,21 +60,26 @@ Description A typical coupling loop would look like this (on the master-side): \verbatim initialize - master takes control - write data for slave - use slave, wait for slave - cleanup old data from master - read data from slave - use master + writeDataMaster() - write data for slave + useSlave() + waitForSlave() + removeDataMaster() - cleanup old data from master [optional?] + readDataMaster() - read data from slave + useMaster() \endverbatim On the slave-side: \verbatim - wait for master - read data from master - write data for master - use master + waitForMaster() + readDataSlave() - read data from master + writeDataSlave() - write data for master + useMaster() \endverbatim + Note that since the waitForSlave() method not only waits for the lock file + to be reinstated but also does a simple check of its contents, it can + also serve to communicate some control from the slave to the master. + SourceFiles externalFileCoupler.C externalFileCouplerI.H @@ -86,6 +91,7 @@ SourceFiles #include "fileName.H" #include "dictionary.H" +#include "Time.H" // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // @@ -177,7 +183,7 @@ public: // Member Functions - // Initialization + // Initialization //- True if state has been initialized inline bool initialized() const; @@ -186,7 +192,7 @@ public: inline bool slaveFirst() const; - // File locations + // File locations //- Return the file path to the base communications directory inline const fileName& commDirectory() const; @@ -198,36 +204,57 @@ public: inline fileName lockFile() const; - // Settings + // Settings //- Read communication settings from dictionary bool readDict(const dictionary& dict); - // Handshaking + // Handshaking //- Create lock file to indicate that OpenFOAM is in charge - // Optionally wait for master to complete as well. - void useMaster(const bool wait=false) const; + // \param wait - wait for master to complete. + // + // \return Time::stopAtControls::saUnknown if not waiting, otherwise + // as per the waitForMaster() description. + enum Time::stopAtControls useMaster(const bool wait=false) const; //- Remove lock file to indicate that the external program is in charge - // Optionally wait for slave to complete as well. - void useSlave(const bool wait=false) const; + // \param wait - wait for slave to complete. + // + // \return Time::stopAtControls::saUnknown if not waiting, otherwise + // as per the waitForSlave() description. + enum Time::stopAtControls useSlave(const bool wait=false) const; //- Wait for master to complete. // This is when the lock file disappears, or exists but has - // "status=done" content. - // \return False if lock file contains "status=done" - bool waitForMaster() const; + // \c status=done content. + // + // \return Time::stopAtControls::saUnknown or if lock file + // contained \c status=done it returns + // Time::stopAtControls::saEndTime + enum Time::stopAtControls waitForMaster() const; //- Wait for slave to complete. // This is when the lock file appears. - // \return False if lock file contains "status=done" - bool waitForSlave() const; - - - // File creation, removal + // + // When the lock file appears, it is checked for the following + // content which corresponds to particular return values: + // - \c status=done + // \return Foam::Time::saEndTime + // - \c action=writeNow + // \return Foam::Time::saWriteNow + // - \c action=nextWrite + // \return Foam::Time::saNextWrite + // - \c action=noNextWrite + // \return Foam::Time::saNoNextWrite + // - Anything else (empty file, no action= or status=, etc) + // \return Foam::Time::saUnknown + enum Time::stopAtControls waitForSlave() const; + + + // File creation, removal //- Read data files on master (OpenFOAM). // These data files are normally created by the slave. @@ -251,13 +278,13 @@ public: //- Remove data files written by slave (external program) virtual void removeDataSlave() const; - //- Generate status=done in lock (only when run-state = master) + + //- Generate \c status=done in lock (only when run-state = master) void shutdown() const; //- Remove files written by OpenFOAM void removeDirectory() const; - }; diff --git a/src/functionObjects/field/externalCoupled/externalCoupled.C b/src/functionObjects/field/externalCoupled/externalCoupled.C index 5f710e33186..5d18e58c335 100644 --- a/src/functionObjects/field/externalCoupled/externalCoupled.C +++ b/src/functionObjects/field/externalCoupled/externalCoupled.C @@ -460,6 +460,42 @@ void Foam::functionObjects::externalCoupled::initCoupling() } +void Foam::functionObjects::externalCoupled::performCoupling() +{ + // Ensure coupling has been initialised + initCoupling(); + + // Write data for external source + writeDataMaster(); + + // Signal external source to execute (by removing lock file) + // - Wait for slave to provide data + useSlave(); + + // Wait for response - and catch any abort information sent from slave + const auto action = waitForSlave(); + + // Remove old data files from OpenFOAM + removeDataMaster(); + + // Read data passed back from external source + readDataMaster(); + + // Signal external source to wait (by creating the lock file) + useMaster(); + + // Process any abort information sent from slave + if + ( + action != time_.stopAt() + && action != Time::stopAtControls::saUnknown + ) + { + time_.stopAt(action); + } +} + + // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // Foam::functionObjects::externalCoupled::externalCoupled @@ -483,46 +519,16 @@ Foam::functionObjects::externalCoupled::externalCoupled } -// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * // - -Foam::functionObjects::externalCoupled::~externalCoupled() -{} - - // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // bool Foam::functionObjects::externalCoupled::execute() { if (!initialisedCoupling_ || time_.timeIndex() % calcFrequency_ == 0) { - // Initialise the coupling - initCoupling(); - - // Write data for external source - writeDataMaster(); - - // Signal external source to execute (by removing lock file) - // - Wait for slave to provide data - useSlave(); - - // Wait for response - waitForSlave(); - - // Remove old data files from OpenFOAM - removeDataMaster(); - - // Read data passed back from external source - readDataMaster(); - - // Signal external source to wait (by creating the lock file) - useMaster(); - - return true; - } - else - { - return false; + performCoupling(); } + + return false; } diff --git a/src/functionObjects/field/externalCoupled/externalCoupled.H b/src/functionObjects/field/externalCoupled/externalCoupled.H index e0059d9bf1a..516d7c4d520 100644 --- a/src/functionObjects/field/externalCoupled/externalCoupled.H +++ b/src/functionObjects/field/externalCoupled/externalCoupled.H @@ -282,6 +282,9 @@ private: static void checkOrder(const wordList& regionNames); + //- Perform the coupling with necessary initialization etc. + void performCoupling(); + //- Disallow default bitwise copy constructor externalCoupled(const externalCoupled&) = delete; @@ -313,7 +316,7 @@ public: //- Destructor - virtual ~externalCoupled(); + virtual ~externalCoupled() = default; // Member Functions diff --git a/src/lumpedPointMotion/lumpedPointDisplacementPointPatchVectorField.C b/src/lumpedPointMotion/lumpedPointDisplacementPointPatchVectorField.C index 91ce90ca712..3368d6f59f5 100644 --- a/src/lumpedPointMotion/lumpedPointDisplacementPointPatchVectorField.C +++ b/src/lumpedPointMotion/lumpedPointDisplacementPointPatchVectorField.C @@ -187,6 +187,8 @@ void Foam::lumpedPointDisplacementPointPatchVectorField::updateCoeffs() return; } + enum Time::stopAtControls action = Time::stopAtControls::saUnknown; + const bool masterPatch = (movement().ownerId() == this->patch().index()); if (masterPatch) { @@ -250,13 +252,14 @@ void Foam::lumpedPointDisplacementPointPatchVectorField::updateCoeffs() { movement().writeData(forces, moments); - // signal external source to execute + // Signal external source to execute movement().coupler().useSlave(); } } - // Wait for slave to provide data - includes MPI barrier - movement().coupler().waitForSlave(); + // Wait for slave to provide data (includes MPI barrier) + // and catch any abort information sent from slave + action = movement().coupler().waitForSlave(); // Read data passed back from external source - includes MPI barrier const_cast<lumpedPointMovement&>(movement()).readState(); @@ -271,6 +274,16 @@ void Foam::lumpedPointDisplacementPointPatchVectorField::updateCoeffs() this->operator==(tdisp); fixedValuePointPatchField<vector>::updateCoeffs(); + + // Process any abort information sent from slave + if + ( + action != this->db().time().stopAt() + && action != Time::stopAtControls::saUnknown + ) + { + this->db().time().stopAt(action); + } } -- GitLab