ENH: refactor and combine externalFileCoupler (issue #529)

This commit is contained in:
Mark Olesen
2017-07-17 12:54:02 +02:00
parent e045d6c03b
commit 7a408c713b
19 changed files with 578 additions and 601 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

@ -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,
@ -592,155 +404,9 @@ 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;
} }
@ -769,7 +435,7 @@ void Foam::functionObjects::externalCoupled::initialise()
bool exists = false; bool exists = false;
if (Pstream::master()) if (Pstream::master())
{ {
fileName dir(groupDir(commsDir_, compName, groupName)); fileName dir(groupDir(commDirectory(), compName, groupName));
exists = exists =
isFile(dir/"patchPoints") isFile(dir/"patchPoints")
@ -778,21 +444,21 @@ void Foam::functionObjects::externalCoupled::initialise()
if (!returnReduce(exists, orOp<bool>())) if (!returnReduce(exists, orOp<bool>()))
{ {
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 +472,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 +488,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 +514,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 +535,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 +544,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());
@ -1013,7 +649,8 @@ bool Foam::functionObjects::externalCoupled::read(const dictionary& dict)
label groupi = groups[i]; 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 +665,218 @@ 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];
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::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];
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::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(commDirectory(), compName, groupName)
/ fieldName + ".out"
);
}
}
}
}
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(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"
); );
@ -280,7 +280,7 @@ bool Foam::functionObjects::externalCoupled::readData
<< exit(FatalError); << exit(FatalError);
} }
initialised_ = true; initialisedCoupling_ = true;
} }
} }
@ -357,7 +357,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"
); );

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

@ -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()
{
log "init - creating ${dataFile}.in"
# 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 # Create lock file to pass control to OpenFOAM
touch ${lockFile} useMaster()
{
log "creating lock file '${lockFile}'"
echo "status=openfoam" >| ${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;