mapDistributeBaseTemplates.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-2017 OpenFOAM Foundation
9  Copyright (C) 2015-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 "Pstream.H"
30 #include "PstreamBuffers.H"
31 #include "flipOp.H"
32 
33 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
34 
35 template<class T, class CombineOp, class NegateOp>
37 (
38  UList<T>& lhs,
39  const UList<T>& rhs,
40 
41  const labelUList& map,
42  const bool hasFlip,
43  const CombineOp& cop,
44  const NegateOp& negOp
45 )
46 {
47  const label len = map.size();
48 
49  // FULLDEBUG: if (lhs.size() < max(map)) FatalError ...;
50  // FULLDEBUG: if (rhs.size() < len) FatalError ...;
51 
52  if (hasFlip)
53  {
54  for (label i = 0; i < len; ++i)
55  {
56  const label index = map[i];
57 
58  if (index > 0)
59  {
60  cop(lhs[index-1], rhs[i]);
61  }
62  else if (index < 0)
63  {
64  cop(lhs[-index-1], negOp(rhs[i]));
65  }
66  else
67  {
69  << "Illegal flip index '0' at " << i << '/' << map.size()
70  << " for list:" << rhs.size() << nl
71  << exit(FatalError);
72  }
73  }
74  }
75  else
76  {
77  for (label i = 0; i < len; ++i)
78  {
79  cop(lhs[map[i]], rhs[i]);
80  }
81  }
82 }
83 
84 
85 template<class T, class NegateOp>
87 (
88  UList<T>& output,
89  const UList<T>& values,
90  const labelUList& map,
91  const bool hasFlip,
92  const NegateOp& negOp
93 )
94 {
95  const label len = map.size();
96 
97  // FULLDEBUG: if (values.size() < max(map)) FatalError ...;
98  // FULLDEBUG: if (output.size() < len) FatalError ...;
99 
100  if (hasFlip)
101  {
102  for (label i = 0; i < len; ++i)
103  {
104  const label index = map[i];
105 
106  if (index > 0)
107  {
108  output[i] = values[index-1];
109  }
110  else if (index < 0)
111  {
112  output[i] = negOp(values[-index-1]);
113  }
114  else
115  {
117  << "Illegal flip index '0' at " << i << '/' << map.size()
118  << " for list:" << values.size() << nl
119  << exit(FatalError);
120  }
121  }
122  }
123  else
124  {
125  // Like indirect list
126  for (label i = 0; i < len; ++i)
127  {
128  output[i] = values[map[i]];
129  }
130  }
131 }
132 
133 
134 template<class T, class NegateOp>
136 (
137  const UList<T>& values,
138  const labelUList& map,
139  const bool hasFlip,
140  const NegateOp& negOp
141 )
142 {
143  List<T> output(map.size());
144  accessAndFlip(output, values, map, hasFlip, negOp);
145  return output;
146 }
147 
148 
149 template<class T, class negateOp>
151 (
152  const labelListList& subMap,
153  const bool subHasFlip,
154  const labelListList& constructMap,
155  const bool constructHasFlip,
156  const UList<T>& field,
157 
158  labelRange& sendRequests,
159  PtrList<List<T>>& sendFields,
160 
161  labelRange& recvRequests,
162  PtrList<List<T>>& recvFields,
163 
164  const negateOp& negOp,
165  const int tag,
166  const label comm
167 )
168 {
169  if constexpr (!is_contiguous_v<T>)
170  {
172  << "Only contiguous is currently supported"
174  }
175 
176  const auto myRank = UPstream::myProcNo(comm);
177  const auto nProcs = UPstream::nProcs(comm);
178 
179 
180  // Set up receives from neighbours
181  recvRequests.start() = UPstream::nRequests();
182  recvRequests.size() = 0;
183 
184  recvFields.resize(nProcs);
185 
186  for (const int proci : UPstream::allProcs(comm))
187  {
188  const labelList& map = constructMap[proci];
189 
190  if (proci == myRank)
191  {
192  // No communication for myself - but recvFields may be used
193  }
194  else if (map.empty())
195  {
196  // No receive necessary
197  (void) recvFields.release(proci);
198  }
199  else
200  {
201  List<T>& subField = recvFields.try_emplace(proci);
202  subField.resize_nocopy(map.size());
203 
205  (
207  proci,
208  subField,
209  tag,
210  comm
211  );
212  }
213  }
214 
215  // Finished setting up the receives
216  recvRequests.size() = (UPstream::nRequests() - recvRequests.start());
217 
218 
219  // Set up sends to neighbours
220  sendRequests.start() = UPstream::nRequests();
221  sendRequests.size() = 0;
222 
223  sendFields.resize(nProcs);
224 
225  for (const int proci : UPstream::allProcs(comm))
226  {
227  const labelList& map = subMap[proci];
228 
229  if (proci == myRank)
230  {
231  // No communication - sendFields not needed
232  (void) sendFields.release(proci);
233  }
234  else if (map.empty())
235  {
236  // No send necessary
237  (void) sendFields.release(proci);
238  }
239  else
240  {
241  List<T>& subField = sendFields.try_emplace(proci);
242  subField.resize_nocopy(map.size());
243 
244  accessAndFlip(subField, field, map, subHasFlip, negOp);
245 
247  (
249  proci,
250  subField,
251  tag,
252  comm
253  );
254  }
255  }
256 
257  // Finished setting up the sends
258  sendRequests.size() = (UPstream::nRequests() - sendRequests.start());
259 
260 
261  // Set up 'send' to myself - copy directly into recvFields
262  {
263  const labelList& map = subMap[myRank];
264 
265  if (map.empty())
266  {
267  // Nothing to send/recv
268  (void) recvFields.release(myRank);
269  }
270  else
271  {
272  List<T>& subField = recvFields.try_emplace(myRank);
273  subField.resize_nocopy(map.size());
274 
275  accessAndFlip(subField, field, map, subHasFlip, negOp);
276  }
277  }
278 }
279 
280 
281 template<class T>
283 (
284  const UList<T>& field,
285  labelRange& sendRequests,
286  PtrList<List<T>>& sendFields,
287  labelRange& recvRequests,
288  PtrList<List<T>>& recvFields,
289  const int tag
290 ) const
291 {
292  send
293  (
294  subMap_,
295  subHasFlip_,
296  constructMap_,
297  constructHasFlip_,
298  field,
299  sendRequests, sendFields,
300  recvRequests, recvFields,
301  flipOp(),
302  tag,
303  comm_
304  );
305 }
306 
307 
308 template<class T, class CombineOp, class negateOp>
310 (
311  const label constructSize,
312  const labelListList& constructMap,
313  const bool constructHasFlip,
314  const labelRange& requests,
315  const UPtrList<List<T>>& recvFields,
316  List<T>& field,
317  const CombineOp& cop,
318  const negateOp& negOp,
319  const int tag,
320  const label comm
321 )
322 {
323  if constexpr (!is_contiguous_v<T>)
324  {
326  << "Only contiguous is currently supported"
328  }
329 
330  const auto myRank = UPstream::myProcNo(comm);
331  [[maybe_unused]]
332  const auto nProcs = UPstream::nProcs(comm);
333 
334 
335  // Receiving from which procs - according to map information
336 
337  DynamicList<int> recvProcs(nProcs);
338  for (const int proci : UPstream::allProcs(comm))
339  {
340  const labelList& map = constructMap[proci];
341 
342  if (proci != myRank && map.size())
343  {
344  recvProcs.push_back(proci);
345 
346  const auto* subFieldPtr = recvFields.get(proci);
347  if (subFieldPtr)
348  {
349  checkReceivedSize(proci, map.size(), subFieldPtr->size());
350  }
351  else
352  {
354  << "From processor " << proci
355  << " : unallocated receive field."
356  << " Expected size " << map.size()
357  << " on comm " << comm
358  << " with procs " << UPstream::nProcs(comm) << nl
359  << exit(FatalError);
360  }
361  }
362  }
363 
364 
365  // Combining bits - can reuse field storage
366  field.resize_nocopy(constructSize);
367 
368 
369  // Received sub field from myself : recvFields[myRank]
370  if (recvFields.test(myRank))
371  {
372  const labelList& map = constructMap[myRank];
373  const List<T>& subField = recvFields[myRank];
374 
375  // Unlikely to need a size check
376  // checkReceivedSize(myRank, map.size(), subField.size());
377 
378  flipAndCombine
379  (
380  field,
381  subField,
382  map,
383  constructHasFlip,
384  cop,
385  negOp
386  );
387  }
388 
389 
390  // NB: do NOT use polling and dispatch here.
391  // There is no certainty if the listed requests are still pending or
392  // have already been waited on before calling this method.
393 
394  // Wait for (receive) requests, but the range may also include
395  // other requests depending on what the caller provided
396 
397  UPstream::waitRequests(requests.start(), requests.size());
398 
399 
400  // Process received fields
401 
402  {
403  for (const int proci : recvProcs)
404  {
405  const labelList& map = constructMap[proci];
406  const List<T>& subField = recvFields[proci];
407 
408  // Already checked the sizes previously
409  // checkReceivedSize(proci, map.size(), subField.size());
410 
411  flipAndCombine
412  (
413  field,
414  subField,
415  map,
416  constructHasFlip,
417  cop,
418  negOp
419  );
420  }
421  }
422 }
423 
424 
425 template<class T>
427 (
428  const labelRange& requests,
429  const UPtrList<List<T>>& recvFields,
430  List<T>& field,
431  const int tag
432 ) const
433 {
434  receive
435  (
436  constructSize_,
437  constructMap_,
438  constructHasFlip_,
439  requests,
440  recvFields,
441  field,
442  eqOp<T>(),
443  flipOp(),
444  tag,
445  comm_
446  );
447 }
448 
449 
450 template<class T, class CombineOp, class NegateOp>
452 (
453  const UPstream::commsTypes commsType,
454  const UList<labelPair>& schedule,
455  const label constructSize,
456  const labelListList& subMap,
457  const bool subHasFlip,
458  const labelListList& constructMap,
459  const bool constructHasFlip,
460  List<T>& field,
461  const T& nullValue,
462  const CombineOp& cop,
463  const NegateOp& negOp,
464  const int tag,
465  const label comm
466 )
467 {
468  const auto myRank = UPstream::myProcNo(comm);
469  [[maybe_unused]]
470  const auto nProcs = UPstream::nProcs(comm);
471 
472  if (!UPstream::parRun())
473  {
474  // Do only me to me.
475 
476  List<T> subField
477  (
478  accessAndFlip(field, subMap[myRank], subHasFlip, negOp)
479  );
480 
481  // Receive sub field from myself (subField)
482  const labelList& map = constructMap[myRank];
483 
484  // Combining bits - can now reuse field storage
485  field.resize_nocopy(constructSize);
486  field = nullValue;
487 
488  flipAndCombine
489  (
490  field,
491  subField,
492  map,
493  constructHasFlip,
494  cop,
495  negOp
496  );
497 
498  return;
499  }
500 
501  if (commsType == UPstream::commsTypes::buffered)
502  {
503  // Since buffered sending can reuse the field to collect the
504  // received data.
505 
506  // Send sub field to neighbour
507  for (const int proci : UPstream::allProcs(comm))
508  {
509  const labelList& map = subMap[proci];
510 
511  if (proci != myRank && map.size())
512  {
513  List<T> subField
514  (
515  accessAndFlip(field, map, subHasFlip, negOp)
516  );
517 
518  // buffered send
519  OPstream os(commsType, proci, 0, tag, comm);
520  os << subField;
521  }
522  }
523 
524  {
525  // Subset myself
526  List<T> subField
527  (
528  accessAndFlip(field, subMap[myRank], subHasFlip, negOp)
529  );
530 
531  // Receive sub field from myself (subField)
532  const labelList& map = constructMap[myRank];
533 
534  // Combining bits - can now reuse field storage
535  field.resize_nocopy(constructSize);
536  field = nullValue;
537 
538  flipAndCombine
539  (
540  field,
541  subField,
542  map,
543  constructHasFlip,
544  cop,
545  negOp
546  );
547  }
548 
549  // Receive and process sub-field from neighbours
550  for (const int proci : UPstream::allProcs(comm))
551  {
552  const labelList& map = constructMap[proci];
553 
554  if (proci != myRank && map.size())
555  {
556  List<T> subField;
557  IPstream::recv(subField, proci, tag, comm);
558 
559  checkReceivedSize(proci, map.size(), subField.size());
560 
561  flipAndCombine
562  (
563  field,
564  subField,
565  map,
566  constructHasFlip,
567  cop,
568  negOp
569  );
570  }
571  }
572  }
573  else if (commsType == UPstream::commsTypes::scheduled)
574  {
575  // Need to make sure I don't overwrite field with received data
576  // since the data might need to be sent to another processor. So
577  // allocate a new field for the results.
578  List<T> newField;
579  newField.resize_nocopy(constructSize);
580  newField = nullValue;
581 
582  // First handle self
583  {
584  // Subset myself
585  List<T> subField
586  (
587  accessAndFlip(field, subMap[myRank], subHasFlip, negOp)
588  );
589 
590  // Receive sub field from myself (subField)
591  const labelList& map = constructMap[myRank];
592 
593  flipAndCombine
594  (
595  newField,
596  subField,
597  map,
598  constructHasFlip,
599  cop,
600  negOp
601  );
602  }
603 
604 
605  // Schedule will already have pruned 0-sized comms
606  for (const labelPair& twoProcs : schedule)
607  {
608  // twoProcs is a swap pair of processors. The first one is the
609  // one that needs to send first and then receive.
610 
611  if (twoProcs.first() == myRank)
612  {
613  // I am send first, receive next
614  const label nbrProc = twoProcs.second();
615 
616  {
617  const labelList& map = subMap[nbrProc];
618 
619  List<T> subField
620  (
621  accessAndFlip(field, map, subHasFlip, negOp)
622  );
623 
624  OPstream::send(subField, nbrProc, tag, comm);
625  }
626  {
627  List<T> subField;
628  IPstream::recv(subField, nbrProc, tag, comm);
629 
630  const labelList& map = constructMap[nbrProc];
631 
632  checkReceivedSize(nbrProc, map.size(), subField.size());
633 
634  flipAndCombine
635  (
636  newField,
637  subField,
638  map,
639  constructHasFlip,
640  cop,
641  negOp
642  );
643  }
644  }
645  else
646  {
647  // I am receive first, send next
648  const label nbrProc = twoProcs.first();
649 
650  {
651  List<T> subField;
652  IPstream::recv(subField, nbrProc, tag, comm);
653 
654  const labelList& map = constructMap[nbrProc];
655 
656  checkReceivedSize(nbrProc, map.size(), subField.size());
657 
658  flipAndCombine
659  (
660  newField,
661  subField,
662  map,
663  constructHasFlip,
664  cop,
665  negOp
666  );
667  }
668  {
669  const labelList& map = subMap[nbrProc];
670 
671  List<T> subField
672  (
673  accessAndFlip(field, map, subHasFlip, negOp)
674  );
675 
676  OPstream::send(subField, nbrProc, tag, comm);
677  }
678  }
679  }
680  field.transfer(newField);
681  }
682  else if (commsType == UPstream::commsTypes::nonBlocking)
683  {
684  const label startOfRequests = UPstream::nRequests();
685 
686  if constexpr (!is_contiguous_v<T>)
687  {
688  PstreamBuffers pBufs(comm, tag);
689 
690  // Stream data into buffer
691  for (const int proci : UPstream::allProcs(comm))
692  {
693  const labelList& map = subMap[proci];
694 
695  if (proci != myRank && map.size())
696  {
697  List<T> subField
698  (
699  accessAndFlip(field, map, subHasFlip, negOp)
700  );
701 
702  UOPstream os(proci, pBufs);
703  os << subField;
704  }
705  }
706 
707  // Initiate receiving - do yet not block
708  pBufs.finishedSends(false);
709 
710  {
711  // Set up 'send' to myself
712  List<T> subField
713  (
714  accessAndFlip(field, subMap[myRank], subHasFlip, negOp)
715  );
716 
717  // Combining bits - can now reuse field storage
718  field.resize_nocopy(constructSize);
719  field = nullValue;
720 
721  // Receive sub field from myself
722  const labelList& map = constructMap[myRank];
723 
724  flipAndCombine
725  (
726  field,
727  subField,
728  map,
729  constructHasFlip,
730  cop,
731  negOp
732  );
733  }
734 
735  // Wait for receive requests (and the send requests too)
736  UPstream::waitRequests(startOfRequests);
737 
738  // Receive and process neighbour fields
739  for (const int proci : UPstream::allProcs(comm))
740  {
741  const labelList& map = constructMap[proci];
742 
743  if (proci != myRank && map.size())
744  {
745  UIPstream is(proci, pBufs);
746  List<T> subField(is);
747 
748  checkReceivedSize(proci, map.size(), subField.size());
749 
750  flipAndCombine
751  (
752  field,
753  subField,
754  map,
755  constructHasFlip,
756  cop,
757  negOp
758  );
759  }
760  }
761  }
762  else
763  {
764  // Set up receives from neighbours
765 
766  List<List<T>> recvFields(nProcs);
767  DynamicList<int> recvProcs(nProcs);
768 
769  for (const int proci : UPstream::allProcs(comm))
770  {
771  const labelList& map = constructMap[proci];
772 
773  if (proci != myRank && map.size())
774  {
775  recvProcs.push_back(proci);
776  List<T>& subField = recvFields[proci];
777  subField.resize_nocopy(map.size());
778 
780  (
782  proci,
783  subField,
784  tag,
785  comm
786  );
787  }
788  }
789 
790 
791  // Set up sends to neighbours
792 
793  List<List<T>> sendFields(nProcs);
794 
795  for (const int proci : UPstream::allProcs(comm))
796  {
797  const labelList& map = subMap[proci];
798 
799  if (proci != myRank && map.size())
800  {
801  List<T>& subField = sendFields[proci];
802  subField.resize_nocopy(map.size());
803 
804  accessAndFlip(subField, field, map, subHasFlip, negOp);
805 
807  (
809  proci,
810  subField,
811  tag,
812  comm
813  );
814  }
815  }
816 
817  // Set up 'send' to myself - copy directly into recvFields
818  {
819  const labelList& map = subMap[myRank];
820  List<T>& subField = recvFields[myRank];
821  subField.resize_nocopy(map.size());
822 
823  accessAndFlip(subField, field, map, subHasFlip, negOp);
824  }
825 
826 
827  // Combining bits - can now reuse field storage
828  field.resize_nocopy(constructSize);
829  field = nullValue;
830 
831  // Receive sub field from myself : recvFields[myRank]
832  {
833  const labelList& map = constructMap[myRank];
834  const List<T>& subField = recvFields[myRank];
835 
836  // Probably don't need a size check
837  // checkReceivedSize(myRank, map.size(), subField.size());
838 
839  flipAndCombine
840  (
841  field,
842  subField,
843  map,
844  constructHasFlip,
845  cop,
846  negOp
847  );
848  }
849 
850 
851  // Poll for completed receive requests and dispatch
852  DynamicList<int> indices(recvProcs.size());
853  while
854  (
856  (
857  startOfRequests,
858  recvProcs.size(),
859  &indices
860  )
861  )
862  {
863  for (const int idx : indices)
864  {
865  const int proci = recvProcs[idx];
866  const labelList& map = constructMap[proci];
867  const List<T>& subField = recvFields[proci];
868 
869  // No size check - was dimensioned above
870  // checkReceivedSize(proci, map.size(), subField.size());
871 
872  flipAndCombine
873  (
874  field,
875  subField,
876  map,
877  constructHasFlip,
878  cop,
879  negOp
880  );
881  }
882  }
883 
884  // Wait for any remaining requests
885  UPstream::waitRequests(startOfRequests);
886  }
887  }
888  else
889  {
891  << "Unknown communication schedule " << int(commsType)
893  }
894 }
895 
896 
897 template<class T, class NegateOp>
899 (
900  const UPstream::commsTypes commsType,
901  const UList<labelPair>& schedule,
902  const label constructSize,
903  const labelListList& subMap,
904  const bool subHasFlip,
905  const labelListList& constructMap,
906  const bool constructHasFlip,
907  List<T>& field,
908  const NegateOp& negOp,
909  const int tag,
910  const label comm
911 )
912 {
913  const auto myRank = UPstream::myProcNo(comm);
914  [[maybe_unused]]
915  const auto nProcs = UPstream::nProcs(comm);
916 
917  if (!UPstream::parRun())
918  {
919  // Do only me to me.
920 
921  List<T> subField
922  (
923  accessAndFlip(field, subMap[myRank], subHasFlip, negOp)
924  );
925 
926  // Receive sub field from myself (subField)
927  const labelList& map = constructMap[myRank];
928 
929  // Combining bits - can now reuse field storage
930  field.resize_nocopy(constructSize);
931 
932  flipAndCombine
933  (
934  field,
935  subField,
936  map,
937  constructHasFlip,
938  eqOp<T>(),
939  negOp
940  );
941 
942  return;
943  }
944 
945  if (commsType == UPstream::commsTypes::buffered)
946  {
947  // Since buffered sending can reuse the field to collect the
948  // received data.
949 
950  // Send sub field to neighbour
951  for (const int proci : UPstream::allProcs(comm))
952  {
953  const labelList& map = subMap[proci];
954 
955  if (proci != myRank && map.size())
956  {
957  List<T> subField
958  (
959  accessAndFlip(field, map, subHasFlip, negOp)
960  );
961 
962  // buffered send
963  OPstream os(commsType, proci, 0, tag, comm);
964  os << subField;
965  }
966  }
967 
968  {
969  // Subset myself
970  List<T> subField
971  (
972  accessAndFlip(field, subMap[myRank], subHasFlip, negOp)
973  );
974 
975  // Receive sub field from myself (subField)
976  const labelList& map = constructMap[myRank];
977 
978  // Combining bits - can now reuse field storage
979  field.resize_nocopy(constructSize);
980 
981  flipAndCombine
982  (
983  field,
984  subField,
985  map,
986  constructHasFlip,
987  eqOp<T>(),
988  negOp
989  );
990  }
991 
992  // Receive and process sub-field from neighbours
993  for (const int proci : UPstream::allProcs(comm))
994  {
995  const labelList& map = constructMap[proci];
996 
997  if (proci != myRank && map.size())
998  {
999  List<T> subField;
1000  IPstream::recv(subField, proci, tag, comm);
1001 
1002  checkReceivedSize(proci, map.size(), subField.size());
1003 
1004  flipAndCombine
1005  (
1006  field,
1007  subField,
1008  map,
1009  constructHasFlip,
1010  eqOp<T>(),
1011  negOp
1012  );
1013  }
1014  }
1015  }
1016  else if (commsType == UPstream::commsTypes::scheduled)
1017  {
1018  // Need to make sure I don't overwrite field with received data
1019  // since the data might need to be sent to another processor. So
1020  // allocate a new field for the results.
1021  List<T> newField;
1022  newField.resize_nocopy(constructSize);
1023 
1024  // First handle self
1025  {
1026  // Subset myself
1027  List<T> subField
1028  (
1029  accessAndFlip(field, subMap[myRank], subHasFlip, negOp)
1030  );
1031 
1032  // Receive sub field from myself (subField)
1033  const labelList& map = constructMap[myRank];
1034 
1035  flipAndCombine
1036  (
1037  newField,
1038  subField,
1039  map,
1040  constructHasFlip,
1041  eqOp<T>(),
1042  negOp
1043  );
1044  }
1045 
1046  // Schedule will already have pruned 0-sized comms
1047  for (const labelPair& twoProcs : schedule)
1048  {
1049  // twoProcs is a swap pair of processors. The first one is the
1050  // one that needs to send first and then receive.
1051 
1052  if (twoProcs.first() == myRank)
1053  {
1054  // I am send first, receive next
1055  const label nbrProc = twoProcs.second();
1056 
1057  {
1058  const labelList& map = subMap[nbrProc];
1059  List<T> subField
1060  (
1061  accessAndFlip(field, map, subHasFlip, negOp)
1062  );
1063 
1064  OPstream::send(subField, nbrProc, tag, comm);
1065  }
1066  {
1067  List<T> subField;
1068  IPstream::recv(subField, nbrProc, tag, comm);
1069 
1070  const labelList& map = constructMap[nbrProc];
1071 
1072  checkReceivedSize(nbrProc, map.size(), subField.size());
1073 
1074  flipAndCombine
1075  (
1076  newField,
1077  subField,
1078  map,
1079  constructHasFlip,
1080  eqOp<T>(),
1081  negOp
1082  );
1083  }
1084  }
1085  else
1086  {
1087  // I am receive first, send next
1088  const label nbrProc = twoProcs.first();
1089 
1090  {
1091  List<T> subField;
1092  IPstream::recv(subField, nbrProc, tag, comm);
1093 
1094  const labelList& map = constructMap[nbrProc];
1095 
1096  checkReceivedSize(nbrProc, map.size(), subField.size());
1097 
1098  flipAndCombine
1099  (
1100  newField,
1101  subField,
1102  map,
1103  constructHasFlip,
1104  eqOp<T>(),
1105  negOp
1106  );
1107  }
1108  {
1109  const labelList& map = subMap[nbrProc];
1110  List<T> subField
1111  (
1112  accessAndFlip(field, map, subHasFlip, negOp)
1113  );
1114 
1115  OPstream::send(subField, nbrProc, tag, comm);
1116  }
1117  }
1118  }
1119  field.transfer(newField);
1120  }
1121  else if (commsType == UPstream::commsTypes::nonBlocking)
1122  {
1123  const label startOfRequests = UPstream::nRequests();
1124 
1125  if constexpr (!is_contiguous_v<T>)
1126  {
1127  PstreamBuffers pBufs(comm, tag);
1128 
1129  // Stream data into buffer
1130  for (const int proci : UPstream::allProcs(comm))
1131  {
1132  const labelList& map = subMap[proci];
1133 
1134  if (proci != myRank && map.size())
1135  {
1136  List<T> subField
1137  (
1138  accessAndFlip(field, map, subHasFlip, negOp)
1139  );
1140 
1141  UOPstream os(proci, pBufs);
1142  os << subField;
1143  }
1144  }
1145 
1146  // Initiate receiving - do yet not block
1147  pBufs.finishedSends(false);
1148 
1149  {
1150  // Set up 'send' to myself
1151  List<T> subField
1152  (
1153  accessAndFlip(field, subMap[myRank], subHasFlip, negOp)
1154  );
1155 
1156  // Combining bits - can now reuse field storage
1157  field.resize_nocopy(constructSize);
1158 
1159  // Receive sub field from myself
1160  const labelList& map = constructMap[myRank];
1161 
1162  flipAndCombine
1163  (
1164  field,
1165  subField,
1166  map,
1167  constructHasFlip,
1168  eqOp<T>(),
1169  negOp
1170  );
1171  }
1172 
1173  // Wait for receive requests (and the send requests too)
1174  UPstream::waitRequests(startOfRequests);
1175 
1176  // Receive and process neighbour fields
1177  for (const int proci : UPstream::allProcs(comm))
1178  {
1179  const labelList& map = constructMap[proci];
1180 
1181  if (proci != myRank && map.size())
1182  {
1183  UIPstream is(proci, pBufs);
1184  List<T> subField(is);
1185 
1186  checkReceivedSize(proci, map.size(), subField.size());
1187 
1188  flipAndCombine
1189  (
1190  field,
1191  subField,
1192  map,
1193  constructHasFlip,
1194  eqOp<T>(),
1195  negOp
1196  );
1197  }
1198  }
1199  }
1200  else
1201  {
1202  // Set up receives from neighbours
1203 
1204  List<List<T>> recvFields(nProcs);
1205  DynamicList<int> recvProcs(nProcs);
1206 
1207  for (const int proci : UPstream::allProcs(comm))
1208  {
1209  const labelList& map = constructMap[proci];
1210 
1211  if (proci != myRank && map.size())
1212  {
1213  recvProcs.push_back(proci);
1214  List<T>& subField = recvFields[proci];
1215  subField.resize_nocopy(map.size());
1216 
1218  (
1220  proci,
1221  subField,
1222  tag,
1223  comm
1224  );
1225  }
1226  }
1227 
1228 
1229  // Set up sends to neighbours
1230 
1231  List<List<T>> sendFields(nProcs);
1232 
1233  for (const int proci : UPstream::allProcs(comm))
1234  {
1235  const labelList& map = subMap[proci];
1236 
1237  if (proci != myRank && map.size())
1238  {
1239  List<T>& subField = sendFields[proci];
1240  subField.resize_nocopy(map.size());
1241 
1242  accessAndFlip(subField, field, map, subHasFlip, negOp);
1243 
1245  (
1247  proci,
1248  subField,
1249  tag,
1250  comm
1251  );
1252  }
1253  }
1254 
1255  // Set up 'send' to myself - copy directly into recvFields
1256  {
1257  const labelList& map = subMap[myRank];
1258  List<T>& subField = recvFields[myRank];
1259  subField.resize_nocopy(map.size());
1260 
1261  accessAndFlip(subField, field, map, subHasFlip, negOp);
1262  }
1263 
1264 
1265  // Combining bits - can now reuse field storage
1266  field.resize_nocopy(constructSize);
1267 
1268 
1269  // Receive sub field from myself : recvFields[myRank]
1270  {
1271  const labelList& map = constructMap[myRank];
1272  const List<T>& subField = recvFields[myRank];
1273 
1274  // Probably don't need a size check
1275  // checkReceivedSize(myRank, map.size(), subField.size());
1276 
1277  flipAndCombine
1278  (
1279  field,
1280  subField,
1281  map,
1282  constructHasFlip,
1283  eqOp<T>(),
1284  negOp
1285  );
1286  }
1287 
1288 
1289  // Poll for completed receive requests and dispatch
1290  DynamicList<int> indices(recvProcs.size());
1291  while
1292  (
1294  (
1295  startOfRequests,
1296  recvProcs.size(),
1297  &indices
1298  )
1299  )
1300  {
1301  for (const int idx : indices)
1302  {
1303  const int proci = recvProcs[idx];
1304  const labelList& map = constructMap[proci];
1305  const List<T>& subField = recvFields[proci];
1306 
1307  // No size check - was dimensioned above
1308  // checkReceivedSize(proci, map.size(), subField.size());
1309 
1310  flipAndCombine
1311  (
1312  field,
1313  subField,
1314  map,
1315  constructHasFlip,
1316  eqOp<T>(),
1317  negOp
1318  );
1319  }
1320  }
1321 
1322  // Wait for any remaining requests
1323  UPstream::waitRequests(startOfRequests);
1324  }
1325  }
1326  else
1327  {
1329  << "Unknown communication schedule " << int(commsType)
1331  }
1332 }
1333 
1334 
1335 template<class T>
1337 (
1338  PstreamBuffers& pBufs,
1339  const UList<T>& field
1340 ) const
1341 {
1342  // Stream data into buffer
1343  for (const int proci : UPstream::allProcs(comm_))
1344  {
1345  const labelList& map = subMap_[proci];
1346 
1347  if (map.size())
1348  {
1349  List<T> subField
1350  (
1351  accessAndFlip(field, map, subHasFlip_, flipOp())
1352  );
1353 
1354  UOPstream os(proci, pBufs);
1355  os << subField;
1356  }
1357  }
1358 
1359  // Start sending and receiving but do not block.
1360  pBufs.finishedSends(false);
1361 }
1362 
1363 
1364 template<class T>
1366 (
1367  PstreamBuffers& pBufs,
1368  List<T>& field
1369 ) const
1370 {
1371  // Consume
1372  field.resize_nocopy(constructSize_);
1373 
1374  for (const int proci : UPstream::allProcs(comm_))
1375  {
1376  const labelList& map = constructMap_[proci];
1377 
1378  if (map.size())
1379  {
1380  UIPstream is(proci, pBufs);
1381  List<T> subField(is);
1382 
1383  checkReceivedSize(proci, map.size(), subField.size());
1384 
1385  flipAndCombine
1386  (
1387  field,
1388  subField,
1389  map,
1390  constructHasFlip_,
1391  eqOp<T>(),
1392  flipOp()
1393  );
1394  }
1395  }
1396 }
1397 
1398 
1399 template<class T, class NegateOp>
1401 (
1402  const UPstream::commsTypes commsType,
1403  List<T>& values,
1404  const NegateOp& negOp,
1405  const int tag
1406 ) const
1407 {
1408  distribute
1409  (
1410  commsType,
1411  whichSchedule(commsType),
1412  constructSize_,
1413  subMap_,
1414  subHasFlip_,
1415  constructMap_,
1416  constructHasFlip_,
1417  values,
1418  negOp,
1419  tag,
1420  comm_
1421  );
1422 }
1423 
1424 
1425 template<class T, class NegateOp>
1427 (
1428  const UPstream::commsTypes commsType,
1429  const T& nullValue,
1430  List<T>& values,
1431  const NegateOp& negOp,
1432  const int tag
1433 ) const
1434 {
1435  distribute
1436  (
1437  commsType,
1438  whichSchedule(commsType),
1439  constructSize_,
1440  subMap_,
1441  subHasFlip_,
1442  constructMap_,
1443  constructHasFlip_,
1444  values,
1445  nullValue,
1446  eqOp<T>(),
1447  negOp,
1448  tag,
1449  comm_
1450  );
1451 }
1452 
1453 
1454 template<class T, class NegateOp>
1456 (
1457  List<T>& values,
1458  const NegateOp& negOp,
1459  const int tag
1460 ) const
1461 {
1462  distribute
1463  (
1465  );
1466 }
1467 
1468 
1469 template<class T>
1471 (
1472  const UPstream::commsTypes commsType,
1473  List<T>& values,
1474  const int tag
1475 ) const
1477  distribute(commsType, values, flipOp(), tag);
1478 }
1479 
1480 
1481 template<class T>
1483 (
1484  const UPstream::commsTypes commsType,
1486  const int tag
1487 ) const
1488 {
1489  List<T> work(std::move(values));
1490 
1491  distribute(commsType, work, tag);
1493  values = std::move(work);
1494 }
1495 
1496 
1497 template<class T>
1499 (
1500  List<T>& values,
1501  const int tag
1502 ) const
1504  distribute(UPstream::defaultCommsType, values, tag);
1505 }
1506 
1507 
1508 template<class T>
1510 (
1512  const int tag
1513 ) const
1515  distribute(UPstream::defaultCommsType, values, tag);
1516 }
1517 
1518 
1519 template<class T>
1521 (
1522  const UPstream::commsTypes commsType,
1523  const label constructSize,
1524  List<T>& values,
1525  const int tag
1526 ) const
1527 {
1528  reverseDistribute<T, flipOp>
1529  (
1530  commsType,
1531  constructSize,
1532  values,
1533  flipOp(),
1534  tag
1535  );
1536 }
1537 
1538 
1539 template<class T, class NegateOp>
1541 (
1542  const UPstream::commsTypes commsType,
1543  const label constructSize,
1544  List<T>& values,
1545  const NegateOp& negOp,
1546  const int tag
1547 ) const
1548 {
1549  distribute
1550  (
1551  commsType,
1552  whichSchedule(commsType),
1553  constructSize,
1554  constructMap_,
1555  constructHasFlip_,
1556  subMap_,
1557  subHasFlip_,
1558  values,
1559  negOp,
1560  tag,
1561  comm_
1562  );
1563 }
1564 
1565 
1566 template<class T>
1568 (
1569  const UPstream::commsTypes commsType,
1570  const label constructSize,
1571  const T& nullValue,
1572  List<T>& values,
1573  const int tag
1574 ) const
1575 {
1576  distribute
1577  (
1578  commsType,
1579  whichSchedule(commsType),
1580  constructSize,
1581  constructMap_,
1582  constructHasFlip_,
1583  subMap_,
1584  subHasFlip_,
1585  values,
1586 
1587  nullValue,
1588  eqOp<T>(),
1589  flipOp(),
1590 
1591  tag,
1592  comm_
1593  );
1594 }
1595 
1596 
1597 template<class T>
1599 (
1600  const label constructSize,
1601  List<T>& values,
1602  const int tag
1603 ) const
1604 {
1605  reverseDistribute
1606  (
1608  constructSize,
1609  values,
1610  tag
1611  );
1612 }
1613 
1614 
1615 template<class T>
1617 (
1618  const label constructSize,
1619  const T& nullValue,
1620  List<T>& values,
1621  const int tag
1622 ) const
1623 {
1624  reverseDistribute
1625  (
1627  constructSize,
1628  nullValue,
1629  values,
1630  tag
1631  );
1632 }
1633 
1634 
1635 // ************************************************************************* //
void send(PstreamBuffers &pBufs, const UList< T > &field) const
Do all sends using PstreamBuffers.
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:114
Definition: ops.H:65
rDeltaTY field()
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:125
commsTypes
Communications types.
Definition: UPstream.H:77
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 label nRequests() noexcept
Number of outstanding requests (on the internal list of requests)
static rangeType allProcs(const label communicator=worldComm)
Range of process indices for all processes.
Definition: UPstream.H:1718
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
static bool & parRun() noexcept
Test if this a parallel run.
Definition: UPstream.H:1586
void resize_nocopy(const label len)
Adjust allocated size of list without necessarily.
Definition: ListI.H:168
static std::streamsize read(const UPstream::commsTypes commsType, const int fromProcNo, Type *buffer, std::streamsize count, const int tag=UPstream::msgType(), const int communicator=UPstream::worldComm, UPstream::Request *req=nullptr)
Receive buffer contents (contiguous types) from given processor.
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
void push_back(const T &val)
Append an element at the end of the list.
Definition: ListI.H:220
List< labelList > labelListList
List of labelList.
Definition: labelList.H:38
static void waitRequests()
Wait for all requests to finish.
Definition: UPstream.H:2189
UList< label > labelUList
A UList of labels.
Definition: UList.H:76
List< T > values(const HashTable< T, Key, Hash > &tbl, const bool doSort=false)
List of values from HashTable, optionally sorted.
Definition: HashOps.H:164
Input inter-processor communications stream using MPI send/recv etc. - operating on external buffer...
Definition: UIPstream.H:295
static void accessAndFlip(UList< T > &output, const UList< T > &values, const labelUList &map, const bool hasFlip, const NegateOp &negOp)
Lookup field values at specified map indices and save after any flip negation operations.
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
"scheduled" (MPI standard) : (MPI_Send, MPI_Recv)
static void recv(Type &value, const int fromProcNo, const int tag=UPstream::msgType(), const int communicator=UPstream::worldComm, IOstreamOption::streamFormat fmt=IOstreamOption::BINARY)
Receive and deserialize a value. Uses operator>> for de-serialization.
Definition: IPstream.H:80
A 1D vector of objects of type <T> that resizes itself as necessary to accept the new objects...
Definition: DynamicList.H:51
void finishedSends(const bool wait=true)
Mark the send phase as being finished.
void reverseDistribute(const label constructSize, List< T > &values, const int tag=UPstream::msgType()) const
Reverse distribute data using default commsType and the default flip/negate operator.
static void flipAndCombine(UList< T > &lhs, const UList< T > &rhs, const labelUList &map, const bool hasFlip, const CombineOp &cop, const NegateOp &negOp)
Combine field values (after any flip negation operation) into the specified mapped target locations...
static bool write(const UPstream::commsTypes commsType, const int toProcNo, const Type *buffer, std::streamsize count, const int tag=UPstream::msgType(), const int communicator=UPstream::worldComm, UPstream::Request *req=nullptr, const UPstream::sendModes sendMode=UPstream::sendModes::normal)
Write buffer contents (contiguous types only) to given processor.
IntType start() const noexcept
The (inclusive) lower value of the range.
Definition: IntRange.H:204
A list of pointers to objects of type <T>, without allocation/deallocation management of the pointers...
Definition: HashTable.H:106
errorManip< error > abort(error &err)
Definition: errorManip.H:139
A 1D vector of objects of type <T>, where the size of the vector is known and can be used for subscri...
Definition: HashTable.H:105
Pair< label > labelPair
A pair of labels.
Definition: Pair.H:51
OBJstream os(runTime.globalPath()/outputName)
Buffers for inter-processor communications streams (UOPstream, UIPstream).
void T(FieldField< Field, Type > &f1, const FieldField< Field, Type > &f2)
void receive(PstreamBuffers &pBufs, List< T > &field) const
Do all receives using PstreamBuffers.
static commsTypes defaultCommsType
Default commsType.
Definition: UPstream.H:963
A list of pointers to objects of type <T>, with allocation/deallocation management of the pointers...
Definition: List.H:54
"nonBlocking" (immediate) : (MPI_Isend, MPI_Irecv)
static Ostream & output(Ostream &os, const IntRange< T > &range)
Definition: IntRanges.C:44
List< label > labelList
A List of labels.
Definition: List.H:61
IntType size() const noexcept
The size of the range.
Definition: IntRange.H:194
"buffered" : (MPI_Bsend, MPI_Recv)
bool send()
Send buffer contents now and not in destructor [advanced usage]. Returns true on success.
Definition: OPstreams.C:84
Functor to negate primitives. Dummy for most other types.
Definition: flipOp.H:66
static void distribute(const UPstream::commsTypes commsType, const UList< labelPair > &schedule, const label constructSize, const labelListList &subMap, const bool subHasFlip, const labelListList &constructMap, const bool constructHasFlip, List< T > &field, const T &nullValue, const CombineOp &cop, const NegateOp &negOp, const int tag=UPstream::msgType(), const label comm=UPstream::worldComm)
Distribute combine data with specified combine operation and negate operator (for flips)...
static bool waitSomeRequests(const label pos, label len=-1, DynamicList< int > *indices=nullptr)
Wait until some requests (from position onwards) have finished. Corresponds to MPI_Waitsome() ...