fileOperationRanks.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) 2022-2025 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 "fileOperation.H"
29 #include "stringOps.H"
30 #include "ITstream.H"
31 #include "Pstream.H"
32 #include "SHA1.H"
33 #include "OSspecific.H" // for hostName()
34 #include <cinttypes>
35 
36 // * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
37 
38 namespace Foam
39 {
40 
41 // Parse space, comma, semicolon separated list of integers, floats etc...
42 template<class PrimitiveType>
43 static List<PrimitiveType> splitStringToList(const std::string& str)
44 {
45  const auto items = stringOps::splitAny(str, " ,;");
46 
47  DynamicList<PrimitiveType> values(items.size());
48 
49  for (const auto& item : items)
50  {
51  const std::string s(item.str());
52 
53  PrimitiveType val;
54 
55  if (Foam::read(s, val))
56  {
57  values.push_back(val);
58  }
59  else
60  {
61  // Report errors? Could get noisy...
62  }
63  }
64 
65  return List<PrimitiveType>(std::move(values));
66 }
67 
68 } // End namespace Foam
69 
70 
71 // * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
72 
74 {
75  // Fast path - no IO ranks.
76  if (mainIOranks.empty())
77  {
78  return labelRange();
79  }
80 
81  // The lowest numbered rank is the IO rank
82  // - linear search for the enclosing range
83  // - fallback is proc = 0, which silently adds master (0) into IO ranks
84 
85  label begProc = 0;
86  label endProc = UPstream::nProcs(UPstream::worldComm);
87 
88  const label myProci = UPstream::myProcNo(UPstream::worldComm);
89 
90  forAllReverse(mainIOranks, i)
91  {
92  if (mainIOranks[i] <= myProci)
93  {
94  begProc = mainIOranks[i];
95  if (i+1 < mainIOranks.size())
96  {
97  endProc = mainIOranks[i+1];
98  }
99  break;
100  }
101  }
102 
103  return labelRange(begProc, (endProc-begProc));
104 }
105 
106 
108 {
109  // Very similar to the code in UPstream::setHostCommunicators()
110  // except we need the leader information on *all* ranks!
111 
112  const label myProci = UPstream::myProcNo(UPstream::worldComm);
113  const label numProc = UPstream::nProcs(UPstream::worldComm);
114 
115  // Could also add lowercase etc, but since hostName()
116  // will be consistent within the same node, there is no need.
117  const SHA1Digest myDigest(SHA1(hostName()).digest());
118 
119  List<SHA1Digest> digests(numProc);
120  digests[myProci] = myDigest;
121 
122  // The fixed-length digest allows use of MPI_Allgather.
124  (
125  digests.data_bytes(), // Send/Recv
126  SHA1Digest::size_bytes(), // Num send/recv per rank
128  );
129 
130 
131  DynamicList<label> hostLeaders(UPstream::numNodes());
132 
133  hostLeaders.push_back(0); // Always include master
134  for (label previ = 0, proci = 1; proci < digests.size(); ++proci)
135  {
136  if (digests[previ] != digests[proci])
137  {
138  hostLeaders.push_back(proci);
139  previ = proci;
140  }
141  }
142 
143  return labelList(std::move(hostLeaders));
145  // Alternative is to recover information from commInterNode()
146  // and broadcast via commLocalNode()
147 }
148 
149 
151 (
152  // const bool useHost
153 )
154 {
155  // bool byHostName = useHost;
156  bool byHostName = false;
157 
158  DynamicList<label> dynRanks;
159 
160  Foam::string str(Foam::getEnv("FOAM_IORANKS"));
161 
162  if (!str.empty())
163  {
164  if (str.contains('('))
165  {
166  // Looks like a list - tokenise it
167  ITstream is(str);
168  if (!is.empty())
169  {
170  is >> dynRanks;
171  }
172  }
173  else if (str == "host")
174  {
175  // Select by hostname
176  byHostName = true;
177  }
178  else
179  {
180  // Manual parse
181  dynRanks = splitStringToList<label>(str);
182  }
183  }
184 
185  if (dynRanks.size())
186  {
187  if (!dynRanks.contains(0))
188  {
189  // Could also add silently
190  // dynRanks.push_back(0);
192  << "Rank 0 (master) should be in the IO ranks. Currently:" << nl
193  << " " << flatOutput(dynRanks) << nl
194  << exit(FatalError);
195  }
196 
197  // Never trust user input.
198  // Sort and eliminate any duplicates
199 
200  std::sort(dynRanks.begin(), dynRanks.end());
201 
202  if (dynRanks.front() < 0)
203  {
205  << "Cannot have negative ranks! Currently:" << nl
206  << " " << flatOutput(dynRanks) << nl
207  << exit(FatalError);
208  }
209 
210  labelList ranks;
211 
212  auto last = std::unique(dynRanks.begin(), dynRanks.end());
213 
214  if (last < dynRanks.end())
215  {
216  ranks = dynRanks.slice(0, (last - dynRanks.begin()));
217  }
218  else
219  {
220  ranks = dynRanks;
221  }
222 
223  return ranks;
224  }
225  else if (byHostName)
226  {
228  }
229 
230  return labelList();
231 }
232 
233 
234 bool Foam::fileOperation::isIOrank(const label proci) const
235 {
236  return
237  (
239  ? UPstream::master(comm_)
240  : ioRanks_.empty()
241  ? (proci == 0) // No io-ranks, assume single communicator
242  : ioRanks_.contains(proci) // Found proci in IO rank
243  );
244 }
245 
246 
248 {
249  // Collect the names of the IO masters
251  if (UPstream::master(comm_))
252  {
254  }
256 
257 
258  DynamicList<label> offsetMaster;
259 
260  // Calculate the offsets/counts
261 
263  {
264  label nHostRanks = 0;
265  forAll(hosts, ranki)
266  {
267  if (!hosts[ranki].empty())
268  {
269  ++nHostRanks;
270  }
271  }
272  offsetMaster.reserve(nHostRanks+1);
273 
274  forAll(hosts, ranki)
275  {
276  if (!hosts[ranki].empty())
277  {
278  offsetMaster.push_back(ranki);
279  }
280  }
281 
282  // End of range is nProcs
283  offsetMaster.push_back(hosts.size());
284  }
285 
286  if (offsetMaster.size() > 2)
287  {
288  DetailInfo
289  << "I/O on :" << nl << '(' << nl;
290  for (label group = 1; group < offsetMaster.size(); ++group)
291  {
292  const label beg = offsetMaster[group-1];
293  const label end = offsetMaster[group];
294 
295  DetailInfo
296  << " (" << hosts[beg].c_str() << ' '
297  << (end-beg) << ')' << nl;
298  }
299  DetailInfo
300  << ')' << nl;
301  }
302 }
303 
304 
305 // ************************************************************************* //
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:114
bool contains(char c) const noexcept
True if string contains given character (cf. C++23)
Definition: string.H:411
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:125
error FatalError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL ERROR&#39; header text and sta...
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:600
A 1D array of objects of type <T>, where the size of the vector is known and used for subscript bound...
Definition: BitOps.H:56
static labelList getGlobalHostIORanks()
Get list of global IO master ranks based on the hostname. It is assumed that each host range is conti...
A range or interval of labels defined by a start and a size.
Definition: labelRange.H:52
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:50
bool empty() const noexcept
True if List is empty (ie, size() is zero)
Definition: UList.H:697
T & front()
Access first element of the list, position [0].
Definition: UListI.H:230
static bool & parRun() noexcept
Test if this a parallel run.
Definition: UPstream.H:1586
Foam::SubStrings splitAny(const std::string &str, const std::string &delim, std::string::size_type pos=0)
Split string into sub-strings using any characters in delimiter.
static int & msgType() noexcept
Message tag of standard messages.
Definition: UPstream.H:1787
string getEnv(const std::string &envName)
Get environment value for given envName.
Definition: POSIX.C:341
static int myProcNo(const label communicator=worldComm)
Rank of this process in the communicator (starting from masterNo()). Negative if the process is not a...
Definition: UPstream.H:1611
static label worldComm
Communicator for all ranks. May differ from commGlobal() if local worlds are in use.
Definition: UPstream.H:987
bool contains(const T &val) const
True if the value is contained in the list.
Definition: UListI.H:293
UList< label > labelUList
A UList of labels.
Definition: UList.H:76
static void gatherList(UList< T > &values, const int tag=UPstream::msgType(), const int communicator=UPstream::worldComm)
Gather data, but keep individual values separate.
bool read(const char *buf, int32_t &val)
Same as readInt32.
Definition: int32.H:127
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:286
constexpr const char *const group
Group name for atomic constants.
List< T > values(const HashTable< T, Key, Hash > &tbl, const bool doSort=false)
List of values from HashTable, optionally sorted.
Definition: HashOps.H:164
Functions used by OpenFOAM that are specific to POSIX compliant operating systems and need to be repl...
static label nProcs(const label communicator=worldComm)
Number of ranks in parallel run (for given communicator). It is 1 for serial run. ...
Definition: UPstream.H:1602
A 1D vector of objects of type <T> that resizes itself as necessary to accept the new objects...
Definition: DynamicList.H:51
SubList< T > slice(const label pos, label len=-1)
Return SubList slice (non-const access) - no range checking.
Definition: SubList.H:259
void sort(UList< T > &list)
Sort the list.
Definition: UList.C:295
#define DetailInfo
Definition: evalEntry.C:30
iterator begin() noexcept
Return an iterator to begin traversing the UList.
Definition: UListI.H:385
string hostName()
Return the system&#39;s host name, as per hostname(1)
Definition: POSIX.C:373
static void mpiAllGather(Type *allData, int count, const int communicator=UPstream::worldComm)
Gather/scatter identically-sized data.
static labelRange subRanks(const labelUList &mainIOranks)
Get (contiguous) range/bounds of ranks addressed within the given main io-ranks.
void printRanks() const
Helper: output which ranks are IO.
static bool master(const label communicator=worldComm)
True if process corresponds to the master rank in the communicator.
Definition: UPstream.H:1619
const char * end
Definition: SVGTools.H:223
static int numNodes() noexcept
The number of shared/host nodes in the (const) world communicator.
Definition: UPstream.H:1659
static List< PrimitiveType > splitStringToList(const std::string &str)
static constexpr unsigned size_bytes() noexcept
The number of bytes in digest (20)
Definition: SHA1Digest.H:176
bool isIOrank(const label proci) const
Is proci a master rank in the communicator (in parallel) or a master rank in the IO ranks (non-parall...
#define forAllReverse(list, i)
Reverse loop across all elements in list.
Definition: stdFoam.H:302
static labelList getGlobalIORanks()
Get list of global IO ranks from FOAM_IORANKS env variable. If set, these correspond to the IO master...
iterator end() noexcept
Return an iterator to end traversing the UList.
Definition: UListI.H:429
List< label > labelList
A List of labels.
Definition: List.H:61
gmvFile<< "tracers "<< particles.size()<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().x()<< " ";}gmvFile<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().y()<< " ";}gmvFile<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().z()<< " ";}gmvFile<< nl;forAll(lagrangianScalarNames, i){ word name=lagrangianScalarNames[i];IOField< scalar > s(IOobject(name, runTime.timeName(), cloud::prefix, mesh, IOobject::MUST_READ, IOobject::NO_WRITE))
A class for handling character strings derived from std::string.
Definition: string.H:72
An input stream of tokens.
Definition: ITstream.H:52
Namespace for OpenFOAM.
FlatOutput::OutputAdaptor< Container, Delimiters > flatOutput(const Container &obj, Delimiters delim)
Global flatOutput() function with specified output delimiters.
Definition: FlatOutput.H:225