externalFileCoupler.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration |
5  \\ / A nd | www.openfoam.com
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8  Copyright (C) 2015-2022 OpenCFD Ltd.
9 -------------------------------------------------------------------------------
10 License
11  This file is part of OpenFOAM.
12 
13  OpenFOAM is free software: you can redistribute it and/or modify it
14  under the terms of the GNU General Public License as published by
15  the Free Software Foundation, either version 3 of the License, or
16  (at your option) any later version.
17 
18  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
19  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
20  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21  for more details.
22 
23  You should have received a copy of the GNU General Public License
24  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
25 
26 \*---------------------------------------------------------------------------*/
27 
28 #include "externalFileCoupler.H"
29 #include "Pstream.H"
30 #include "PstreamReduceOps.H"
31 #include "OSspecific.H"
32 #include "Switch.H"
33 #include <fstream>
34 
35 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
36 
37 namespace Foam
38 {
39  defineTypeNameAndDebug(externalFileCoupler, 0);
40 }
41 
43 
44 
45 // * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
46 
47 namespace Foam
48 {
49 
50 // Read file contents and return a stop control as follows:
51 // - contains "done" (should actually be status=done, but we are generous) :
52 // The master (OpenFOAM) has signalled that it is done. Report as <endTime>
53 //
54 // - action=writeNow, action=nextWrite action=noWriteNow :
55 // The slave has signalled that it is done and wants the master to exit with
56 // the specified type of action. Report as corresponding <action>.
57 //
58 // Anything else (empty file, no action=, etc) is reported as <unknown>.
59 //
60 static enum Time::stopAtControls getStopAction(const std::string& filename)
61 {
62  // Slurp entire input file (must exist) as a single string
63  string fileContent;
64 
65  std::ifstream is(filename);
66  std::getline(is, fileContent, '\0');
67 
68  if (fileContent.contains("done"))
69  {
70  return Time::stopAtControls::saEndTime;
71  }
72 
73  const auto equals = fileContent.find('=');
74 
75  if (equals != std::string::npos)
76  {
77  const word actionName(word::validate(fileContent.substr(equals+1)));
78 
79  return
81  (
82  actionName,
83  Time::stopAtControls::saUnknown
84  );
85  }
86 
87  return Time::stopAtControls::saUnknown;
88 }
89 
90 } // End namespace Foam
91 
92 
93 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
94 
96 :
97  runState_(NONE),
98  commsDir_("<case>/comms"),
99  statusDone_("done"),
100  waitInterval_(1u),
101  timeOut_(100u),
102  slaveFirst_(false),
103  log(false)
104 {
105  commsDir_.expand();
106  commsDir_.clean(); // Remove unneeded ".."
107 }
108 
109 
111 :
112  runState_(NONE),
113  commsDir_(commsDir),
114  statusDone_("done"),
115  waitInterval_(1u),
116  timeOut_(100u),
117  slaveFirst_(false),
118  log(false)
119 {
120  commsDir_.expand();
121  commsDir_.clean(); // Remove unneeded ".."
122 
124  {
125  mkDir(commsDir_);
126  }
127 }
128 
129 
131 :
132  externalFileCoupler()
133 {
134  readDict(dict);
135 
136  if (Pstream::master())
137  {
138  mkDir(commsDir_);
139  }
140 }
141 
142 
143 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
144 
146 {
147  shutdown();
148 }
149 
150 
151 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
152 
154 {
155  // Normally cannot change directory or initialization
156  // if things have already been initialized
157  if (!initialized())
158  {
159  dict.readEntry("commsDir", commsDir_);
160  commsDir_.expand();
161  commsDir_.clean(); // Remove unneeded ".."
162  statusDone_ = dict.getOrDefault<word>("statusDone", "done");
163  slaveFirst_ = dict.getOrDefault("initByExternal", false);
164 
165  Info<< type() << ": initialize" << nl
166  << " directory: " << commsDir_ << nl
167  << " slave-first: " << Switch(slaveFirst_) << endl;
168  }
169 
170  waitInterval_ = dict.getOrDefault("waitInterval", 1u);
171  if (!waitInterval_)
172  {
173  // Enforce non-zero sleep
174  waitInterval_ = 1u;
175  }
176 
177  timeOut_ = dict.getOrDefault("timeOut", 100*waitInterval_);
178 
179  log = dict.getOrDefault("log", false);
180 
181  return true;
182 }
183 
184 
186 Foam::externalFileCoupler::useMaster(const bool wait) const
187 {
188  const bool wasInit = initialized();
189  runState_ = MASTER;
190 
191  if (Pstream::master())
192  {
193  if (!wasInit)
194  {
195  // First time
196  mkDir(commsDir_);
197  }
198 
199  const fileName lck(lockFile());
200 
201  // Create lock file - only if it doesn't already exist
202  if (!Foam::isFile(lck))
203  {
204  Log << type()
205  << ": creating lock file with status=openfoam" << endl;
206 
207  std::ofstream os(lck);
208  os << "status=openfoam\n";
209  }
210  }
211 
212  if (wait)
213  {
214  return waitForMaster();
215  }
216 
217  return Time::stopAtControls::saUnknown;
218 }
219 
220 
222 Foam::externalFileCoupler::useSlave(const bool wait) const
223 {
224  const bool wasInit = initialized();
225  runState_ = SLAVE;
226 
227  if (Pstream::master())
228  {
229  if (!wasInit)
230  {
231  // First time
232  mkDir(commsDir_);
233  }
234 
235  Log << type() << ": removing lock file" << endl;
236 
237  Foam::rm(lockFile());
238  }
239 
240  if (wait)
241  {
242  return waitForSlave();
243  }
244 
245  return Time::stopAtControls::saUnknown;
246 }
247 
248 
251 {
252  if (!initialized())
253  {
254  useMaster(); // was not initialized
255  }
256 
257  auto action = Time::stopAtControls::saUnknown;
258 
259  if (Pstream::master())
260  {
261  const fileName lck(lockFile());
262 
263  double prevTime = 0;
264  double modTime = 0;
265 
266  // Wait until file disappears (modTime == 0)
267  // But also check for status=done content in the file
268  while ((modTime = highResLastModified(lck)) > 0)
269  {
270  if (prevTime < modTime)
271  {
272  prevTime = modTime;
273 
274  if (Time::stopAtControls::saEndTime == getStopAction(lck))
275  {
276  // Found 'done' - slave should not wait for master
277  action = Time::stopAtControls::saEndTime;
278  break;
279  }
280  }
281  sleep(waitInterval_);
282  }
283  }
284 
285  // Send to sub-ranks. Also acts as an MPI barrier
286  int intAction(action);
287  Pstream::broadcast(intAction);
288 
289  return Time::stopAtControls(intAction);
290 }
291 
292 
295 {
296  if (!initialized())
297  {
298  useSlave(); // was not initialized
299  }
300 
301  auto action = Time::stopAtControls::saUnknown;
302 
303  if (Pstream::master())
304  {
305  const fileName lck(lockFile());
306  unsigned totalTime = 0;
307 
308  Log << type() << ": waiting for lock file to appear " << lck << endl;
309 
310  while (!Foam::isFile(lck))
311  {
312  sleep(waitInterval_);
313 
314  if (timeOut_ && (totalTime += waitInterval_) > timeOut_)
315  {
317  << "Wait time exceeded timeout of " << timeOut_
318  << " s" << abort(FatalError);
319  }
320 
321  Log << type() << ": wait time = " << totalTime << endl;
322  }
323 
324  action = getStopAction(lck);
325 
326  Log << type() << ": found lock file " << lck << endl;
327  }
328 
329  // Send to sub-ranks. Also acts as an MPI barrier
330  int intAction(action);
331  Pstream::broadcast(intAction);
332 
333  return Time::stopAtControls(intAction);
334 }
335 
336 
338 {}
339 
340 
342 {}
343 
344 
346 {}
347 
348 
350 {}
351 
352 
354 {}
355 
356 
358 {}
359 
360 
362 {
363  if (Pstream::master() && runState_ == MASTER && Foam::isDir(commsDir_))
364  {
365  Log << type() << ": lock file status=" << statusDone_ << endl;
366 
367  std::ofstream os(lockFile());
368  os << "status=" << statusDone_ << nl;
369  }
370 
371  runState_ = DONE; // Avoid re-triggering in destructor
372 }
373 
374 
375 // ************************************************************************* //
static word validate(const std::string &s, const bool prefix=false)
Construct validated word (no invalid characters).
Definition: word.C:39
enum Time::stopAtControls waitForMaster() const
Wait for master to complete.
virtual ~externalFileCoupler()
Destructor.
dictionary dict
enum Time::stopAtControls waitForSlave() const
Wait for slave to complete.
A class for handling file names.
Definition: fileName.H:72
Inter-processor communication reduction functions.
enum Time::stopAtControls useSlave(const bool wait=false) const
Remove lock file to indicate that the external program is in charge.
dimensionedScalar log(const dimensionedScalar &ds)
bool contains(char c) const noexcept
True if string contains given character (cf. C++23)
Definition: string.H:411
error FatalError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL ERROR&#39; header text and sta...
A list of keyword definitions, which are a keyword followed by a number of values (eg...
Definition: dictionary.H:129
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:598
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:50
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:531
A simple wrapper around bool so that it can be read as a word: true/false, on/off, yes/no, any/none. Also accepts 0/1 as a string and shortcuts t/f, y/n.
Definition: Switch.H:77
static const Enum< stopAtControls > stopAtControlNames
Names for stopAtControls.
Definition: Time.H:114
virtual void readDataMaster()
Read data files on master (OpenFOAM).
static void broadcast(Type &value, const label comm=UPstream::worldComm)
Broadcast content (contiguous or non-contiguous) to all communicator ranks. Does nothing in non-paral...
void shutdown() const
Generate status=done in lock (only when run-state = master)
bool isDir(const fileName &name, const bool followLink=true)
Does the name exist as a DIRECTORY in the file system?
Definition: POSIX.C:860
bool readEntry(const word &keyword, T &val, enum keyType::option matchOpt=keyType::REGEX, IOobjectOption::readOption readOpt=IOobjectOption::MUST_READ) const
Find entry and assign to T val. FatalIOError if it is found and the number of tokens is incorrect...
Functions used by OpenFOAM that are specific to POSIX compliant operating systems and need to be repl...
virtual void removeDataMaster() const
Remove data files written by master (OpenFOAM)
fileName::Type type(const fileName &name, const bool followLink=true)
Return the file type: DIRECTORY or FILE, normally following symbolic links.
Definition: POSIX.C:799
bool mkDir(const fileName &pathName, mode_t mode=0777)
Make a directory and return an error if it could not be created.
Definition: POSIX.C:614
A class for handling words, derived from Foam::string.
Definition: word.H:63
enum Time::stopAtControls useMaster(const bool wait=false) const
Create lock file to indicate that OpenFOAM is in charge.
static word lockName
Name of the lock file.
errorManip< error > abort(error &err)
Definition: errorManip.H:139
externalFileCoupler()
Construct using standard defaults.
static enum Time::stopAtControls getStopAction(const std::string &filename)
Definition: abort.C:61
bool readDict(const dictionary &dict)
Read communication settings from dictionary.
unsigned int sleep(const unsigned int sec)
Sleep for the specified number of seconds.
Definition: POSIX.C:1547
OBJstream os(runTime.globalPath()/outputName)
virtual void writeDataMaster() const
Write data files from master (OpenFOAM)
defineTypeNameAndDebug(combustionModel, 0)
static bool clean(std::string &str)
Cleanup filename string, possibly applies other transformations such as changing the path separator e...
Definition: fileName.C:192
bool isFile(const fileName &name, const bool checkGzip=true, const bool followLink=true)
Does the name exist as a FILE in the file system?
Definition: POSIX.C:877
string & expand(const bool allowEmpty=false)
Inplace expand initial tags, tildes, and all occurrences of environment variables as per stringOps::e...
Definition: string.C:166
virtual void readDataSlave()
Read data files on slave (external program).
static bool master(const label communicator=worldComm)
True if process corresponds to the master rank in the communicator.
Definition: UPstream.H:1082
#define Log
Definition: PDRblock.C:28
messageStream Info
Information stream (stdout output on master, null elsewhere)
virtual void removeDataSlave() const
Remove data files written by slave (external program)
T getOrDefault(const word &keyword, const T &deflt, enum keyType::option matchOpt=keyType::REGEX) const
Find and return a T, or return the given default value. FatalIOError if it is found and the number of...
stopAtControls
Stop-run control options, which are primarily used when altering the stopAt condition.
Definition: Time.H:97
Namespace for OpenFOAM.
EnumType lookup(const word &enumName, const EnumType deflt) const
The enumeration corresponding to the given name.
Definition: Enum.C:85
double highResLastModified(const fileName &, const bool followLink=true)
Return time of last file modification.
Definition: POSIX.C:946
bool rm(const fileName &file)
Remove a file (or its gz equivalent), returning true if successful.
Definition: POSIX.C:1404
virtual void writeDataSlave() const
Write data files from slave (external program)