TrioCFD 1.9.9_beta
TrioCFD documentation
Loading...
Searching...
No Matches
communications.cpp
1/****************************************************************************
2* Copyright (c) 2026, CEA
3* All rights reserved.
4*
5* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
6* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
7* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
8* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
9*
10* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
11* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
12* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
13*
14*****************************************************************************/
15
16#include <InOutCommBuffers.h>
17#include <communications.h>
18#include <Schema_Comm.h>
19#include <TRUSTArrays.h>
20#include <TRUSTTabs.h>
21#include <TRUSTTab.h>
22#include <Motcle.h>
23
24/*! @brief Synchronous point-to-point message sending between source and target.
25 *
26 * The envoyer function completes at the latest when the message has been
27 * received by the corresponding "recevoir" instruction, and the send can be blocking
28 * (blocking is forced in debug mode, according to Comm_Group::check_enabled())
29 * These communications use Comm_Group::send() and Comm_Group::recv()
30 * Examples of possible uses:
31 * 1) point-to-point communication (processor 2 to processor 5)
32 * if (me() == 2)
33 * envoyer(objet, 5, canal);
34 * else if (me() == 5)
35 * recevoir(objet, 2, canal);
36 * or equivalently:
37 * envoyer(objet, 2, 5, canal); // does nothing on processors other than 2
38 * recevoir(objet, 2, 5, canal); // does nothing on processors other than 5
39 * 2) gather: processor 0 retrieves the data of object t from everyone
40 * envoyer(t, me(), 0, canal); // Everyone sends to processor 0, on master it does nothing
41 * if (je_suis_maitre())
42 * for (i = 0; i < nproc(); i++) {
43 * recevoir(t, 0, i; canal); // If i==0, does nothing.
44 * ... do something with t
45 * }
46 * 3) broadcast: processor 0 sends the object to all other processors
47 * It is not necessary to do the test "if (je_suis_maitre())"
48 * envoyer(objet, 0, -1, canal); // Processors other than 0 ignore the call
49 * recevoir(objet, 0, -1, canal);// Processor 0 ignores the call
50 * It is better to use envoyer_broadcast() for this purpose.
51 * 4) broadcast: equivalent form
52 * if (je_suis_maitre())
53 * envoyer(objet, -1, canal); // Emission by master to all processors, except itself
54 * else
55 * recevoir(objet, 0, canal); // Reception of a message from the master processor
56 * It is better to use envoyer_broadcast() for this purpose.
57 * In the implementation of envoyer_, the received object is encapsulated in a buffer and the buffer is sent.
58 * This therefore works for complex types as long as Entree and Sortie can read and write the object.
59 * Each send requires two messages: first the buffer size, then the content.
60 *
61 */
62template <typename T>
63bool envoyer_buffered_(const T& objet, int source, int cible, int canal)
64{
65 const int moi = Process::me();
66 if (moi == cible)
67 return false;
68
69 if (source != moi && source != -1)
70 return false;
71
72 OutputCommBuffer buffer;
73 buffer << objet;
74 const char * data = buffer.get_buffer();
75 const int sz = buffer.get_buffer_size();
77 if (cible < 0)
78 {
79 const int n = grp.nproc();
80 for (int i = 0; i < n; i++)
81 {
82 if (i != moi)
83 {
84 grp.send(i, &sz, sizeof(int), canal);
85 grp.send(i, data, sz, canal);
86 }
87 }
88 }
89 else
90 {
91 grp.send(cible, &sz, sizeof(int), canal);
92 grp.send(cible, data, sz, canal);
93 }
94 return true;
95}
96
97/*! @brief Reception of a message from the source processor.
98 *
99 */
100template <typename T>
101bool recevoir_buffered_(T& objet, int source, int cible, int canal)
102{
103 const int moi = Process::me();
104 if (moi == source)
105 return false;
106
107 if (cible != moi && cible != -1)
108 return false;
109
110 int sz;
112 grp.recv(source, &sz, sizeof(int), canal);
113 InputCommBuffer buffer;
114 char * data = buffer.reserve_buffer(sz);
115 grp.recv(source, data, sz, canal);
116 buffer.create_stream();
117 buffer >> objet;
118 buffer.clear();
119 return true;
120}
121
122/*! @brief Broadcast of the object by the source processor to all other processors.
123 *
124 * Works for complex types supported by Entree or Sortie.
125 *
126 */
127template <typename T>
128bool envoyer_broadcast_buffered_(T& objet, int source)
129{
131 if (grp.rank() == source)
132 {
133 OutputCommBuffer buffer;
134 buffer << objet;
135 const char * data = buffer.get_buffer();
136 int sz = buffer.get_buffer_size();
137 grp.broadcast(&sz, sizeof(int), source);
138 grp.broadcast((char*) data, sz, source);
139 }
140 else
141 {
142 int sz;
143 grp.broadcast(&sz, sizeof(int), source);
144 InputCommBuffer buffer;
145 char * data = buffer.reserve_buffer(sz);
146 grp.broadcast((char*) data, sz, source);
147 buffer.create_stream();
148 buffer >> objet;
149 buffer.clear();
150 }
151 return true;
152}
153
154bool envoyer(const Objet_U& t, int source, int cible, int canal)
155{
156 return envoyer_buffered_(t, source, cible, canal);
157}
158
159bool envoyer(const Objet_U& t, int cible, int canal)
160{
161 return envoyer_buffered_(t, Process::me(), cible, canal);
162}
163
164bool recevoir(Objet_U& t, int source, int cible, int canal)
165{
166 return recevoir_buffered_(t, source, cible, canal);
167}
168
169bool recevoir(Objet_U& t, int source, int canal)
170{
171 return recevoir_buffered_(t, source, Process::me(), canal);
172}
173
174bool envoyer_broadcast(Objet_U& t, int source)
175{
176 return envoyer_broadcast_buffered_(t, source);
177}
178
179// Template that works for any object T that looks like a vector (has an operator[]) of size nproc()
180template <typename T>
181bool envoyer_all_to_all_(const T& src, T& dest)
182{
184 assert(src.size() == Process::nproc());
185 assert(dest.size() == Process::nproc());
186 Schema_Comm sch;
187 const int nproc = grp.nproc();
188 const int moi = grp.rank();
189 dest[moi] = src[moi];
190 ArrOfInt send_pe_list(1);
191 ArrOfInt recv_pe_list(1);
192 for (int p = 1; p < nproc; p++)
193 {
194 const int send_to = (moi + p) % nproc;
195 const int recv_from = (moi + nproc - p) % nproc;
196 send_pe_list[0] = send_to;
197 recv_pe_list[0] = recv_from;
198 sch.set_send_recv_pe_list(send_pe_list, recv_pe_list);
199 sch.begin_comm();
200 sch.send_buffer(send_to) << src[send_to];
202 sch.recv_buffer(recv_from) >> dest[recv_from];
203 sch.end_comm();
204 }
205 return true;
206}
207
208bool envoyer_all_to_all(const TRUST_Vector<TRUSTTab<double>>& src, TRUST_Vector<TRUSTTab<double>>& dest)
209{
210 return envoyer_all_to_all_(src, dest);
211}
212
213bool envoyer_all_to_all(const TRUST_Vector<TRUSTArray<int>>& src, TRUST_Vector<TRUSTArray<int>>& dest)
214{
215 return envoyer_all_to_all_(src, dest);
216}
217
218#if INT_is_64_ == 2
219bool envoyer_all_to_all(const TRUST_Vector<TRUSTArray<trustIdType>>& src, TRUST_Vector<TRUSTArray<trustIdType>>& dest)
220{
221 return envoyer_all_to_all_(src, dest);
222}
223#endif
224
225/*! @brief Assumes that the input and output arrays are of size nproc().
226 *
227 * src[0] is sent to proc 0,
228 * src[1] to proc 1, etc... the value received from processor 0 is placed in
229 * dest[0], processor 1 in dest[1], etc...
230 * It is allowed to call this function with the same array for src and dest.
231 *
232 */
233bool envoyer_all_to_all(std::vector<long long>& src, std::vector<long long>& dest)
234{
236 assert(static_cast<int>(src.size()) == grp.nproc());
237 assert(static_cast<int>(dest.size()) == grp.nproc());
238 if (src.data() == dest.data())
239 {
240 std::vector<long long> tmp(grp.nproc(),0);
241 grp.all_to_all(src.data(), tmp.data(), sizeof(long long));
242 dest = tmp;
243 }
244 else
245 {
246 grp.all_to_all(src.data(), dest.data(), sizeof(long long));
247 }
248 return true;
249}
250
251/*! @brief Computes the transpose of a processor list: builds the array dest_list such that:
252 *
253 * x is in the src_list on processor y
254 * if and only if
255 * y is in the dest_list on processor x
256 * In practice, this function computes the list of
257 * processors that receive messages when the list of
258 * processors that send messages is known, and vice versa.
259 *
260 * @param (src_list) A list of PE numbers present in this comm group. These are the indices of the PEs in the current group (0 <= pe < nproc_)
261 * @param (dest_list) The array in which to store the result. It is resized and filled appropriately. PE numbers are stored in ascending order. These are the indices of the PEs in the current group (0 <= pe < nproc_)
262 */
263bool reverse_send_recv_pe_list(const ArrOfInt& src_list, ArrOfInt& dest_list)
264{
266 const int np = grp.nproc();
267 assert(src_list.size_array() == 0 || max_array(src_list) < np);
268 assert(src_list.size_array() == 0 || min_array(src_list) >= 0);
269 ArrOfInt flag(np);
270 int i;
271 const int src_size = src_list.size_array();
272 for (i = 0; i < src_size; i++)
273 {
274 const int pe = src_list[i];
275 flag[pe] = 1;
276 }
277 ArrOfInt flag_recv(np);
278 envoyer_all_to_all(flag, flag_recv);
279 int n = 0;
280 dest_list.resize_array(np);
281 for (i = 0; i < np; i++)
282 if (flag_recv[i])
283 dest_list[n++] = i;
284 dest_list.resize_array(n);
285 return true;
286}
287
288/*! @brief Assumes that the input and output arrays have dimension(0)==nproc() and that the other dimensions
289 *
290 * are identical. src(0, i, j, ...) is sent to proc 0,
291 * src(0, i, j, ...) to proc 1, etc... the value received from processor 0 is placed in
292 * dest(0, i, j, ...) , processor 1 in dest(1, i, j, ...) , etc...
293 * It is allowed to call this function with the same array for src and dest.
294 *
295 */
296void envoyer_all_to_all(const DoubleTab& src, DoubleTab& dest)
297{
299 assert(src.dimension(0) == grp.nproc());
300 assert(dest.dimension(0) == grp.nproc());
301 assert(src.size_array() == dest.size_array());
302
303 if (dest.data() == src.data())
304 {
305 DoubleTab tmp(src);
306 envoyer_all_to_all(src, tmp);
307 dest = tmp;
308 }
309 else
310 {
311 const int sz = src.size() / grp.nproc() * (int)sizeof(double);
312 grp.all_to_all(src.data(), dest.data(), sz);
313 }
314}
315template <typename T>
316void envoyer_all_gather_(const TRUSTTab<T>& src, TRUSTTab<T>& dest)
317{
319
320 if (dest.addr() == src.addr())
321 {
322 assert(src.dimension(0) == grp.nproc());
323 assert(dest.dimension(0) == grp.nproc());
324 const int sz = src.size() / grp.nproc() * (int)sizeof(T);
325 TRUSTTab<T> tmp(src);
326 grp.all_gather(src.addr(), tmp.addr(), sz);
327 dest = tmp;
328 }
329 else
330 {
331 const int sz = src.size() * (int)sizeof(T);
332 assert( dest.size_array() == grp.nproc()*src.size_array());
333 grp.all_gather(src.addr(), dest.addr(), sz);
334 }
335}
336
337template <typename T>
338void envoyer_gather_(const TRUSTTab<T>& src, TRUSTTab<T>& dest, int root)
339{
341
342 if (dest.addr() == src.addr())
343 {
344 assert(src.dimension(0) == grp.nproc());
345 assert(dest.dimension(0) == grp.nproc());
346 const int sz = src.size() / grp.nproc() * (int)sizeof(T);
347 TRUSTTab<T> tmp(src);
348 grp.gather(src.addr(), tmp.addr(), sz, root);
349 dest = tmp;
350 }
351 else
352 {
353 const int sz = src.size() * (int)sizeof(T);
354 assert( dest.size_array() == grp.nproc()*src.size_array());
355 grp.gather(src.addr(), dest.addr(), sz, root);
356 }
357}
358
359void envoyer_gather(const DoubleTab& src, DoubleTab& dest, int root)
360{
361 envoyer_gather_(src,dest,root);
362}
363
364void envoyer_gather(const IntTab& src, IntTab& dest, int root)
365{
366 envoyer_gather_(src,dest,root);
367}
368
369void envoyer_all_gather(const DoubleTab& src, DoubleTab& dest)
370{
371 envoyer_all_gather_(src,dest);
372}
373
374void envoyer_all_gather(const IntTab& src, IntTab& dest)
375{
376 envoyer_all_gather_(src,dest);
377}
378
379void envoyer_all_gatherv(const DoubleTab& src, DoubleTab& dest, const IntTab& recv_size)
380{
382
383 assert(dest.size_array()==local_somme_vect(recv_size));
384 assert(src.size_array()==recv_size[grp.me()]);
385
386 const int nbprocs = (int)grp.nproc();
387
388 std::vector<int> sz(nbprocs);
389 for (int p=0; p<nbprocs; p++)
390 sz[p] = recv_size[p] * (int)sizeof(double);
391
392 int sz0 = src.size() * (int)sizeof(double);
393
394 std::vector<int> displs(nbprocs);
395 displs[0] = 0;
396 for (int p=1; p<nbprocs; p++)
397 displs[p] = displs[p-1]+sz[p-1];
398
399 grp.all_gatherv(src.addr(), dest.addr(), sz0 , sz.data(),displs.data());
400}
401
402
403/*! @brief Returns the Comm_Group::check_enabled() flag.
404 *
405 * This flag indicates whether the code is running in parallel DEBUG mode. In that case, additional communications take place to verify parallelism consistency and the communication mode
406 * is forced to synchronous. This method allows testing the flag, and if it is set, performing additional checks.
407 *
408 */
409bool comm_check_enabled() { return Comm_Group::check_enabled(); }
410
411/*! @brief Returns 1 (on all procs) if the parameter is identical on all procs, otherwise returns 0 (on all procs).
412 *
413 */
414bool is_parallel_object(const int x)
415{
416 const int x1 = Process::mp_min(x);
417 const int x2 = Process::mp_max(x);
418 return x1 == x2;
419}
420
421#if INT_is_64_ == 2
422bool is_parallel_object(const trustIdType x)
423{
424 const trustIdType x1 = Process::mp_min(x);
425 const trustIdType x2 = Process::mp_max(x);
426 return x1 == x2;
427}
428#endif
429
430/*! @brief Returns 1 (on all procs) if the parameter is identical on all procs, otherwise returns 0 (on all procs).
431 *
432 */
433bool is_parallel_object(const double x)
434{
436 double tab_x[2];
437 tab_x[0] = x;
438 tab_x[1] = x;
439 double tab_y[2];
441 grp.mp_collective_op(tab_x, tab_y, tab_op, 2);
442 return tab_y[0] == tab_y[1];
443}
444
445template <class T> int compare(const Objet_U& a, const Objet_U& b, int& erreur)
446{
447 if (sub_type(T, a))
448 {
449 const T& a_ = static_cast<const T&> (a);
450 const T& b_ = static_cast<const T&> (b);
451 if (! (a_ == b_))
452 erreur = 1;
453 return 1;
454 }
455 else
456 {
457 return 0;
458 }
459}
460
461/*! @brief Returns 1 (on all procs) if the parameter is identical on all procs, otherwise returns 0 (on all procs).
462 *
463 */
464bool is_parallel_object(const Objet_U& obj)
465{
466 int erreur = 0;
468 {
469 DerObjU copie;
470 const int n = Process::nproc();
471 for (int i = 1; i < n; i++)
472 {
473 recevoir(copie, i, 67 /* tag */);
474 if (erreur)
475 continue; // We already found an error, we receive the other messages but that's all...
476 const Objet_U& obj2 = copie.valeur();
477 if (obj2.que_suis_je() != obj.que_suis_je())
478 {
479 erreur = 1;
480 }
481 else
482 {
483 // The == operator is not virtual, so we cannot simply compare with ==.
484 // We must call == for the specific sub-type of the object.
485 // The order is important: first the most derived type, then its base class.
486 if (compare<IntTab> (obj, obj2, erreur)) { /* Do nothing */ }
487 else if (compare<DoubleTab> (obj, obj2, erreur)) { /* Do nothing */ }
488 else if (compare<IntVect> (obj, obj2, erreur)) { /* Do nothing */ }
489 else if (compare<DoubleVect> (obj, obj2, erreur)) { /* Do nothing */ }
490 else if (compare<ArrOfInt> (obj, obj2, erreur)) { /* Do nothing */ }
491 else if (compare<ArrOfDouble> (obj, obj2, erreur)) { /* Do nothing */ }
492 else if (compare<Motcle> (obj, obj2, erreur)) { /* Do nothing */ }
493 else if (compare<Nom> (obj, obj2, erreur)) { /* Do nothing */ }
494 else
495 {
496 Cerr << "Internal error in is_parallel_objec() : it is not coded for " << obj.que_suis_je() << finl;
498 }
499 }
500 }
501 }
502 else
503 {
504 // We send an object of type OWN_PTR..
505 DerObjU copie(obj);
506 envoyer(copie, 0, 67 /* tag */);
507 }
508
509 envoyer_broadcast(erreur, 0 /* pe source */);
510 return !erreur;
511}
512
513/*! @brief In comm_check_enabled() mode, verifies that the parameter has the same value on all processors.
514 *
515 * Does not work for all types! Currently ok for Noms, Motcles and arrays.
516 *
517 */
518void assert_parallel(const Objet_U& obj)
519{
521 return;
522
523 if (!is_parallel_object(obj))
524 {
525 Cerr << "Fatal error in assert_parallel(const Objet_U & obj): objects do not match, see log files" << finl;
526 Process::Journal() << "Error in assert_parallel(const Objet_U & obj)"
527 << "This processor has this object : " << obj.que_suis_je() << finl
528 << obj << finl;
531 }
532}
: This class describes a group of processors on which
Definition Comm_Group.h:37
static int check_enabled()
Definition Comm_Group.h:154
virtual void all_gather(const void *src_buffer, void *dest_buffer, int data_size) const =0
virtual void send(int pe, const void *buffer, int size, int tag) const =0
int nproc() const
Returns the number of processors in the group *this.
Definition Comm_Group.h:185
int rank() const
Returns the rank of the local processor in the group *this.
Definition Comm_Group.h:177
virtual void all_gatherv(const void *src_buffer, void *dest_buffer, int send_size, const int *recv_size, const int *displs) const =0
virtual void broadcast(void *buffer, int size, int pe_source) const =0
virtual void mp_collective_op(const double *x, double *resu, int n, Collective_Op op) const =0
virtual void all_to_all(const void *src_buffer, void *dest_buffer, int data_size) const =0
virtual void recv(int pe, void *buffer, int size, int tag) const =0
virtual void gather(const void *src_buffer, void *dest_buffer, int data_size, int root) const =0
: Helper class used exclusively by Schema_Comm.
char * reserve_buffer(int bufsize)
Base class for TRUST objects (Objet_U).
Definition Objet_U.h:68
const Nom & que_suis_je() const
Returns the string identifying the class.
Definition Objet_U.cpp:104
Tool class used exclusively by Schema_Comm.
const char * get_buffer()
static const Comm_Group & current_group()
Returns a reference to the current active processor group.
Definition PE_Groups.h:64
static double mp_min(double)
Definition Process.cpp:391
static double mp_max(double)
Definition Process.cpp:379
static Sortie & Journal(int message_level=0)
Returns a static Sortie object used as an event journal.
Definition Process.cpp:592
static int nproc()
Returns the number of processors in the current group. See Comm_Group::nproc() and PE_Groups::current...
Definition Process.cpp:102
static void barrier()
Synchronizes all processors in the current group (waits until all processors have reached the barrier...
Definition Process.cpp:133
static int me()
Returns the rank of the local processor in the current communication group. See Comm_Group::rank() an...
Definition Process.cpp:122
static void exit(int exit_code=-1)
Exit routine for TRUST within a Kokkos region.
Definition Process.cpp:466
static int je_suis_maitre()
Returns 1 if on the master processor of the current group (i.e. me() == 0), 0 otherwise.
Definition Process.cpp:82
void echange_taille_et_messages() const
Launches the data exchange between all processors.
Sortie & send_buffer(int num_PE) const
Returns the buffer corresponding to processor num_PE to stack data to send.
void end_comm() const
Clears the buffers and releases resources: reading of received data from buffers is complete.
Entree & recv_buffer(int num_PE) const
Returns the buffer corresponding to processor num_PE to read received data.
void begin_comm() const
Reserves communication buffers for a new communication.
void set_send_recv_pe_list(const ArrOfInt &send_pe_list, const ArrOfInt &recv_pe_list, const int me_to_me=0)
Defines the list of processors to send data to and receive data from.
Represents a an array of int/int64/double/... values.
Definition TRUSTArray.h:81
_SIZE_ size_array() const
_TYPE_ * addr()
_TYPE_ * data()
void resize_array(_SIZE_ new_size, RESIZE_OPTIONS opt=RESIZE_OPTIONS::COPY_INIT)
N-dimensional array for N <= 4.
Definition TRUSTTab.h:31
_SIZE_ dimension(int d) const
Definition TRUSTTab.tpp:133
_SIZE_ size() const
Definition TRUSTVect.tpp:45
const Objet_U & valeur() const
TRUST_Vector class.