Commit 00ec58a1 authored by Mark OLESEN's avatar Mark OLESEN
Browse files

ENH: extend flexibility of abort function object (#1119)

- Now also responds to the contents of the trigger file,
  processing action= contents similar to used with external coupling.

  Previously it only handled an action that was defined in the
  dictionary. With this update, the user can chose a diferent action
  simply by echoing the appropriate action string into the trigger
  file.
parent 781246c2
......@@ -40,10 +40,11 @@ namespace Foam
Foam::word Foam::externalFileCoupler::lockName = "OpenFOAM";
// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
namespace Foam
{
// file-scope
// 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>
......@@ -86,6 +87,7 @@ static enum Time::stopAtControls getStopAction(const std::string& filename)
} // End namespace Foam
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::externalFileCoupler::externalFileCoupler()
......
......@@ -30,6 +30,7 @@ License
#include "OSspecific.H"
#include "PstreamReduceOps.H"
#include "addToRunTimeSelectionTable.H"
#include <fstream>
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
......@@ -51,15 +52,54 @@ namespace functionObjects
// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
// file-scope
// Long description for the action name
namespace Foam
{
// Read file contents and return a stop control as follows:
//
// - action=writeNow, action=nextWrite action=noWriteNow :
// The signalled 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)
{
// Slurp entire input file (must exist) as a single string
std::string fileContent;
std::ifstream is(filename);
std::getline(is, fileContent, '\0');
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 Time::stopAtControls::saUnknown;
}
// Long description for the action name
static std::string longDescription(const Time::stopAtControls ctrl)
{
switch (ctrl)
{
case Foam::Time::saNoWriteNow :
case Time::saEndTime :
{
return "continue simulation to the endTime";
break;
}
case Time::saNoWriteNow :
{
return "stop without writing data";
break;
......@@ -80,12 +120,13 @@ static std::string longDescription(const Time::stopAtControls ctrl)
default:
{
// Invalid choices already filtered out by Enum
return "abort";
return "unknown action";
break;
}
}
}
}
} // End namespace Foam
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
......@@ -99,18 +140,16 @@ Foam::functionObjects::abort::abort
:
functionObject(name),
time_(runTime),
abortFile_(time_.globalPath()/name),
action_(Time::stopAtControls::saNextWrite),
file_(),
defaultAction_(Time::stopAtControls::saUnknown),
triggered_(false)
{
abortFile_.clean();
read(dict);
// Cleanup old files from previous runs
if (Pstream::master())
{
Foam::rm(abortFile_);
Foam::rm(file_);
}
}
......@@ -121,36 +160,38 @@ bool Foam::functionObjects::abort::read(const dictionary& dict)
{
functionObject::read(dict);
if (dict.readIfPresent("file", abortFile_))
file_.clear();
if (dict.readIfPresent("file", file_))
{
abortFile_.expand();
file_.expand();
if (!abortFile_.isAbsolute())
if (!file_.isAbsolute() && file_.size())
{
abortFile_ = time_.globalPath()/abortFile_;
abortFile_.clean();
}
file_ = time_.globalPath()/file_;
file_.clean();
}
}
const auto oldAction = action_;
// Ensure we always have a reasonable default file
if (file_.empty())
{
file_ = time_.globalPath()/name();
file_.clean();
}
action_ = Time::stopAtControlNames.lookupOrDefault
triggered_ = false;
defaultAction_ = Time::stopAtControlNames.lookupOrDefault
(
"action",
dict,
Time::stopAtControls::saNextWrite
);
// User can change action and re-trigger the abort.
// eg, they had nextWrite, but actually wanted writeNow.
if (oldAction != action_)
{
triggered_ = false;
}
Info<< type() << " activated ("
<< longDescription(action_).c_str() <<")" << nl
<< " File: " << abortFile_ << endl;
<< longDescription(defaultAction_).c_str() <<")" << nl
<< " File: " << file_ << endl;
return true;
}
......@@ -161,22 +202,35 @@ bool Foam::functionObjects::abort::execute()
// If it has been triggered (eg, nextWrite) don't need to check it again
if (!triggered_)
{
bool hasAbort = (Pstream::master() && isFile(abortFile_));
Pstream::scatter(hasAbort);
auto action = Time::stopAtControls::saUnknown;
if (hasAbort)
if (Pstream::master() && Foam::isFile(file_))
{
triggered_ = time_.stopAt(action_);
action = getStopAction(file_);
if (triggered_)
if (Time::stopAtControls::saUnknown == action)
{
Info<< "USER REQUESTED ABORT (timeIndex="
<< time_.timeIndex()
<< "): " << longDescription(action_).c_str()
<< endl;
// An unknown action means an empty file or bad content.
// Treat as a request for the default action.
action = defaultAction_;
}
}
Pstream::scatter(triggered_);
// Send to slaves. Also acts as an MPI barrier
label intAction(action);
Pstream::scatter(intAction);
action = Time::stopAtControls(intAction);
// Call stopAt() on all processes
triggered_ = time_.stopAt(action);
if (triggered_)
{
Info<< "USER REQUESTED ABORT (timeIndex="
<< time_.timeIndex() << "): "
<< longDescription(action).c_str() << endl;
}
}
......@@ -192,10 +246,10 @@ bool Foam::functionObjects::abort::write()
bool Foam::functionObjects::abort::end()
{
// Cleanup ABORT file
// Cleanup trigger file
if (Pstream::master())
{
Foam::rm(abortFile_);
Foam::rm(file_);
}
return true;
......
......@@ -28,24 +28,56 @@ Group
grpUtilitiesFunctionObjects
Description
Watches for presence of the named file in the case directory
and aborts the calculation if it is present.
Watches for presence of the named trigger file in the case directory
and signals a simulation stop (or other) event if found.
The presence of the abort file is only checked on the master process.
The presence of the trigger file is only checked on the master process.
Currently the following action types are supported:
- noWriteNow
- writeNow
- nextWrite
Example of function object specification:
\verbatim
abort
{
type abort;
libs ("libutilityFunctionObjects.so");
file "<case>/GOODBYE";
action writeNow
}
\endverbatim
\heading Function object usage
\table
Property | Description | Required | Default value
type | Type name: abort | yes |
file | The abort filename | no | \<case\>/name
action | Abort action | no | nextWrite
Property | Description | Required | Default
type | Type name: abort | yes |
file | The trigger filename | no | \<case\>/name
action | The default action to trigger | no | nextWrite
\endtable
When the trigger file is found, it is checked for the following
content which corresponds to actions.
- \c action=noWriteNow
: triggers Foam::Time::saNoWriteNow (stop without writing data)
- \c action=writeNow
: triggers Foam::Time::saWriteNow (stop and write data)
- \c action=nextWrite
: triggers Foam::Time::saNextWrite (stop after next normal data write)
- \c action=endTime
: triggers Foam::Time::saEndTime (continue simulation to the end)
- Anything else (empty file, no action=, ...)
: use the default action
.
Note
The trigger file is considered "sticky". This means that once detected
and processed, the trigger is duly noted and the file will not be
rechecked. It is not possible or desirable to 'untrigger' an action.
SourceFiles
abort.C
......@@ -72,18 +104,18 @@ class abort
:
public functionObject
{
// Private data
// Private Data
//- Reference to the Time
const Time& time_;
//- The fully-qualified name of the abort file
fileName abortFile_;
//- The fully-qualified name of the trigger file
fileName file_;
//- The type of action
Time::stopAtControls action_;
//- The default action (defined in dictionary)
Time::stopAtControls defaultAction_;
//- Only trigger action once
//- Only trigger the action once
bool triggered_;
......@@ -122,13 +154,13 @@ public:
//- Read the dictionary settings
virtual bool read(const dictionary& dict);
//- Check existence of abort file and take action
//- Check existence of the file and take action
virtual bool execute();
//- No-op
virtual bool write();
//- Remove abort file after the final time-loop.
//- Remove the trigger file after the final time-loop.
virtual bool end();
};
......
// OpenFOAM dictionary -*- C++ -*-
abort
{
type abort;
libs ("libutilityFunctionObjects.so");
file "<case>/ABORT"; // Instead of default name
// action writeNow; // If we want to see immediate results
// Or use default (nextWrite) and force with
// "action=writeNow" in the trigger file
}
// ************************************************************************* //
......@@ -47,5 +47,9 @@ graphFormat raw;
runTimeModifiable true;
functions
{
#include "abort"
}
// ************************************************************************* //
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment