globalIndex.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) 2011-2016 OpenFOAM Foundation
9  Copyright (C) 2018-2025 OpenCFD Ltd.
10 -------------------------------------------------------------------------------
11 License
12  This file is part of OpenFOAM.
13 
14  OpenFOAM is free software: you can redistribute it and/or modify it
15  under the terms of the GNU General Public License as published by
16  the Free Software Foundation, either version 3 of the License, or
17  (at your option) any later version.
18 
19  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22  for more details.
23 
24  You should have received a copy of the GNU General Public License
25  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26 
27 \*---------------------------------------------------------------------------*/
28 
29 #include "globalIndex.H"
30 #include "Pair.H"
31 
32 // * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
33 
34 void Foam::globalIndex::reportOverflowAndExit
35 (
36  const label idx,
37  const label prevOffset,
38  const label count
39 )
40 {
41  if (idx < 0)
42  {
43  // No overflow tagged
44  return;
45  }
46 
48  << "Overflow : sum of sizes exceeds labelMax ("
49  << labelMax << ") after index " << idx;
50 
51  if (prevOffset >= 0 && count >= 0)
52  {
54  << " while trying to add (" << count
55  << ") to offset (" << prevOffset << ")";
56  }
57 
59  << nl
60  << "Please recompile with larger datatype for label." << nl
61  << exit(FatalError);
62 }
63 
64 
67 (
68  const label localSize,
69  const label comm,
70  const bool checkOverflow
71 )
72 {
73  // Range with 0-offset initially
74  labelRange myRange(0, localSize);
75 
76  if (!UPstream::is_parallel(comm))
77  {
78  return myRange;
79  }
80 
81  const label myProci = UPstream::myProcNo(comm);
82  const labelList counts = UPstream::allGatherValues(localSize, comm);
83 
84  if (checkOverflow)
85  {
86  const label len = counts.size();
87 
88  label start = 0;
89 
90  for (label i = 0; i < len; ++i)
91  {
92  const label count = counts[i];
93 
94  if (i == myProci)
95  {
96  myRange.start() = start;
97  }
98 
99  const label prev = start;
100  start += count;
101 
102  if (start < prev)
103  {
104  reportOverflowAndExit(i, prev, count);
105  }
106  }
107  }
108  else
109  {
110  // std::accumulate
111  // (
112  // counts.cbegin(),
113  // counts.cbegin(myProci),
114  // label(0)
115  // );
116 
117  label start = 0;
118 
119  for (label i = 0; i < myProci; ++i)
120  {
121  start += counts[i];
122  }
123  myRange.start() = start;
124  }
126  return myRange;
127 }
128 
129 
130 Foam::label
132 (
133  const label localSize,
134  const label comm,
135  const bool checkOverflow
136 )
137 {
138  // Placeholder value
139  label myOffset = 0;
140 
141  if (!UPstream::is_parallel(comm))
142  {
143  return myOffset;
144  }
145 
146  const label myProci = UPstream::myProcNo(comm);
147  const labelList counts = UPstream::allGatherValues(localSize, comm);
148 
149  if (checkOverflow)
150  {
151  const label len = counts.size();
152 
153  label start = 0;
154 
155  for (label i = 0; i < len; ++i)
156  {
157  const label count = counts[i];
158  if (i == myProci)
159  {
160  myOffset = start;
161  }
162 
163  const label prev = start;
164  start += count;
165 
166  if (start < prev)
167  {
168  reportOverflowAndExit(i, prev, count);
169  }
170  }
171  }
172  else
173  {
174  // std::accumulate
175  // (
176  // counts.cbegin(),
177  // counts.cbegin(myProci),
178  // label(0)
179  // );
180 
181  label start = 0;
182 
183  for (label i = 0; i < myProci; ++i)
184  {
185  start += counts[i];
186  }
187  myOffset = start;
188  }
190  return myOffset;
191 }
192 
193 
196 (
197  const labelUList& counts,
198  const bool checkOverflow
199 )
200 {
202 
203  const label len = counts.size();
204 
205  if (len)
206  {
207  values.resize(len+1);
208 
209  label start = 0;
210  for (label i = 0; i < len; ++i)
211  {
212  const label count = counts[i];
213  values[i] = start;
214  start += count;
215 
216  if (checkOverflow && start < values[i])
217  {
218  reportOverflowAndExit(i, values[i], count);
219  }
220  }
221  values[len] = start;
222  }
224  return values;
225 }
226 
227 
230 (
231  const labelUList& counts,
232  const bool checkOverflow
233 )
234 {
236 
237  const label len = counts.size();
238 
239  if (len)
240  {
241  values.resize(len);
242 
243  label start = 0;
244  for (label i = 0; i < len; ++i)
245  {
246  const label count = counts[i];
247  values[i].reset(start, count);
248  start += count;
249 
250  if
251  (
252  checkOverflow
253  && (start < values[i].start())
254  && (i < len-1) // Do not check the one beyond the end range
255  )
256  {
257  reportOverflowAndExit(i, values[i].start(), count);
258  }
259  }
260  }
261 
262  return values;
263 }
264 
265 
267 (
268  labelList& interNodeOffsets,
269  labelList& localNodeOffsets,
270  const label communicator,
271  const bool absoluteLocalNodeOffsets
272 ) const
273 {
274  // Require const-world as the starting point
275  if (!UPstream::parRun() || communicator != UPstream::commConstWorld())
276  {
277  interNodeOffsets.clear();
278  localNodeOffsets.clear();
279  return false;
280  }
281 
282  const auto interNodeComm = UPstream::commInterNode();
283 
284  // Only generate information on the node leaders
285  if (!UPstream::is_rank(interNodeComm))
286  {
287  interNodeOffsets.clear();
288  localNodeOffsets.clear();
289  return true; // Not involved, but return true to match others...
290  }
291 
292  const label numProc = UPstream::nProcs(UPstream::commConstWorld());
293  const auto& procIds = UPstream::procID(interNodeComm);
294  const int ranki = UPstream::myProcNo(interNodeComm);
295 
296  if (FOAM_UNLIKELY(procIds.empty()))
297  {
298  // Should not happen...
299  interNodeOffsets.clear();
300  localNodeOffsets.clear();
301  return true; // Return true to match others...
302  }
303 
304  // The inter-node offsets from the node-specific segment of the
305  // overall offsets, but avoiding MPI_Scatterv (slow, doesn't
306  // handle overlaps) and using MPI_Bcast() instead.
307 
308  // Send top-level offsets to the node leaders.
309  // Could also be a mutable operation and use offsets_ directly.
310  //
311  // - number of overall offsets is always (nProc+1) [worldComm]
312  labelList allOffsets;
313  if (UPstream::master(interNodeComm))
314  {
315  allOffsets = offsets_;
316  }
317  else // ie, UPstream::is_subrank(interNodeComm)
318  {
319  allOffsets.resize_nocopy(numProc+1);
320  }
321 
322  UPstream::broadcast(allOffsets.data(), allOffsets.size(), interNodeComm);
323 
324 
325  if (FOAM_UNLIKELY(allOffsets.empty()))
326  {
327  // Should not happen...
328  interNodeOffsets.clear();
329  localNodeOffsets.clear();
330  return true; // Return true to match others...
331  }
332 
333  // The local node span
334  const label firstProc = procIds[ranki];
335  const label lastProc =
336  (
337  (ranki+1 < procIds.size())
338  ? procIds[ranki+1]
339  : numProc
340  );
341 
342  // Offsets (within a node)
343  localNodeOffsets = allOffsets.slice
344  (
345  firstProc,
346  (lastProc - firstProc) + 1 // +1 since offsets
347  );
348 
349  if (!absoluteLocalNodeOffsets && !localNodeOffsets.empty())
350  {
351  const auto start0 = localNodeOffsets.front();
352  for (auto& val : localNodeOffsets)
353  {
354  val -= start0;
355  }
356  }
357 
358  // Offsets (between nodes)
359  interNodeOffsets.resize_nocopy(procIds.size()+1);
360  {
361  forAll(procIds, i)
362  {
363  interNodeOffsets[i] = allOffsets[procIds[i]];
364  }
365  interNodeOffsets.back() = allOffsets.back();
366  }
368  return true;
369 }
370 
371 
372 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
373 
375 {
376  is >> offsets_;
377 }
378 
379 
380 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
381 
383 Foam::globalIndex::bin
384 (
385  const labelUList& offsets,
386  const labelUList& globalIds,
387  labelList& order,
388  DynamicList<label>& validBins
389 )
390 {
391  Foam::sortedOrder(globalIds, order);
392  validBins.clear();
393 
394  CompactListList<label> bins;
395 
396  if (!globalIds.empty())
397  {
398  labelList& binOffsets = bins.offsets();
399  binOffsets.resize(offsets.size(), Foam::zero{});
400 
401  labelList& binValues = bins.values();
402  binValues = UIndirectList<label>(globalIds, order);
403 
404  const label id = binValues[0];
405  label proci = findLower(offsets, id+1);
406 
407  validBins.push_back(proci);
408  label binSize = 1;
409 
410  for (label i = 1; i < order.size(); i++)
411  {
412  const label id = binValues[i];
413 
414  if (id < offsets[proci+1])
415  {
416  ++binSize;
417  }
418  else
419  {
420  // Not local. Reset proci
421  const label oldProci = proci;
422  proci = findLower(offsets, id+1);
423 
424  // Set offsets
425  for (label j = oldProci+1; j < proci; ++j)
426  {
427  binOffsets[j] = binOffsets[oldProci]+binSize;
428  }
429  binOffsets[proci] = i;
430  validBins.push_back(proci);
431  binSize = 1;
432  }
433  }
434 
435  for (label j = proci+1; j < binOffsets.size(); ++j)
436  {
437  binOffsets[j] = binOffsets[proci]+binSize;
438  }
439  }
440 
441  return bins;
442 }
443 
444 
445 void Foam::globalIndex::resize(const label n)
446 {
447  if (n < 1)
448  {
449  offsets_.clear();
450  }
451  else
452  {
453  offsets_.resize(n+1, end_value());
454  }
455 }
456 
457 
459 (
460  const label localSize,
461  const label comm,
462  const bool parallel
463 )
464 {
465  const label len = UPstream::nProcs(comm);
466 
467  if (len)
468  {
469  labelList counts;
470 
471  if (parallel && UPstream::parRun()) // or UPstream::is_parallel(comm)
472  {
473  counts = UPstream::allGatherValues(localSize, comm);
474  }
475  else
476  {
477  // Non-parallel branch: use localSize on-proc, zero elsewhere
478  // TBD: check for (proci >= 0) ?
479  const auto proci = UPstream::myProcNo(comm);
480 
481  counts.resize(len, Foam::zero{});
482  counts[proci] = localSize;
483  }
484 
485  reset(counts, true); // checkOverflow = true
486  }
487  else
488  {
489  // Nothing to do
490  offsets_.clear();
491  }
492 }
493 
494 
496 (
497  const labelUList& counts,
498  const bool checkOverflow
499 )
500 {
501  const label len = counts.size();
502 
503  if (len)
504  {
505  offsets_.resize_nocopy(len+1);
506 
507  label start = 0;
508  for (label i = 0; i < len; ++i)
509  {
510  const label count = counts[i];
511  offsets_[i] = start;
512  start += count;
513 
514  if (checkOverflow && start < offsets_[i])
515  {
516  reportOverflowAndExit(i, offsets_[i], count);
517  }
518  }
519  offsets_[len] = start;
520  }
521  else
522  {
523  offsets_.clear();
524  }
525 }
526 
527 
528 void Foam::globalIndex::setLocalSize(const label proci, const label len)
529 {
530  if (proci >= 0 && proci+1 < offsets_.size() && len >= 0)
531  {
532  const label delta = (len - (offsets_[proci+1] - offsets_[proci]));
533 
534  // TBD: additional overflow check
535  if (delta)
536  {
537  for (label i = proci+1; i < offsets_.size(); ++i)
538  {
539  offsets_[i] += delta;
540  }
541  }
542  }
543 }
544 
545 
547 {
549 
550  const label len = (offsets_.size() - 1);
551 
552  if (len < 1)
553  {
554  return values;
555  }
556 
557  values.resize(len);
558 
559  for (label proci=0; proci < len; ++proci)
560  {
561  values[proci] = offsets_[proci+1] - offsets_[proci];
562  }
563 
564  return values;
565 }
566 
567 
570 {
571  List<labelRange> values;
572 
573  const label len = (offsets_.size() - 1);
574 
575  if (len < 1)
576  {
577  return values;
578  }
579 
580  values.resize(len);
581 
582  for (label proci=0; proci < len; ++proci)
583  {
584  values[proci].reset
585  (
586  offsets_[proci],
587  (offsets_[proci+1] - offsets_[proci])
588  );
589  }
590 
591  return values;
592 }
593 
594 
595 Foam::label Foam::globalIndex::maxNonLocalSize(const label proci) const
596 {
597  const label len = (offsets_.size() - 1);
598 
599  if (len < 1)
600  {
601  return 0;
602  }
603 
604  label maxLen = 0;
605 
606  for (label i=0; i < len; ++i)
607  {
608  if (i != proci)
609  {
610  const label count = (offsets_[i+1] - offsets_[i]);
611  maxLen = max(maxLen, count);
612  }
613  }
614 
615  return maxLen;
616 }
617 
618 
620 {
621  return
622  (
623  (offsets_.size() < 2)
624  ? labelRange()
625  : labelRange(Pair<label>(offsets_[0], offsets_[1]))
626  );
627 }
628 
629 
631 {
632  return
633  (
634  (offsets_.size() < 2)
635  ? labelRange()
636  : labelRange
637  (
639  (
640  offsets_[offsets_.size()-2],
641  offsets_[offsets_.size()-1]
642  )
643  )
644  );
645 }
646 
647 
648 // * * * * * * * * * * * * * * * IOstream Operators * * * * * * * * * * * * //
651 {
652  return is >> gi.offsets();
653 }
654 
655 
656 Foam::Ostream& Foam::operator<<(Ostream& os, const globalIndex& gi)
657 {
658  return os << gi.offsets();
659 }
660 
661 
662 // ************************************************************************* //
scalar delta
static labelList calcOffsets(const labelUList &counts, const bool checkOverflow=false)
Calculate offsets from a list of local sizes, with optional check for label overflow.
Definition: globalIndex.C:189
void reset(const label localSize, const label comm=UPstream::worldComm, const bool parallel=UPstream::parRun())
Reset from local size, using gather/broadcast with default/specified communicator if parallel...
Definition: globalIndex.C:452
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:114
label findLower(const ListType &input, const T &val, const label start, const ComparePredicate &comp)
Binary search to find the index of the last element in a sorted list that is less than value...
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:125
void resize(const label len)
Adjust allocated size of list.
Definition: ListI.H:150
error FatalError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL ERROR&#39; header text and sta...
List< labelRange > ranges() const
Return start/size ranges for all data.
Definition: globalIndex.C:562
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:600
labelList sortedOrder(const UList< T > &input)
Return the (stable) sort order for the list.
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:40
A range or interval of labels defined by a start and a size.
Definition: labelRange.H:52
An Istream is an abstract base class for all input systems (streams, files, token lists etc)...
Definition: Istream.H:57
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:50
static List< labelRange > calcRanges(const labelUList &counts, const bool checkOverflow=false)
Calculate ranges (offset/size) from a list of local sizes, with optional check for label overflow...
Definition: globalIndex.C:223
static bool & parRun() noexcept
Test if this a parallel run.
Definition: UPstream.H:1586
static List< T > allGatherValues(const T &localValue, const int communicator=UPstream::worldComm)
Allgather individual values into list locations.
void resize_nocopy(const label len)
Adjust allocated size of list without necessarily.
Definition: ListI.H:168
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
labelList localSizes() const
The local sizes.
Definition: globalIndex.C:539
UList< label > labelUList
A UList of labels.
Definition: UList.H:76
globalIndex() noexcept=default
Default construct (empty)
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:286
const labelList & offsets() const noexcept
Const-access to the offsets.
Definition: globalIndexI.H:205
List< T > values(const HashTable< T, Key, Hash > &tbl, const bool doSort=false)
List of values from HashTable, optionally sorted.
Definition: HashOps.H:164
unsigned int count(const UList< bool > &bools, const bool val=true)
Count number of &#39;true&#39; entries.
Definition: BitOps.H:73
Calculates a unique integer (label so might not have enough room - 2G max) for processor + local inde...
Definition: globalIndex.H:61
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
static int commInterNode() noexcept
Communicator between nodes/hosts (respects any local worlds)
Definition: UPstream.H:1063
static bool is_rank(const label communicator=worldComm)
True if process corresponds to any rank (master or sub-rank) in the given communicator.
Definition: UPstream.H:1628
Istream & operator>>(Istream &, directionInfo &)
static int commConstWorld() noexcept
Communicator for all ranks (respecting any local worlds).
Definition: UPstream.H:1014
void setLocalSize(const label proci, const label len)
Alter local size for given processor.
Definition: globalIndex.C:521
#define FOAM_UNLIKELY(cond)
Definition: stdFoam.H:64
static bool is_parallel(const label communicator=worldComm)
True if parallel algorithm or exchange is required.
Definition: UPstream.H:1648
A packed storage of objects of type <T> using an offset table for access.
An Ostream is an abstract base class for all output systems (streams, files, token lists...
Definition: Ostream.H:56
OBJstream os(runTime.globalPath()/outputName)
bool splitNodeOffsets(labelList &interNodeOffsets, labelList &localNodeOffsets, const label communicator=UPstream::worldComm, const bool absoluteLocalNodeOffsets=false) const
Split the top-level offsets into inter-node and local-node components suitable to a two-stage hierarc...
Definition: globalIndex.C:260
Ostream & operator<<(Ostream &, const boundaryPatch &p)
Write boundaryPatch as dictionary entries (without surrounding braces)
Definition: boundaryPatch.C:77
limits reset(1/(limits.max()+VSMALL), 1/(limits.min()+VSMALL))
static bool master(const label communicator=worldComm)
True if process corresponds to the master rank in the communicator.
Definition: UPstream.H:1619
constexpr label labelMax
Definition: label.H:55
static List< int > & procID(int communicator)
The list of ranks within a given communicator.
Definition: UPstream.H:1672
A class representing the concept of 0 (zero) that can be used to avoid manipulating objects known to ...
Definition: zero.H:57
static labelRange calcRange(const label localSize, const label comm=UPstream::worldComm, const bool checkOverflow=false)
Calculate globally-consistent local range (offset/size) based on the local input size(s).
Definition: globalIndex.C:60
label n
List< label > labelList
A List of labels.
Definition: List.H:61
labelRange back() const
The last offset range. It is (0,0) if globalIndex is empty.
Definition: globalIndex.C:623
static label calcOffset(const label localSize, const label comm=UPstream::worldComm, const bool checkOverflow=false)
Calculate globally-consistent local start offset based on the local input size(s).
Definition: globalIndex.C:125
static bool broadcast(Type *buffer, std::streamsize count, const int communicator)
Broadcast buffer contents (contiguous types), from rank=0 to all ranks. The sizes must match on all p...
label maxNonLocalSize() const
The max of localSizes, excluding current (myProcNo) rank.
Definition: globalIndexI.H:276
void resize(const label n)
Change the number of entries (nProcs) in the offsets table. Extending will fill with empty local size...
Definition: globalIndex.C:438
labelRange front() const
The first offset range. It is (0,0) if globalIndex is empty.
Definition: globalIndex.C:612