Commit 0d5bce68 authored by Mark Olesen's avatar Mark Olesen
Browse files

ENH: refactor and combine externalFileCoupler (issue #529)

parent b7fb6d60
Test-externalCoupler.C
EXE = $(FOAM_USER_APPBIN)/Test-externalCoupler
EXE_INC = \
-I$(LIB_SRC)/finiteVolume/lnInclude \
-I$(LIB_SRC)/lumpedPointMotion/lnInclude
EXE_LIBS = \
-lfiniteVolume \
-llumpedPointMotion
Test-externalFileCoupler.C
EXE = $(FOAM_USER_APPBIN)/Test-externalFileCoupler
EXE_INC = \
-I$(LIB_SRC)/finiteVolume/lnInclude
EXE_LIBS = \
-lfiniteVolume
......@@ -22,14 +22,14 @@ License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
Application
Test-externalCoupler
Test-externalFileCoupler
Description
Test of master/slave communication etc.
\*---------------------------------------------------------------------------*/
#include "argList.H"
#include "externalCoupler.H"
#include "externalFileCoupler.H"
using namespace Foam;
......@@ -47,7 +47,7 @@ int main(int argc, char *argv[])
const label maxCount = args.optionLookupOrDefault<label>("max", 1000);
externalCoupler coupler;
externalFileCoupler coupler;
if (args.optionFound("slave"))
{
......
......@@ -29,7 +29,7 @@ Description
points/rotations and the corresponding movement of the building surfaces.
Uses the tabulated responses from the specified file.
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
with the lumpedPointMovement point-patch boundary condition.
......@@ -131,7 +131,7 @@ int main(int argc, char *argv[])
{
Info<< "Running as slave responder" << endl;
externalCoupler& coupler = movement().coupler();
externalFileCoupler& coupler = movement().coupler();
label count = 0;
for (label index = 0; index < responseTable.size(); index += span)
......
......@@ -411,6 +411,9 @@ $(general)/bound/bound.C
$(general)/CorrectPhi/correctUphiBCs.C
$(general)/pressureControl/pressureControl.C
coupling = $(general)/coupling
$(coupling)/externalFileCoupler.C
solutionControl = $(general)/solutionControl
$(solutionControl)/solutionControl/solutionControl.C
$(solutionControl)/simpleControl/simpleControl.C
......
......@@ -23,22 +23,21 @@ License
\*---------------------------------------------------------------------------*/
#include "externalCoupler.H"
#include "externalFileCoupler.H"
#include "Pstream.H"
#include "PstreamReduceOps.H"
#include "OSspecific.H"
#include "IFstream.H"
#include "OFstream.H"
#include "Switch.H"
#include <fstream>
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam
{
defineTypeNameAndDebug(externalCoupler, 0);
defineTypeNameAndDebug(externalFileCoupler, 0);
}
Foam::word Foam::externalCoupler::lockName = "OpenFOAM";
Foam::word Foam::externalFileCoupler::lockName = "OpenFOAM";
// file-scope
......@@ -46,33 +45,33 @@ Foam::word Foam::externalCoupler::lockName = "OpenFOAM";
static bool checkIsDone(const std::string& lck)
{
std::string content;
std::ifstream is(lck.c_str());
std::ifstream is(lck);
is >> content;
return (content.find("done") != std::string::npos);
}
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
const Foam::fileName& Foam::externalCoupler::baseDir() const
{
return commsDir_;
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::fileName Foam::externalCoupler::lockFile() const
Foam::externalFileCoupler::externalFileCoupler()
:
runState_(NONE),
commsDir_("$FOAM_CASE/comms"),
waitInterval_(1u),
timeOut_(100u),
slaveFirst_(false),
log(false)
{
return resolveFile(lockName + ".lock");
commsDir_.expand();
commsDir_.clean();
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::externalCoupler::externalCoupler()
Foam::externalFileCoupler::externalFileCoupler(const fileName& commsDir)
:
runState_(NONE),
commsDir_("$FOAM_CASE/comms"),
commsDir_(commsDir),
waitInterval_(1u),
timeOut_(100u),
slaveFirst_(false),
......@@ -80,25 +79,30 @@ Foam::externalCoupler::externalCoupler()
{
commsDir_.expand();
commsDir_.clean();
if (Pstream::master())
{
mkDir(commsDir_);
}
}
Foam::externalCoupler::externalCoupler(const dictionary& dict)
Foam::externalFileCoupler::externalFileCoupler(const dictionary& dict)
:
externalCoupler()
externalFileCoupler()
{
readDict(dict);
if (Pstream::master())
{
mkDir(baseDir());
mkDir(commsDir_);
}
}
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
Foam::externalCoupler::~externalCoupler()
Foam::externalFileCoupler::~externalFileCoupler()
{
shutdown();
}
......@@ -106,14 +110,21 @@ Foam::externalCoupler::~externalCoupler()
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
bool Foam::externalCoupler::readDict(const dictionary& dict)
bool Foam::externalFileCoupler::readDict(const dictionary& dict)
{
// Normally cannot change directory or initialization
// if things have already been initialized
dict.lookup("commsDir") >> commsDir_;
commsDir_.expand();
commsDir_.clean();
slaveFirst_ = dict.lookupOrDefault<bool>("initByExternal", false);
if (!initialized())
{
dict.lookup("commsDir") >> commsDir_;
commsDir_.expand();
commsDir_.clean();
slaveFirst_ = dict.lookupOrDefault<bool>("initByExternal", false);
Info<< type() << ": initialize" << nl
<< " directory: " << commsDir_ << nl
<< " slave-first: " << Switch(slaveFirst_) << endl;
}
waitInterval_ = dict.lookupOrDefault("waitInterval", 1u);
if (!waitInterval_)
......@@ -130,19 +141,7 @@ bool Foam::externalCoupler::readDict(const dictionary& dict)
}
bool Foam::externalCoupler::initialized() const
{
return runState_ != NONE;
}
bool Foam::externalCoupler::slaveFirst() const
{
return slaveFirst_;
}
void Foam::externalCoupler::useMaster(const bool wait) const
void Foam::externalFileCoupler::useMaster(const bool wait) const
{
const bool wasInit = initialized();
runState_ = MASTER;
......@@ -162,7 +161,7 @@ void Foam::externalCoupler::useMaster(const bool wait) const
{
Log << type() << ": creating lock file" << endl;
OFstream os(lck);
std::ofstream os(lck);
os << "status=openfoam\n";
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();
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())
{
......@@ -212,7 +211,7 @@ bool Foam::externalCoupler::waitForMaster() const
{
const fileName lck(lockFile());
double prevTime = -1;
double prevTime = 0;
double 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())
{
......@@ -281,23 +280,38 @@ bool Foam::externalCoupler::waitForSlave() const
}
Foam::fileName Foam::externalCoupler::resolveFile
(
const word& file
) const
{
return fileName(baseDir()/file);
}
void Foam::externalFileCoupler::readDataMaster()
{}
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::externalCoupler::shutdown() const
void Foam::externalFileCoupler::shutdown() const
{
if (Pstream::master() && runState_ == MASTER && Foam::isDir(commsDir_))
{
const fileName lck(lockFile());
Log << type() << ": lock file status=done" << endl;
OFstream os(lck);
std::ofstream os(lck);
os << "status=done\n";
os.flush();
}
......
......@@ -22,7 +22,7 @@ License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
Class
Foam::externalCoupler
Foam::externalFileCoupler
Description
Encapsulates the logic for coordinating between OpenFOAM and an
......@@ -57,13 +57,32 @@ Description
}
\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
externalCoupler.C
externalFileCoupler.C
externalFileCouplerI.H
\*---------------------------------------------------------------------------*/
#ifndef externalCoupler_H
#define externalCoupler_H
#ifndef externalFileCoupler_H
#define externalFileCoupler_H
#include "fileName.H"
#include "dictionary.H"
......@@ -74,20 +93,26 @@ namespace Foam
{
/*---------------------------------------------------------------------------*\
Class externalCoupler Declaration
Class externalFileCoupler Declaration
\*---------------------------------------------------------------------------*/
class externalCoupler
class externalFileCoupler
{
//- The run state (ie, who is currently in charge)
enum runState
{
NONE, //!< Not initialized
MASTER, //!< The master (OpenFOAM) is in charge
SLAVE, //!< The slave (external program) is in charge
DONE //!< Finished
};
public:
// Public data types
//- The run state (ie, who is currently in charge)
enum runState
{
NONE, //!< Not initialized
MASTER, //!< The master (OpenFOAM) is in charge
SLAVE, //!< The slave (external program) is in charge
DONE //!< Finished
};
private:
// Private data
......@@ -103,7 +128,7 @@ class externalCoupler
//- Timeout [s] while waiting for the external application
unsigned timeOut_;
//- Flag to indicate values are initialized by external application
//- Flag to indicate values are initialized by external program
bool slaveFirst_;
//- Local logging/verbosity flag
......@@ -112,22 +137,17 @@ class externalCoupler
// Private Member Functions
//- Return the file path to the base communications directory
const fileName& baseDir() const;
//- Return the file path to the lock file
fileName lockFile() const;
//- Disallow default bitwise copy construc
externalCoupler(const externalCoupler&) = delete;
//- Disallow default bitwise copy construct
externalFileCoupler(const externalFileCoupler&) = delete;
//- Disallow default bitwise assignmen
void operator=(const externalCoupler&) = delete;
void operator=(const externalFileCoupler&) = delete;
public:
//- Runtime type information
TypeName("externalCoupler");
TypeName("externalFileCoupler");
// Static data members
......@@ -138,48 +158,98 @@ public:
// Constructors
//- Construct null using standard defaults
externalCoupler();
//- Construct using standard defaults.
// Does not create communications directory.
externalFileCoupler();
//- Construct with specified communications directory.
// Creates the communications directory upon construction.
externalFileCoupler(const fileName& commsDir);
//- Construct from dictionary
externalCoupler(const dictionary& dict);
// Creates the communications directory upon construction.
externalFileCoupler(const dictionary& dict);
//- Destructor
virtual ~externalCoupler();
virtual ~externalFileCoupler();
// Member Functions
// Access
// Initialization
//- True if state has been initialized
bool initialized() const;
inline bool initialized() const;
//- 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
// Optionally wait for master as well.
// Optionally wait for master to complete as well.
void useMaster(const bool wait=false) const;
//- Wait for indication that OpenFOAM has supplied output.
// This is when the lock file disappears, or it exists but with
//- Remove lock file to indicate that the external program is in charge
// Optionally wait for slave to complete as well.
void useSlave(const bool wait=false) const;
//- Wait for master to complete.
// This is when the lock file disappears, or exists but has
// "status=done" content.
// \return False if lock file contains "status=done"
bool waitForMaster() const;
//- Remove lock file to indicate that the external program is in charge
// Optionally wait for slave as well.
void useSlave(const bool wait=false) const;
//- Wait for indication that the external program has supplied input.
//- Wait for slave to complete.
// This is when the lock file appears.
// \return False if lock file contains "status=done"
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)
void shutdown() const;
......@@ -188,11 +258,6 @@ public:
void removeDirectory() const;
// Edit
//- Read communication settings from dictionary
bool readDict(const dictionary& dict);
};
......@@ -202,6 +267,10 @@ public:
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#include "externalFileCouplerI.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //
/*---------------------------------------------------------------------------*\
========= |
\\ / 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
{