Merge branch 'feature-externalFileCoupler' into 'develop'

Feature external file coupler

See merge request !126
This commit is contained in:
Andrew Heather
2017-07-18 11:12:05 +01:00
25 changed files with 689 additions and 755 deletions

View File

@ -1,3 +0,0 @@
Test-externalCoupler.C
EXE = $(FOAM_USER_APPBIN)/Test-externalCoupler

View File

@ -1,7 +0,0 @@
EXE_INC = \
-I$(LIB_SRC)/finiteVolume/lnInclude \
-I$(LIB_SRC)/lumpedPointMotion/lnInclude
EXE_LIBS = \
-lfiniteVolume \
-llumpedPointMotion

View File

@ -0,0 +1,3 @@
Test-externalFileCoupler.C
EXE = $(FOAM_USER_APPBIN)/Test-externalFileCoupler

View File

@ -0,0 +1,5 @@
EXE_INC = \
-I$(LIB_SRC)/finiteVolume/lnInclude
EXE_LIBS = \
-lfiniteVolume

View File

@ -22,14 +22,14 @@ License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>. along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
Application Application
Test-externalCoupler Test-externalFileCoupler
Description Description
Test of master/slave communication etc. Test of master/slave communication etc.
\*---------------------------------------------------------------------------*/ \*---------------------------------------------------------------------------*/
#include "argList.H" #include "argList.H"
#include "externalCoupler.H" #include "externalFileCoupler.H"
using namespace Foam; using namespace Foam;
@ -47,7 +47,7 @@ int main(int argc, char *argv[])
const label maxCount = args.optionLookupOrDefault<label>("max", 1000); const label maxCount = args.optionLookupOrDefault<label>("max", 1000);
externalCoupler coupler; externalFileCoupler coupler;
if (args.optionFound("slave")) if (args.optionFound("slave"))
{ {

View File

@ -29,7 +29,7 @@ Description
points/rotations and the corresponding movement of the building surfaces. points/rotations and the corresponding movement of the building surfaces.
Uses the tabulated responses from the specified file. Uses the tabulated responses from the specified file.
Optionally, it can also be used to a dummy responder for the Optionally, it can also be used to a dummy responder for the
externalCoupler logic, which makes it useful as a debugging facility externalFileCoupler logic, which makes it useful as a debugging facility
as well demonstrating how an external application could communicate as well demonstrating how an external application could communicate
with the lumpedPointMovement point-patch boundary condition. with the lumpedPointMovement point-patch boundary condition.
@ -131,7 +131,7 @@ int main(int argc, char *argv[])
{ {
Info<< "Running as slave responder" << endl; Info<< "Running as slave responder" << endl;
externalCoupler& coupler = movement().coupler(); externalFileCoupler& coupler = movement().coupler();
label count = 0; label count = 0;
for (label index = 0; index < responseTable.size(); index += span) for (label index = 0; index < responseTable.size(); index += span)

View File

@ -45,6 +45,8 @@ Foam::fileStat::fileStat
const bool followLink, const bool followLink,
const unsigned int maxTime const unsigned int maxTime
) )
:
isValid_(false)
{ {
// Work on volatile // Work on volatile
volatile bool locIsValid = false; volatile bool locIsValid = false;
@ -76,6 +78,12 @@ Foam::fileStat::fileStat(Istream& is)
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
Foam::label Foam::fileStat::size() const
{
return isValid_ ? label(status_.st_size) : 0;
}
bool Foam::fileStat::sameDevice(const fileStat& stat2) const bool Foam::fileStat::sameDevice(const fileStat& stat2) const
{ {
return return

View File

@ -55,8 +55,8 @@ namespace Foam
class fileStat; class fileStat;
Istream& operator>>(Istream&, fileStat&); Istream& operator>>(Istream& is, fileStat& fStat);
Ostream& operator<<(Ostream&, const fileStat&); Ostream& operator<<(Ostream& os, const fileStat& fStat);
/*---------------------------------------------------------------------------*\ /*---------------------------------------------------------------------------*\
@ -96,7 +96,7 @@ public:
); );
//- Construct from Istream //- Construct from Istream
fileStat(Istream&); fileStat(Istream& is);
// Member Functions // Member Functions
@ -115,6 +115,9 @@ public:
return isValid_; return isValid_;
} }
//- Size in bytes. Zero for invalid file-stat.
label size() const;
// Check // Check
@ -130,8 +133,8 @@ public:
// IOstream Operators // IOstream Operators
friend Istream& operator>>(Istream&, fileStat&); friend Istream& operator>>(Istream& is, fileStat& fStat);
friend Ostream& operator<<(Ostream&, const fileStat&); friend Ostream& operator<<(Ostream& os, const fileStat& fStat);
}; };

View File

@ -411,6 +411,9 @@ $(general)/bound/bound.C
$(general)/CorrectPhi/correctUphiBCs.C $(general)/CorrectPhi/correctUphiBCs.C
$(general)/pressureControl/pressureControl.C $(general)/pressureControl/pressureControl.C
coupling = $(general)/coupling
$(coupling)/externalFileCoupler.C
solutionControl = $(general)/solutionControl solutionControl = $(general)/solutionControl
$(solutionControl)/solutionControl/solutionControl.C $(solutionControl)/solutionControl/solutionControl.C
$(solutionControl)/simpleControl/simpleControl.C $(solutionControl)/simpleControl/simpleControl.C

View File

@ -23,22 +23,21 @@ License
\*---------------------------------------------------------------------------*/ \*---------------------------------------------------------------------------*/
#include "externalCoupler.H" #include "externalFileCoupler.H"
#include "Pstream.H" #include "Pstream.H"
#include "PstreamReduceOps.H" #include "PstreamReduceOps.H"
#include "OSspecific.H" #include "OSspecific.H"
#include "IFstream.H"
#include "OFstream.H"
#include "Switch.H" #include "Switch.H"
#include <fstream>
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam namespace Foam
{ {
defineTypeNameAndDebug(externalCoupler, 0); defineTypeNameAndDebug(externalFileCoupler, 0);
} }
Foam::word Foam::externalCoupler::lockName = "OpenFOAM"; Foam::word Foam::externalFileCoupler::lockName = "OpenFOAM";
// file-scope // file-scope
@ -46,30 +45,16 @@ Foam::word Foam::externalCoupler::lockName = "OpenFOAM";
static bool checkIsDone(const std::string& lck) static bool checkIsDone(const std::string& lck)
{ {
std::string content; std::string content;
std::ifstream is(lck.c_str()); std::ifstream is(lck);
is >> content; is >> content;
return (content.find("done") != std::string::npos); return (content.find("done") != std::string::npos);
} }
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
const Foam::fileName& Foam::externalCoupler::baseDir() const
{
return commsDir_;
}
Foam::fileName Foam::externalCoupler::lockFile() const
{
return resolveFile(lockName + ".lock");
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::externalCoupler::externalCoupler() Foam::externalFileCoupler::externalFileCoupler()
: :
runState_(NONE), runState_(NONE),
commsDir_("$FOAM_CASE/comms"), commsDir_("$FOAM_CASE/comms"),
@ -83,22 +68,41 @@ Foam::externalCoupler::externalCoupler()
} }
Foam::externalCoupler::externalCoupler(const dictionary& dict) Foam::externalFileCoupler::externalFileCoupler(const fileName& commsDir)
: :
externalCoupler() runState_(NONE),
commsDir_(commsDir),
waitInterval_(1u),
timeOut_(100u),
slaveFirst_(false),
log(false)
{
commsDir_.expand();
commsDir_.clean();
if (Pstream::master())
{
mkDir(commsDir_);
}
}
Foam::externalFileCoupler::externalFileCoupler(const dictionary& dict)
:
externalFileCoupler()
{ {
readDict(dict); readDict(dict);
if (Pstream::master()) if (Pstream::master())
{ {
mkDir(baseDir()); mkDir(commsDir_);
} }
} }
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
Foam::externalCoupler::~externalCoupler() Foam::externalFileCoupler::~externalFileCoupler()
{ {
shutdown(); shutdown();
} }
@ -106,15 +110,22 @@ Foam::externalCoupler::~externalCoupler()
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
bool Foam::externalCoupler::readDict(const dictionary& dict) bool Foam::externalFileCoupler::readDict(const dictionary& dict)
{ {
// Normally cannot change directory or initialization // Normally cannot change directory or initialization
// if things have already been initialized // if things have already been initialized
if (!initialized())
{
dict.lookup("commsDir") >> commsDir_; dict.lookup("commsDir") >> commsDir_;
commsDir_.expand(); commsDir_.expand();
commsDir_.clean(); commsDir_.clean();
slaveFirst_ = dict.lookupOrDefault<bool>("initByExternal", false); slaveFirst_ = dict.lookupOrDefault<bool>("initByExternal", false);
Info<< type() << ": initialize" << nl
<< " directory: " << commsDir_ << nl
<< " slave-first: " << Switch(slaveFirst_) << endl;
}
waitInterval_ = dict.lookupOrDefault("waitInterval", 1u); waitInterval_ = dict.lookupOrDefault("waitInterval", 1u);
if (!waitInterval_) if (!waitInterval_)
{ {
@ -130,19 +141,7 @@ bool Foam::externalCoupler::readDict(const dictionary& dict)
} }
bool Foam::externalCoupler::initialized() const void Foam::externalFileCoupler::useMaster(const bool wait) const
{
return runState_ != NONE;
}
bool Foam::externalCoupler::slaveFirst() const
{
return slaveFirst_;
}
void Foam::externalCoupler::useMaster(const bool wait) const
{ {
const bool wasInit = initialized(); const bool wasInit = initialized();
runState_ = MASTER; runState_ = MASTER;
@ -162,7 +161,7 @@ void Foam::externalCoupler::useMaster(const bool wait) const
{ {
Log << type() << ": creating lock file" << endl; Log << type() << ": creating lock file" << endl;
OFstream os(lck); std::ofstream os(lck);
os << "status=openfoam\n"; os << "status=openfoam\n";
os.flush(); os.flush();
} }
@ -175,7 +174,7 @@ void Foam::externalCoupler::useMaster(const bool wait) const
} }
void Foam::externalCoupler::useSlave(const bool wait) const void Foam::externalFileCoupler::useSlave(const bool wait) const
{ {
const bool wasInit = initialized(); const bool wasInit = initialized();
runState_ = SLAVE; runState_ = SLAVE;
@ -200,7 +199,7 @@ void Foam::externalCoupler::useSlave(const bool wait) const
} }
bool Foam::externalCoupler::waitForMaster() const bool Foam::externalFileCoupler::waitForMaster() const
{ {
if (!initialized()) if (!initialized())
{ {
@ -212,7 +211,7 @@ bool Foam::externalCoupler::waitForMaster() const
{ {
const fileName lck(lockFile()); const fileName lck(lockFile());
double prevTime = -1; double prevTime = 0;
double modTime = 0; double modTime = 0;
// Wait until file disappears (modTime == 0) // Wait until file disappears (modTime == 0)
@ -239,7 +238,7 @@ bool Foam::externalCoupler::waitForMaster() const
} }
bool Foam::externalCoupler::waitForSlave() const bool Foam::externalFileCoupler::waitForSlave() const
{ {
if (!initialized()) if (!initialized())
{ {
@ -281,23 +280,38 @@ bool Foam::externalCoupler::waitForSlave() const
} }
Foam::fileName Foam::externalCoupler::resolveFile void Foam::externalFileCoupler::readDataMaster()
( {}
const word& file
) const
{
return fileName(baseDir()/file);
}
void Foam::externalCoupler::shutdown() const void Foam::externalFileCoupler::readDataSlave()
{}
void Foam::externalFileCoupler::writeDataMaster() const
{}
void Foam::externalFileCoupler::writeDataSlave() const
{}
void Foam::externalFileCoupler::removeDataMaster() const
{}
void Foam::externalFileCoupler::removeDataSlave() const
{}
void Foam::externalFileCoupler::shutdown() const
{ {
if (Pstream::master() && runState_ == MASTER && Foam::isDir(commsDir_)) if (Pstream::master() && runState_ == MASTER && Foam::isDir(commsDir_))
{ {
const fileName lck(lockFile()); const fileName lck(lockFile());
Log << type() << ": lock file status=done" << endl; Log << type() << ": lock file status=done" << endl;
OFstream os(lck); std::ofstream os(lck);
os << "status=done\n"; os << "status=done\n";
os.flush(); os.flush();
} }

View File

@ -22,7 +22,7 @@ License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>. along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
Class Class
Foam::externalCoupler Foam::externalFileCoupler
Description Description
Encapsulates the logic for coordinating between OpenFOAM and an Encapsulates the logic for coordinating between OpenFOAM and an
@ -57,13 +57,32 @@ Description
} }
\endverbatim \endverbatim
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
\endverbatim
On the slave-side:
\verbatim
wait for master
read data from master
write data for master
use master
\endverbatim
SourceFiles SourceFiles
externalCoupler.C externalFileCoupler.C
externalFileCouplerI.H
\*---------------------------------------------------------------------------*/ \*---------------------------------------------------------------------------*/
#ifndef externalCoupler_H #ifndef externalFileCoupler_H
#define externalCoupler_H #define externalFileCoupler_H
#include "fileName.H" #include "fileName.H"
#include "dictionary.H" #include "dictionary.H"
@ -74,11 +93,15 @@ namespace Foam
{ {
/*---------------------------------------------------------------------------*\ /*---------------------------------------------------------------------------*\
Class externalCoupler Declaration Class externalFileCoupler Declaration
\*---------------------------------------------------------------------------*/ \*---------------------------------------------------------------------------*/
class externalCoupler class externalFileCoupler
{ {
public:
// Public data types
//- The run state (ie, who is currently in charge) //- The run state (ie, who is currently in charge)
enum runState enum runState
{ {
@ -89,6 +112,8 @@ class externalCoupler
}; };
private:
// Private data // Private data
//- The current run (and initialization) state //- The current run (and initialization) state
@ -103,7 +128,7 @@ class externalCoupler
//- Timeout [s] while waiting for the external application //- Timeout [s] while waiting for the external application
unsigned timeOut_; unsigned timeOut_;
//- Flag to indicate values are initialized by external application //- Flag to indicate values are initialized by external program
bool slaveFirst_; bool slaveFirst_;
//- Local logging/verbosity flag //- Local logging/verbosity flag
@ -112,22 +137,17 @@ class externalCoupler
// Private Member Functions // Private Member Functions
//- Return the file path to the base communications directory //- Disallow default bitwise copy construct
const fileName& baseDir() const; externalFileCoupler(const externalFileCoupler&) = delete;
//- Return the file path to the lock file
fileName lockFile() const;
//- Disallow default bitwise copy construc
externalCoupler(const externalCoupler&) = delete;
//- Disallow default bitwise assignmen //- Disallow default bitwise assignmen
void operator=(const externalCoupler&) = delete; void operator=(const externalFileCoupler&) = delete;
public: public:
//- Runtime type information //- Runtime type information
TypeName("externalCoupler"); TypeName("externalFileCoupler");
// Static data members // Static data members
@ -138,48 +158,98 @@ public:
// Constructors // Constructors
//- Construct null using standard defaults //- Construct using standard defaults.
externalCoupler(); // Does not create communications directory.
externalFileCoupler();
//- Construct with specified communications directory.
// Creates the communications directory upon construction.
externalFileCoupler(const fileName& commsDir);
//- Construct from dictionary //- Construct from dictionary
externalCoupler(const dictionary& dict); // Creates the communications directory upon construction.
externalFileCoupler(const dictionary& dict);
//- Destructor //- Destructor
virtual ~externalCoupler(); virtual ~externalFileCoupler();
// Member Functions // Member Functions
// Access // Initialization
//- True if state has been initialized //- True if state has been initialized
bool initialized() const; inline bool initialized() const;
//- External application provides initial values //- External application provides initial values
bool slaveFirst() const; inline bool slaveFirst() const;
// File locations
//- Return the file path to the base communications directory
inline const fileName& commDirectory() const;
//- Return the file path in the communications directory
inline fileName resolveFile(const word& file) const;
//- Return the file path to the lock file
inline fileName lockFile() const;
// Settings
//- Read communication settings from dictionary
bool readDict(const dictionary& dict);
// Handshaking
//- Create lock file to indicate that OpenFOAM is in charge //- Create lock file to indicate that OpenFOAM is in charge
// Optionally wait for master as well. // Optionally wait for master to complete as well.
void useMaster(const bool wait=false) const; void useMaster(const bool wait=false) const;
//- Wait for indication that OpenFOAM has supplied output. //- Remove lock file to indicate that the external program is in charge
// This is when the lock file disappears, or it exists but with // Optionally wait for slave to complete as well.
void 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. // "status=done" content.
// \return False if lock file contains "status=done" // \return False if lock file contains "status=done"
bool waitForMaster() const; bool waitForMaster() const;
//- Remove lock file to indicate that the external program is in charge //- Wait for slave to complete.
// Optionally wait for slave as well.
void useSlave(const bool wait=false) const;
//- Wait for indication that the external program has supplied input.
// This is when the lock file appears. // This is when the lock file appears.
// \return False if lock file contains "status=done" // \return False if lock file contains "status=done"
bool waitForSlave() const; bool waitForSlave() const;
//- Return the file path in the communications directory
fileName resolveFile(const word& file) const; // File creation, removal
//- Read data files on master (OpenFOAM).
// These data files are normally created by the slave.
virtual void readDataMaster();
//- Read data files on slave (external program).
// These data files are normally created by the master.
virtual void readDataSlave();
//- Write data files from master (OpenFOAM)
virtual void writeDataMaster() const;
//- Write data files from slave (external program)
virtual void writeDataSlave() const;
//- Remove data files written by master (OpenFOAM)
virtual void removeDataMaster() const;
//- Remove data files written by slave (external program)
virtual void removeDataSlave() const;
//- Generate status=done in lock (only when run-state = master) //- Generate status=done in lock (only when run-state = master)
void shutdown() const; void shutdown() const;
@ -188,11 +258,6 @@ public:
void removeDirectory() const; void removeDirectory() const;
// Edit
//- Read communication settings from dictionary
bool readDict(const dictionary& dict);
}; };
@ -202,6 +267,10 @@ public:
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#include "externalFileCouplerI.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif #endif
// ************************************************************************* // // ************************************************************************* //

View File

@ -0,0 +1,61 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2017 OpenCFD Ltd.
\\/ 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 <http://www.gnu.org/licenses/>.
\*---------------------------------------------------------------------------*/
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
inline bool Foam::externalFileCoupler::initialized() const
{
return runState_ != NONE;
}
inline bool Foam::externalFileCoupler::slaveFirst() const
{
return slaveFirst_;
}
inline const Foam::fileName& Foam::externalFileCoupler::commDirectory() const
{
return commsDir_;
}
inline Foam::fileName Foam::externalFileCoupler::resolveFile
(
const word& file
) const
{
return fileName(commDirectory()/file);
}
inline Foam::fileName Foam::externalFileCoupler::lockFile() const
{
return resolveFile(lockName + ".lock");
}
// ************************************************************************* //

View File

@ -50,20 +50,6 @@ namespace functionObjects
} }
} }
const Foam::Enum
<
Foam::functionObjects::externalCoupled::stateEnd
>
Foam::functionObjects::externalCoupled::stateEndNames_
{
{ stateEnd::REMOVE, "remove" },
{ stateEnd::DONE, "done" }
// 'IGNORE' is internal use only and thus without a name
};
Foam::word Foam::functionObjects::externalCoupled::lockName = "OpenFOAM";
Foam::string Foam::functionObjects::externalCoupled::patchKey = "// Patch:"; Foam::string Foam::functionObjects::externalCoupled::patchKey = "// Patch:";
@ -99,15 +85,6 @@ static void writeList(Ostream& os, const string& header, const UList<T>& L)
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
Foam::fileName Foam::functionObjects::externalCoupled::baseDir() const
{
fileName result(commsDir_);
result.clean();
return result;
}
Foam::fileName Foam::functionObjects::externalCoupled::groupDir Foam::fileName Foam::functionObjects::externalCoupled::groupDir
( (
const fileName& commsDir, const fileName& commsDir,
@ -127,171 +104,6 @@ Foam::fileName Foam::functionObjects::externalCoupled::groupDir
} }
Foam::fileName Foam::functionObjects::externalCoupled::lockFile() const
{
return fileName(baseDir()/(lockName + ".lock"));
}
void Foam::functionObjects::externalCoupled::useMaster() const
{
if (Pstream::master())
{
const fileName lck(lockFile());
// Only create lock file if it doesn't already exist
if (!Foam::isFile(lck))
{
Log << type() << ": creating lock file" << endl;
OFstream os(lck);
os << "status=openfoam\n";
os.flush();
}
}
}
void Foam::functionObjects::externalCoupled::useSlave() const
{
if (Pstream::master())
{
Log << type() << ": removing lock file" << endl;
Foam::rm(lockFile());
}
}
void Foam::functionObjects::externalCoupled::cleanup() const
{
if (Pstream::master())
{
const fileName lck(lockFile());
switch (stateEnd_)
{
case REMOVE:
{
Log << type() << ": removing lock file" << endl;
Foam::rm(lck);
break;
}
case DONE:
{
Log << type() << ": lock file status=done" << endl;
OFstream os(lck);
os << "status=done\n";
os.flush();
break;
}
case IGNORE:
break;
}
stateEnd_ = IGNORE; // Avoid re-triggering in destructor
}
}
void Foam::functionObjects::externalCoupled::removeDataSlave() const
{
if (!Pstream::master())
{
return;
}
Log << type() << ": removing data files written by slave" << nl;
forAll(regionGroupNames_, regioni)
{
const word& compName = regionGroupNames_[regioni];
const labelList& groups = regionToGroups_[compName];
forAll(groups, i)
{
label groupi = groups[i];
const wordRe& groupName = groupNames_[groupi];
forAll(groupReadFields_[groupi], fieldi)
{
const word& fieldName = groupReadFields_[groupi][fieldi];
rm
(
groupDir(commsDir_, compName, groupName)
/ fieldName + ".in"
);
}
}
}
}
void Foam::functionObjects::externalCoupled::removeDataMaster() const
{
if (!Pstream::master())
{
return;
}
Log << type() << ": removing data files written by master" << nl;
forAll(regionGroupNames_, regioni)
{
const word& compName = regionGroupNames_[regioni];
const labelList& groups = regionToGroups_[compName];
forAll(groups, i)
{
label groupi = groups[i];
const wordRe& groupName = groupNames_[groupi];
forAll(groupReadFields_[groupi], fieldi)
{
const word& fieldName = groupReadFields_[groupi][fieldi];
rm
(
groupDir(commsDir_, compName, groupName)
/ fieldName + ".out"
);
}
}
}
}
void Foam::functionObjects::externalCoupled::waitForSlave() const
{
bool waiting = true;
if (Pstream::master())
{
const fileName lck(lockFile());
unsigned totalTime = 0;
Log << type() << ": beginning wait for lock file " << lck << nl;
while ((waiting = !Foam::isFile(lck)) == true)
{
sleep(waitInterval_);
totalTime += waitInterval_;
if (timeOut_ && totalTime > timeOut_)
{
FatalErrorInFunction
<< "Wait time exceeded timeout of " << timeOut_
<< " s" << abort(FatalError);
}
Log << type() << ": wait time = " << totalTime << endl;
}
Log << type() << ": found lock file " << lck << endl;
}
// MPI barrier
Pstream::scatter(waiting);
}
void Foam::functionObjects::externalCoupled::readColumns void Foam::functionObjects::externalCoupled::readColumns
( (
const label nRows, const label nRows,
@ -465,21 +277,19 @@ void Foam::functionObjects::externalCoupled::writeGeometry
labelList pointToGlobal; labelList pointToGlobal;
labelList uniquePointIDs; labelList uniquePointIDs;
forAll(meshes, meshi) for (const fvMesh& mesh : meshes)
{ {
const fvMesh& mesh = meshes[meshi];
const labelList patchIDs const labelList patchIDs
( (
mesh.boundaryMesh().patchSet mesh.boundaryMesh().patchSet
( (
List<wordRe>(1, groupName) List<wordRe>{groupName}
).sortedToc() ).sortedToc()
); );
forAll(patchIDs, i) for (const label patchi : patchIDs)
{ {
const polyPatch& p = mesh.boundaryMesh()[patchIDs[i]]; const polyPatch& p = mesh.boundaryMesh()[patchi];
mesh.globalData().mergePoints mesh.globalData().mergePoints
( (
@ -592,207 +402,62 @@ void Foam::functionObjects::externalCoupled::checkOrder
} }
void Foam::functionObjects::externalCoupled::readData() void Foam::functionObjects::externalCoupled::initCoupling()
{ {
forAll(regionGroupNames_, regioni) if (initialisedCoupling_)
{
const word& compName = regionGroupNames_[regioni];
const wordList& regionNames = regionGroupRegions_[regioni];
// Get the meshes for the region-group
UPtrList<const fvMesh> meshes(regionNames.size());
forAll(regionNames, j)
{
const word& regionName = regionNames[j];
meshes.set(j, &time_.lookupObject<fvMesh>(regionName));
}
const labelList& groups = regionToGroups_[compName];
forAll(groups, i)
{
label groupi = groups[i];
const wordRe& groupName = groupNames_[groupi];
const wordList& fieldNames = groupReadFields_[groupi];
forAll(fieldNames, fieldi)
{
const word& fieldName = fieldNames[fieldi];
const bool ok =
(
readData<scalar>
(
meshes,
groupName,
fieldName
)
|| readData<vector>
(
meshes,
groupName,
fieldName
)
|| readData<sphericalTensor>
(
meshes,
groupName,
fieldName
)
|| readData<symmTensor>
(
meshes,
groupName,
fieldName
)
|| readData<tensor>
(
meshes,
groupName,
fieldName
)
);
if (!ok)
{
WarningInFunction
<< "Field " << fieldName << " in regions " << compName
<< " was not found." << endl;
}
}
}
}
}
void Foam::functionObjects::externalCoupled::writeData() const
{
forAll(regionGroupNames_, regioni)
{
const word& compName = regionGroupNames_[regioni];
const wordList& regionNames = regionGroupRegions_[regioni];
// Get the meshes for the region-group
UPtrList<const fvMesh> meshes(regionNames.size());
forAll(regionNames, j)
{
const word& regionName = regionNames[j];
meshes.set(j, &time_.lookupObject<fvMesh>(regionName));
}
const labelList& groups = regionToGroups_[compName];
forAll(groups, i)
{
label groupi = groups[i];
const wordRe& groupName = groupNames_[groupi];
const wordList& fieldNames = groupWriteFields_[groupi];
forAll(fieldNames, fieldi)
{
const word& fieldName = fieldNames[fieldi];
const bool ok =
(
writeData<scalar>
(
meshes,
groupName,
fieldName
)
|| writeData<vector>
(
meshes,
groupName,
fieldName
)
|| writeData<sphericalTensor>
(
meshes,
groupName,
fieldName
)
|| writeData<symmTensor>
(
meshes,
groupName,
fieldName
)
|| writeData<tensor>
(
meshes,
groupName,
fieldName
)
);
if (!ok)
{
WarningInFunction
<< "Field " << fieldName << " in regions " << compName
<< " was not found." << endl;
}
}
}
}
}
void Foam::functionObjects::externalCoupled::initialise()
{
if (initialised_)
{ {
return; return;
} }
// Write the geometry if not already there // Write the geometry if not already there
forAll(regionGroupRegions_, i) forAll(regionGroupNames_, regioni)
{ {
const word& compName = regionGroupNames_[i]; const word& compName = regionGroupNames_[regioni];
const wordList& regionNames = regionGroupRegions_[i]; const wordList& regionNames = regionGroupRegions_[regioni];
// Get the meshes for the region-group // Get the meshes for the region-group
UPtrList<const fvMesh> meshes(regionNames.size()); UPtrList<const fvMesh> meshes(regionNames.size());
forAll(regionNames, j) forAll(regionNames, regi)
{ {
const word& regionName = regionNames[j]; const word& regionName = regionNames[regi];
meshes.set(j, &time_.lookupObject<fvMesh>(regionName)); meshes.set(regi, &time_.lookupObject<fvMesh>(regionName));
} }
const labelList& groups = regionToGroups_[compName]; const labelList& groups = regionToGroups_[compName];
forAll(groups, i) for (const label groupi : groups)
{ {
label groupi = groups[i];
const wordRe& groupName = groupNames_[groupi]; const wordRe& groupName = groupNames_[groupi];
bool exists = false; bool geomExists = false;
if (Pstream::master()) if (Pstream::master())
{ {
fileName dir(groupDir(commsDir_, compName, groupName)); fileName dir(groupDir(commDirectory(), compName, groupName));
exists = geomExists =
isFile(dir/"patchPoints") isFile(dir/"patchPoints")
|| isFile(dir/"patchFaces"); || isFile(dir/"patchFaces");
} }
if (!returnReduce(exists, orOp<bool>())) Pstream::scatter(geomExists);
if (!geomExists)
{ {
writeGeometry(meshes, commsDir_, groupName); writeGeometry(meshes, commDirectory(), groupName);
} }
} }
} }
if (slaveFirst_) if (slaveFirst())
{ {
// Wait for initial data to be made available // Wait for initial data to be made available
waitForSlave(); waitForSlave();
// Read data passed back from external source // Read data passed back from external source
readData(); readDataMaster();
} }
initialised_ = true; initialisedCoupling_ = true;
} }
@ -806,18 +471,13 @@ Foam::functionObjects::externalCoupled::externalCoupled
) )
: :
functionObject(name), functionObject(name),
externalFileCoupler(),
time_(runTime), time_(runTime),
stateEnd_(REMOVE), initialisedCoupling_(false)
initialised_(false)
{ {
read(dict); read(dict);
if (Pstream::master()) if (!slaveFirst())
{
mkDir(baseDir());
}
if (!slaveFirst_)
{ {
useMaster(); useMaster();
} }
@ -827,22 +487,20 @@ Foam::functionObjects::externalCoupled::externalCoupled
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
Foam::functionObjects::externalCoupled::~externalCoupled() Foam::functionObjects::externalCoupled::~externalCoupled()
{ {}
cleanup();
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
bool Foam::functionObjects::externalCoupled::execute() bool Foam::functionObjects::externalCoupled::execute()
{ {
if (!initialised_ || time_.timeIndex() % calcFrequency_ == 0) if (!initialisedCoupling_ || time_.timeIndex() % calcFrequency_ == 0)
{ {
// Initialise the coupling // Initialise the coupling
initialise(); initCoupling();
// Write data for external source // Write data for external source
writeData(); writeDataMaster();
// Signal external source to execute (by removing lock file) // Signal external source to execute (by removing lock file)
// - Wait for slave to provide data // - Wait for slave to provide data
@ -855,7 +513,7 @@ bool Foam::functionObjects::externalCoupled::execute()
removeDataMaster(); removeDataMaster();
// Read data passed back from external source // Read data passed back from external source
readData(); readDataMaster();
// Signal external source to wait (by creating the lock file) // Signal external source to wait (by creating the lock file)
useMaster(); useMaster();
@ -876,7 +534,7 @@ bool Foam::functionObjects::externalCoupled::end()
// Remove old data files // Remove old data files
removeDataMaster(); removeDataMaster();
removeDataSlave(); removeDataSlave();
cleanup(); shutdown();
return true; return true;
} }
@ -885,33 +543,10 @@ bool Foam::functionObjects::externalCoupled::end()
bool Foam::functionObjects::externalCoupled::read(const dictionary& dict) bool Foam::functionObjects::externalCoupled::read(const dictionary& dict)
{ {
functionObject::read(dict); functionObject::read(dict);
externalFileCoupler::readDict(dict);
// NB: Cannot change directory or initialization
// if things have already been initialized
if (!initialised_)
{
dict.lookup("commsDir") >> commsDir_;
commsDir_.expand();
commsDir_.clean();
slaveFirst_ = readBool(dict.lookup("initByExternal"));
// slaveFirst_ = dict.lookupOrDefault<bool>("initByExternal", false);
}
calcFrequency_ = dict.lookupOrDefault("calcFrequency", 1); calcFrequency_ = dict.lookupOrDefault("calcFrequency", 1);
waitInterval_ = dict.lookupOrDefault("waitInterval", 1u);
if (!waitInterval_)
{
// Enforce non-zero sleep
waitInterval_ = 1u;
}
timeOut_ = dict.lookupOrDefault("timeOut", 100*waitInterval_);
stateEnd_ =
stateEndNames_.lookupOrDefault("stateEnd", dict, stateEnd::DONE);
// Get names of all fvMeshes (and derived types) // Get names of all fvMeshes (and derived types)
wordList allRegionNames(time_.lookupClass<fvMesh>().sortedToc()); wordList allRegionNames(time_.lookupClass<fvMesh>().sortedToc());
@ -960,7 +595,7 @@ bool Foam::functionObjects::externalCoupled::read(const dictionary& dict)
regionToGroups_.insert regionToGroups_.insert
( (
regionGroupNames_.last(), regionGroupNames_.last(),
labelList(1, nGroups) labelList{nGroups}
); );
} }
groupNames_.append(groupName); groupNames_.append(groupName);
@ -971,16 +606,12 @@ bool Foam::functionObjects::externalCoupled::read(const dictionary& dict)
Info<< type() << ": Communicating with regions:" << endl; Info<< type() << ": Communicating with regions:" << endl;
forAll(regionGroupNames_, rgi) for (const word& compName : regionGroupNames_)
{ {
//const wordList& regionNames = regionGroupRegions_[rgi];
const word& compName = regionGroupNames_[rgi];
Info<< "Region: " << compName << endl << incrIndent; Info<< "Region: " << compName << endl << incrIndent;
const labelList& groups = regionToGroups_[compName]; const labelList& groups = regionToGroups_[compName];
forAll(groups, i) for (const label groupi : groups)
{ {
label groupi = groups[i];
const wordRe& groupName = groupNames_[groupi]; const wordRe& groupName = groupNames_[groupi];
Info<< indent << "patchGroup: " << groupName << "\t" Info<< indent << "patchGroup: " << groupName << "\t"
@ -1003,17 +634,15 @@ bool Foam::functionObjects::externalCoupled::read(const dictionary& dict)
// should already be written - but just make sure // should already be written - but just make sure
if (Pstream::master()) if (Pstream::master())
{ {
forAll(regionGroupNames_, rgi) for (const word& compName : regionGroupNames_)
{ {
const word& compName = regionGroupNames_[rgi];
const labelList& groups = regionToGroups_[compName]; const labelList& groups = regionToGroups_[compName];
forAll(groups, i) for (const label groupi : groups)
{ {
label groupi = groups[i];
const wordRe& groupName = groupNames_[groupi]; const wordRe& groupName = groupNames_[groupi];
fileName dir(groupDir(commsDir_, compName, groupName)); fileName dir(groupDir(commDirectory(), compName, groupName));
if (!isDir(dir)) if (!isDir(dir))
{ {
Log << type() << ": creating communications directory " Log << type() << ": creating communications directory "
@ -1028,6 +657,156 @@ bool Foam::functionObjects::externalCoupled::read(const dictionary& dict)
} }
void Foam::functionObjects::externalCoupled::readDataMaster()
{
forAll(regionGroupNames_, regioni)
{
const word& compName = regionGroupNames_[regioni];
const wordList& regionNames = regionGroupRegions_[regioni];
// Get the meshes for the region-group
UPtrList<const fvMesh> meshes(regionNames.size());
forAll(regionNames, j)
{
const word& regionName = regionNames[j];
meshes.set(j, &time_.lookupObject<fvMesh>(regionName));
}
const labelList& groups = regionToGroups_[compName];
for (const label groupi : groups)
{
const wordRe& groupName = groupNames_[groupi];
const wordList& fieldNames = groupReadFields_[groupi];
for (const word& fieldName : fieldNames)
{
const bool ok =
(
readData<scalar>(meshes, groupName, fieldName)
|| readData<vector>(meshes, groupName, fieldName)
|| readData<sphericalTensor>(meshes, groupName, fieldName)
|| readData<symmTensor>(meshes, groupName, fieldName)
|| readData<tensor>(meshes, groupName, fieldName)
);
if (!ok)
{
WarningInFunction
<< "Field " << fieldName << " in regions " << compName
<< " was not found." << endl;
}
}
}
}
}
void Foam::functionObjects::externalCoupled::writeDataMaster() const
{
forAll(regionGroupNames_, regioni)
{
const word& compName = regionGroupNames_[regioni];
const wordList& regionNames = regionGroupRegions_[regioni];
// Get the meshes for the region-group
UPtrList<const fvMesh> meshes(regionNames.size());
forAll(regionNames, j)
{
const word& regionName = regionNames[j];
meshes.set(j, &time_.lookupObject<fvMesh>(regionName));
}
const labelList& groups = regionToGroups_[compName];
for (const label groupi : groups)
{
const wordRe& groupName = groupNames_[groupi];
const wordList& fieldNames = groupWriteFields_[groupi];
for (const word& fieldName : fieldNames)
{
const bool ok =
(
writeData<scalar>(meshes, groupName, fieldName)
|| writeData<vector>(meshes, groupName, fieldName)
|| writeData<sphericalTensor>(meshes, groupName, fieldName)
|| writeData<symmTensor>(meshes, groupName, fieldName)
|| writeData<tensor>(meshes, groupName, fieldName)
);
if (!ok)
{
WarningInFunction
<< "Field " << fieldName << " in regions " << compName
<< " was not found." << endl;
}
}
}
}
}
void Foam::functionObjects::externalCoupled::removeDataMaster() const
{
if (!Pstream::master())
{
return;
}
Log << type() << ": removing data files written by master" << nl;
for (const word& compName : regionGroupNames_)
{
const labelList& groups = regionToGroups_[compName];
for (const label groupi : groups)
{
const wordRe& groupName = groupNames_[groupi];
const wordList& fieldNames = groupReadFields_[groupi];
for (const word& fieldName : fieldNames)
{
Foam::rm
(
groupDir(commDirectory(), compName, groupName)
/ fieldName + ".out"
);
}
}
}
}
void Foam::functionObjects::externalCoupled::removeDataSlave() const
{
if (!Pstream::master())
{
return;
}
Log << type() << ": removing data files written by slave" << nl;
for (const word& compName : regionGroupNames_)
{
const labelList& groups = regionToGroups_[compName];
for (const label groupi : groups)
{
const wordRe& groupName = groupNames_[groupi];
const wordList& fieldNames = groupReadFields_[groupi];
for (const word& fieldName : fieldNames)
{
Foam::rm
(
groupDir(commDirectory(), compName, groupName)
/ fieldName + ".in"
);
}
}
}
}
bool Foam::functionObjects::externalCoupled::write() bool Foam::functionObjects::externalCoupled::write()
{ {
return true; return true;

View File

@ -42,7 +42,7 @@ Description
where the actual entries depend on the bc type: where the actual entries depend on the bc type:
- mixed: value, snGrad, refValue, refGrad, valueFraction - mixed: value, snGrad, refValue, refGrad, valueFraction
- externalCoupledMixed: output of writeData - externalCoupledMixed: output of writeDataMaster
- other: value, snGrad - other: value, snGrad
These text files are located in a user specified communications directory These text files are located in a user specified communications directory
@ -146,6 +146,7 @@ SourceFiles
#define functionObjects_externalCoupled_H #define functionObjects_externalCoupled_H
#include "functionObject.H" #include "functionObject.H"
#include "externalFileCoupler.H"
#include "DynamicList.H" #include "DynamicList.H"
#include "wordReList.H" #include "wordReList.H"
#include "scalarField.H" #include "scalarField.H"
@ -171,7 +172,8 @@ namespace functionObjects
class externalCoupled class externalCoupled
: :
public functionObject public functionObject,
public externalFileCoupler
{ {
public: public:
@ -190,30 +192,14 @@ private:
//- State end names (NB, only selectable values itemized) //- State end names (NB, only selectable values itemized)
static const Enum<stateEnd> stateEndNames_; static const Enum<stateEnd> stateEndNames_;
// Private data // Private data
//- Reference to the time database //- Reference to the time database
const Time& time_; const Time& time_;
//- Path to communications directory
fileName commsDir_;
//- Interval time between checking for return data [s]
unsigned waitInterval_;
//- Time out time [s]
unsigned timeOut_;
//- Calculation frequency //- Calculation frequency
label calcFrequency_; label calcFrequency_;
//- Flag to indicate values are initialised by external application
bool slaveFirst_;
//- Lockfile state on termination
mutable stateEnd stateEnd_;
//- Names of (composite) regions //- Names of (composite) regions
DynamicList<word> regionGroupNames_; DynamicList<word> regionGroupNames_;
@ -232,8 +218,8 @@ private:
// Per group the names of the fields to write // Per group the names of the fields to write
DynamicList<wordList> groupWriteFields_; DynamicList<wordList> groupWriteFields_;
//- Initialised flag //- Initialised coupling
bool initialised_; bool initialisedCoupling_;
// Private Member Functions // Private Member Functions
@ -246,32 +232,6 @@ private:
const wordRe& groupName const wordRe& groupName
); );
//- Return the file path to the base communications directory
fileName baseDir() const;
//- Return the file path to the lock file
fileName lockFile() 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 removeDataMaster() const;
//- Remove files written by external code
void removeDataSlave() 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 //- Read data for a single region, single field
template<class Type> template<class Type>
@ -281,8 +241,6 @@ private:
const wordRe& groupName, const wordRe& groupName,
const word& fieldName const word& fieldName
); );
//- Read data for all regions, all fields
void readData();
//- Write data for a single region, single field //- Write data for a single region, single field
template<class Type> template<class Type>
@ -293,10 +251,7 @@ private:
const word& fieldName const word& fieldName
) const; ) const;
//- Write data for all regions, all fields void initCoupling();
void writeData() const;
void initialise();
//- Read (and distribute) scalar columns from stream. Every processor //- Read (and distribute) scalar columns from stream. Every processor
// gets nRows (= patch size) of these. Note: could make its argument // gets nRows (= patch size) of these. Note: could make its argument
@ -339,12 +294,12 @@ public:
//- Runtime type information //- Runtime type information
TypeName("externalCoupled"); TypeName("externalCoupled");
//- 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; static string patchKey;
//- Inherited variable for logging
using functionObject::log;
// Constructors // Constructors
@ -378,6 +333,21 @@ public:
virtual bool write(); virtual bool write();
// File creation, removal
//- Write data files (all regions, all fields) from master (OpenFOAM)
virtual void writeDataMaster() const;
//- Read data files (all regions, all fields) on master (OpenFOAM)
virtual void readDataMaster();
//- Remove data files written by master (OpenFOAM)
virtual void removeDataMaster() const;
//- Remove data files written by slave (external code)
virtual void removeDataSlave() const;
// Other // Other
//- Create single name by appending words (in sorted order), //- Create single name by appending words (in sorted order),

View File

@ -62,7 +62,7 @@ bool Foam::functionObjects::externalCoupled::readData
{ {
const fileName transferFile const fileName transferFile
( (
groupDir(commsDir_, compositeName(regionNames), groupName) groupDir(commDirectory(), compositeName(regionNames), groupName)
/ fieldName + ".in" / fieldName + ".in"
); );
@ -81,46 +81,40 @@ bool Foam::functionObjects::externalCoupled::readData
label nFound = 0; label nFound = 0;
forAll(meshes, i) for (const fvMesh& mesh : meshes)
{ {
const fvMesh& mesh = meshes[i]; const volFieldType* vfptr =
mesh.lookupObjectPtr<volFieldType>(fieldName);
if (!mesh.foundObject<volFieldType>(fieldName)) if (!vfptr)
{ {
continue; continue;
} }
nFound++; nFound++;
const volFieldType& cvf = mesh.lookupObject<volFieldType>(fieldName); typename volFieldType::Boundary& bf =
const typename volFieldType::Boundary& bf = cvf.boundaryField(); const_cast<volFieldType*>(vfptr)->boundaryFieldRef();
// Get the patches // Get the patches
const labelList patchIDs const labelList patchIDs
( (
mesh.boundaryMesh().patchSet mesh.boundaryMesh().patchSet
( (
List<wordRe>(1, groupName) List<wordRe>{groupName}
).sortedToc() ).sortedToc()
); );
// Handle column-wise reading of patch data. Supports most easy types // Handle column-wise reading of patch data. Supports most easy types
forAll(patchIDs, i) for (const label patchi : patchIDs)
{ {
label patchi = patchIDs[i];
if (isA<patchFieldType>(bf[patchi])) if (isA<patchFieldType>(bf[patchi]))
{ {
// Explicit handling of externalCoupledMixed bcs - they // Explicit handling of externalCoupledMixed bcs - they
// have specialised reading routines. // have specialised reading routines.
patchFieldType& pf = const_cast<patchFieldType&> patchFieldType& pf = refCast<patchFieldType>
(
refCast<const patchFieldType>
( (
bf[patchi] bf[patchi]
)
); );
// Read from master into local stream // Read from master into local stream
@ -141,6 +135,11 @@ bool Foam::functionObjects::externalCoupled::readData
} }
else if (isA<mixedFvPatchField<Type>>(bf[patchi])) else if (isA<mixedFvPatchField<Type>>(bf[patchi]))
{ {
mixedFvPatchField<Type>& pf = refCast<mixedFvPatchField<Type>>
(
bf[patchi]
);
// Read columns from file for // Read columns from file for
// value, snGrad, refValue, refGrad, valueFraction // value, snGrad, refValue, refGrad, valueFraction
List<scalarField> data; List<scalarField> data;
@ -152,15 +151,6 @@ bool Foam::functionObjects::externalCoupled::readData
data data
); );
mixedFvPatchField<Type>& pf =
const_cast<mixedFvPatchField<Type>&>
(
refCast<const mixedFvPatchField<Type>>
(
bf[patchi]
)
);
// Transfer read data to bc. // Transfer read data to bc.
// Skip value, snGrad // Skip value, snGrad
direction columni = 2*pTraits<Type>::nComponents; direction columni = 2*pTraits<Type>::nComponents;
@ -193,6 +183,9 @@ bool Foam::functionObjects::externalCoupled::readData
} }
else if (isA<fixedGradientFvPatchField<Type>>(bf[patchi])) else if (isA<fixedGradientFvPatchField<Type>>(bf[patchi]))
{ {
fixedGradientFvPatchField<Type>& pf =
refCast<fixedGradientFvPatchField<Type>>(bf[patchi]);
// Read columns for value and gradient // Read columns for value and gradient
List<scalarField> data; List<scalarField> data;
readColumns readColumns
@ -203,15 +196,6 @@ bool Foam::functionObjects::externalCoupled::readData
data data
); );
fixedGradientFvPatchField<Type>& pf =
const_cast<fixedGradientFvPatchField<Type>&>
(
refCast<const fixedGradientFvPatchField<Type>>
(
bf[patchi]
)
);
// Transfer gradient to bc // Transfer gradient to bc
Field<Type>& gradient = pf.gradient(); Field<Type>& gradient = pf.gradient();
for for
@ -234,6 +218,9 @@ bool Foam::functionObjects::externalCoupled::readData
} }
else if (isA<fixedValueFvPatchField<Type>>(bf[patchi])) else if (isA<fixedValueFvPatchField<Type>>(bf[patchi]))
{ {
fixedValueFvPatchField<Type>& pf =
refCast<fixedValueFvPatchField<Type>>(bf[patchi]);
// Read columns for value only // Read columns for value only
List<scalarField> data; List<scalarField> data;
readColumns readColumns
@ -256,15 +243,6 @@ bool Foam::functionObjects::externalCoupled::readData
value.replace(cmpt, data[cmpt]); value.replace(cmpt, data[cmpt]);
} }
fixedValueFvPatchField<Type>& pf =
const_cast<fixedValueFvPatchField<Type>&>
(
refCast<const fixedValueFvPatchField<Type>>
(
bf[patchi]
)
);
pf == value; pf == value;
// Update the value from the read coefficicient. Bypass any // Update the value from the read coefficicient. Bypass any
@ -280,7 +258,7 @@ bool Foam::functionObjects::externalCoupled::readData
<< exit(FatalError); << exit(FatalError);
} }
initialised_ = true; initialisedCoupling_ = true;
} }
} }
@ -357,7 +335,7 @@ bool Foam::functionObjects::externalCoupled::writeData
{ {
const fileName transferFile const fileName transferFile
( (
groupDir(commsDir_, compositeName(regionNames), groupName) groupDir(commDirectory(), compositeName(regionNames), groupName)
/ fieldName + ".out" / fieldName + ".out"
); );
@ -379,35 +357,32 @@ bool Foam::functionObjects::externalCoupled::writeData
label nFound = 0; label nFound = 0;
forAll(meshes, i) for (const fvMesh& mesh : meshes)
{ {
const fvMesh& mesh = meshes[i]; const volFieldType* vfptr =
mesh.lookupObjectPtr<volFieldType>(fieldName);
if (!mesh.foundObject<volFieldType>(fieldName)) if (!vfptr)
{ {
continue; continue;
} }
nFound++; nFound++;
const volFieldType& cvf = mesh.lookupObject<volFieldType>(fieldName); const typename volFieldType::Boundary& bf =
const typename volFieldType::Boundary& bf = cvf.boundaryField(); vfptr->boundaryField();
// Get the patches // Get the patches
const labelList patchIDs const labelList patchIDs
( (
mesh.boundaryMesh().patchSet mesh.boundaryMesh().patchSet
( (
List<wordRe>(1, groupName) List<wordRe>{groupName}
).sortedToc() ).sortedToc()
); );
// Handle column-wise writing of patch data. Supports most easy types // Handle column-wise writing of patch data. Supports most easy types
forAll(patchIDs, i) for (const label patchi : patchIDs)
{ {
label patchi = patchIDs[i];
const globalIndex globalFaces(bf[patchi].size()); const globalIndex globalFaces(bf[patchi].size());
if (isA<patchFieldType>(bf[patchi])) if (isA<patchFieldType>(bf[patchi]))

View File

@ -3,7 +3,7 @@
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | \\ / O peration |
\\ / A nd | Copyright (C) 2011-2017 OpenFOAM Foundation \\ / A nd | Copyright (C) 2011-2017 OpenFOAM Foundation
\\/ M anipulation | Copyright (C) 2016 OpenCFD Ltd. \\/ M anipulation | Copyright (C) 2016-2017 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -61,18 +61,42 @@ Foam::functionObjects::abort::actionNames_
}; };
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // // * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
void Foam::functionObjects::abort::removeFile() const // file-scope
// Long description for the action name
namespace Foam
{ {
bool hasAbort = isFile(abortFile_); static std::string longDescription(const Time::stopAtControls ctrl)
reduce(hasAbort, orOp<bool>()); {
switch (ctrl)
if (hasAbort && Pstream::master())
{ {
// Cleanup ABORT file (on master only) case Foam::Time::saNoWriteNow :
rm(abortFile_); {
return "stop without writing data";
break;
} }
case Time::saWriteNow :
{
return "stop and write data";
break;
}
case Time::saNextWrite :
{
return "stop after next data write";
break;
}
default:
{
// Invalid choices already filtered out by Enum
return "abort";
break;
}
}
}
} }
@ -88,13 +112,17 @@ Foam::functionObjects::abort::abort
functionObject(name), functionObject(name),
time_(runTime), time_(runTime),
abortFile_("$FOAM_CASE/" + name), abortFile_("$FOAM_CASE/" + name),
action_(Time::stopAtControls::saNextWrite) action_(Time::stopAtControls::saNextWrite),
triggered_(false)
{ {
abortFile_.expand(); abortFile_.expand();
read(dict); read(dict);
// Remove any old files from previous runs // Cleanup old files from previous runs
removeFile(); if (Pstream::master())
{
Foam::rm(abortFile_);
}
} }
@ -110,6 +138,13 @@ bool Foam::functionObjects::abort::read(const dictionary& dict)
{ {
functionObject::read(dict); functionObject::read(dict);
if (dict.readIfPresent("file", abortFile_))
{
abortFile_.expand();
}
const auto oldAction = action_;
action_ = actionNames_.lookupOrDefault action_ = actionNames_.lookupOrDefault
( (
"action", "action",
@ -117,64 +152,42 @@ bool Foam::functionObjects::abort::read(const dictionary& dict)
Time::stopAtControls::saNextWrite Time::stopAtControls::saNextWrite
); );
if (dict.readIfPresent("file", abortFile_)) // User can change action and re-trigger the abort.
// eg, they had nextWrite, but actually wanted writeNow.
if (oldAction != action_)
{ {
abortFile_.expand(); triggered_ = false;
} }
Info<< type() << " activated ("
<< longDescription(action_).c_str() <<")" << nl
<< " File: " << abortFile_ << endl;
return true; return true;
} }
bool Foam::functionObjects::abort::execute() bool Foam::functionObjects::abort::execute()
{ {
bool hasAbort = isFile(abortFile_); // If it has been triggered (eg, nextWrite) don't need to check it again
reduce(hasAbort, orOp<bool>()); if (!triggered_)
{
bool hasAbort = (Pstream::master() && isFile(abortFile_));
Pstream::scatter(hasAbort);
if (hasAbort) if (hasAbort)
{ {
switch (action_) triggered_ = time_.stopAt(action_);
{
case Time::saNoWriteNow : if (triggered_)
{
if (time_.stopAt(action_))
{ {
Info<< "USER REQUESTED ABORT (timeIndex=" Info<< "USER REQUESTED ABORT (timeIndex="
<< time_.timeIndex() << time_.timeIndex()
<< "): stop without writing data" << "): " << longDescription(action_).c_str()
<< endl; << endl;
} }
break;
}
case Time::saWriteNow : Pstream::scatter(triggered_);
{
if (time_.stopAt(action_))
{
Info<< "USER REQUESTED ABORT (timeIndex="
<< time_.timeIndex()
<< "): stop+write data"
<< endl;
}
break;
}
case Time::saNextWrite :
{
if (time_.stopAt(action_))
{
Info<< "USER REQUESTED ABORT (timeIndex="
<< time_.timeIndex()
<< "): stop after next data write"
<< endl;
}
break;
}
default:
{
// Invalid choices already filtered out by Enum
}
} }
} }
@ -190,7 +203,12 @@ bool Foam::functionObjects::abort::write()
bool Foam::functionObjects::abort::end() bool Foam::functionObjects::abort::end()
{ {
removeFile(); // Cleanup ABORT file
if (Pstream::master())
{
Foam::rm(abortFile_);
}
return true; return true;
} }

View File

@ -3,7 +3,7 @@
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | \\ / O peration |
\\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation \\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation
\\/ M anipulation | \\/ M anipulation | Copyright (C) 2017 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -30,12 +30,21 @@ Group
Description Description
Watches for presence of the named file in the $FOAM_CASE directory Watches for presence of the named file in the $FOAM_CASE directory
and aborts the calculation if it is present. and aborts the calculation if it is present.
The presence of the abort file is only checked on the master process.
Currently the following action types are supported: Currently the following action types are supported:
- noWriteNow - noWriteNow
- writeNow - writeNow
- nextWrite - nextWrite
\heading Function object usage
\table
Property | Description | Required | Default value
type | Type name: abort | yes |
file | The abort filename | no | $FOAM_CASE/name
action | Abort action | no | nextWrite
\endtable
SourceFiles SourceFiles
abort.C abort.C
@ -76,12 +85,12 @@ class abort
//- The type of action //- The type of action
Time::stopAtControls action_; Time::stopAtControls action_;
//- Only trigger action once
bool triggered_;
// Private Member Functions // Private Member Functions
//- Remove abort file.
void removeFile() const;
//- Disallow default bitwise copy construct //- Disallow default bitwise copy construct
abort(const abort&) = delete; abort(const abort&) = delete;
@ -102,7 +111,7 @@ public:
( (
const word& name, const word& name,
const Time& runTime, const Time& runTime,
const dictionary& const dictionary& dict
); );
@ -115,13 +124,13 @@ public:
//- Read the dictionary settings //- Read the dictionary settings
virtual bool read(const dictionary& dict); virtual bool read(const dictionary& dict);
//- Execute, check existence of abort file and take action //- Check existence of abort file and take action
virtual bool execute(); virtual bool execute();
//- Execute, check existence of abort file and take action //- No-op
virtual bool write(); virtual bool write();
//- Execute at the final time-loop, used for cleanup //- Remove abort file after the final time-loop.
virtual bool end(); virtual bool end();
}; };

View File

@ -1,5 +1,3 @@
externalCoupler.C
lumpedPointMovement.C lumpedPointMovement.C
lumpedPointMovementWriter.C lumpedPointMovementWriter.C
lumpedPointState.C lumpedPointState.C

View File

@ -50,7 +50,7 @@ SourceFiles
#include "IOobject.H" #include "IOobject.H"
#include "tmp.H" #include "tmp.H"
#include "faceZoneMeshFwd.H" #include "faceZoneMeshFwd.H"
#include "externalCoupler.H" #include "externalFileCoupler.H"
#include "lumpedPointState.H" #include "lumpedPointState.H"
#include "boundBox.H" #include "boundBox.H"
#include "Enum.H" #include "Enum.H"
@ -120,7 +120,7 @@ private:
dictionary forcesDict_; dictionary forcesDict_;
//- Communication control //- Communication control
externalCoupler coupler_; externalFileCoupler coupler_;
//- File io //- File io
word inputName_; word inputName_;
@ -223,10 +223,10 @@ public:
//- Communication control //- Communication control
inline const externalCoupler& coupler() const; inline const externalFileCoupler& coupler() const;
//- Communication control //- Communication control
inline externalCoupler& coupler(); inline externalFileCoupler& coupler();
//- The initial state (positions/rotations) //- The initial state (positions/rotations)
inline const lumpedPointState& state0() const; inline const lumpedPointState& state0() const;

View File

@ -83,13 +83,14 @@ Foam::lumpedPointMovement::threshold(const point& position) const
} }
inline const Foam::externalCoupler& Foam::lumpedPointMovement::coupler() const inline const Foam::externalFileCoupler&
Foam::lumpedPointMovement::coupler() const
{ {
return coupler_; return coupler_;
} }
inline Foam::externalCoupler& Foam::lumpedPointMovement::coupler() inline Foam::externalFileCoupler& Foam::lumpedPointMovement::coupler()
{ {
return coupler_; return coupler_;
} }

View File

@ -0,0 +1,13 @@
// OpenFOAM dictionary -*- C++ -*-
ABORT
{
type abort;
libs ("libutilityFunctionObjects.so");
//file "$FOAM_CASE/ABORT"; // default name
// action writeNow;
action nextWrite;
}
// ************************************************************************* //

View File

@ -59,6 +59,7 @@ writeInterval 100;
functions functions
{ {
#include "abort"
#include "scalarTransport" #include "scalarTransport"
#include "sampling" #include "sampling"
} }

View File

@ -6,6 +6,9 @@ cd ${0%/*} || exit 1 # Run from this directory
./Allrun.pre ./Allrun.pre
# Remove lock file on interrupt
trap '\rm -f comms/OpenFOAM.lock 2>/dev/null' INT
#-- Run on single processor #-- Run on single processor
#runApplication $(getApplication) & #runApplication $(getApplication) &
# Simulated external solver # Simulated external solver

View File

@ -14,12 +14,11 @@ set -u
echo "Executing dummy external solver" echo "Executing dummy external solver"
commsDir="comms" commsDir="comms"
regionGroupName="heater_topAir" patchDir="heater_topAir/coupleGroup"
patchGroupName="coupleGroup"
fieldName="T" fieldName="T"
lockFile="${commsDir}/OpenFOAM.lock" lockFile="${commsDir}/OpenFOAM.lock"
dataFile="${commsDir}/${regionGroupName}/${patchGroupName}/${fieldName}" dataFile="${commsDir}/${patchDir}/${fieldName}"
waitSec=5 waitSec=5
timeOut=100 timeOut=100
nSteps=1000 # maximum number of time steps. Note: should be more than nSteps=1000 # maximum number of time steps. Note: should be more than
@ -27,7 +26,6 @@ nSteps=1000 # maximum number of time steps. Note: should be more than
refGrad=0 refGrad=0
valueFraction=1 valueFraction=1
# Remove any old junk # Remove any old junk
\rm -f $lockFile 2>/dev/null \rm -f $lockFile 2>/dev/null
@ -36,36 +34,53 @@ log()
echo "External: $@" echo "External: $@"
} }
init()
# Create lock file to pass control to OpenFOAM
useMaster()
{ {
log "init - creating ${dataFile}.in" log "creating lock file '${lockFile}'"
echo "status=openfoam" >| ${lockFile}
# Hard-coded for patch of size 8 (heater/minY)
n1=8
refValue1=500
touch "${dataFile}.in"
log "init - adding $n1 data elements with refValue $refValue1"
for i in $(seq 1 $n1); do
echo "$refValue1 $refGrad $valueFraction" >> "${dataFile}.in"
done
# Hard-coded for patch of size 40 (topAir/minX)
n2=40
refValue2=300
log "init - adding $n2 data elements with refValue $refValue2"
for i in $(seq 1 $n2); do
echo "$refValue2 $refGrad $valueFraction" >> "${dataFile}.in"
done
# Create lock file to pass control to OpenFOAM
touch ${lockFile}
} }
# create the comms directory init()
mkdir -p ${commsDir}/${regionGroupName}/${patchGroupName} {
log "init - creating ${dataFile}.in"
cat /dev/null >| "${dataFile}.in"
# Tutorial case uses 'initByExternalOption', so we must provide initial values # Hard-coded for patch of size 8 (heater/minY)
local n1=8
local refValue1=500
log "init - adding $n1 data elements with refValue $refValue1"
for i in $(seq 1 $n1)
do
echo "$refValue1 $refGrad $valueFraction"
done >> "${dataFile}.in"
# Hard-coded for patch of size 40 (topAir/minX)
local n2=40
local refValue2=300
log "init - adding $n2 data elements with refValue $refValue2"
for i in $(seq 1 $n2)
do
echo "$refValue2 $refGrad $valueFraction"
done >> "${dataFile}.in"
# Verify line count?
# log "init ($(wc -l ${dataFile}.in))"
# Give time for T.in file to flush
sleep 1
useMaster
}
# Create the comms directory
mkdir -p ${commsDir}/${patchDir}
# Tutorial case uses 'initByExternal' option, so we must provide initial values
init init
@ -108,8 +123,7 @@ do
awk '{if( $1 != "#" ){print $1+1 " 0 1"}}' \ awk '{if( $1 != "#" ){print $1+1 " 0 1"}}' \
${dataFile}.out >| ${dataFile}.in ${dataFile}.out >| ${dataFile}.in
log "creating lock file '${lockFile}'" useMaster
touch ${lockFile}
fi fi
done done

View File

@ -14,9 +14,6 @@ externalCoupled
// Does external process start first // Does external process start first
initByExternal true; initByExternal true;
// Cleanup behaviour on termination (remove|done)
stateEnd done;
// Additional output // Additional output
log true; log true;