TrioCFD 1.9.9_beta
TrioCFD documentation
Loading...
Searching...
No Matches
Schema_Comm.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#include <Schema_Comm.h>
16#include <PE_Groups.h>
17#include <InOutCommBuffers.h>
18#include <Comm_Group.h>
19#include <communications.h>
20#include <Comm_Group_MPI.h>
21
22// ====================================================================
23// class Schema_Comm
24// ====================================================================
25
27InOutCommBuffers Schema_Comm::buffers_;
28int Schema_Comm::n_buffers_ = 0;
29
30/*! @brief @brief Accessor to a member of the obuffers_ array (with verification).
31 *
32 * @param pe Processor index.
33 * @return Reference to the OutputCommBuffer for processor pe.
34 */
36{
37 assert(pe >= 0 && pe < n_buffers_);
38 return buffers_.obuffers_[pe];
39}
40
41/*! @brief @brief Accessor to a member of the ebuffers_ array (with verification).
42 *
43 * @param pe Processor index.
44 * @return Reference to the InputCommBuffer for processor pe.
45 */
47{
48 assert(pe >= 0 && pe < n_buffers_);
49 return buffers_.ebuffers_[pe];
50}
51
52//static const int SET_GROUP_TAG = 0;
53static const int BEGIN_COMM_TAG = 1;
54static const int ECHANGE_MESSAGES_COMM_TAG = 2;
55static const int END_COMM_TAG = 3;
56static const int COPY_OPERATOR_TAG = 4;
57//static const int CHECK_SEND_RCV_TAG = 5;
58
59/*! @brief Constructs a new communication schema.
60 *
61 */
63{
64
65
66 me_to_me_ = 0;
68 // To verify later that we are still in the correct group,
69 // we keep a reference to the current group.
70 ref_group_ = PE_Groups::current_group();
72 {
73 n_buffers_ = Process::nproc();
74 buffers_.obuffers_ = new OutputCommBuffer[n_buffers_];
75 buffers_.ebuffers_ = new InputCommBuffer[n_buffers_];
76 status_ = RESET;
77 }
78}
79
80/*! @brief Destructs a communication schema.
81 *
82 */
86
87/*! @brief Copy constructor (new schema placed in RESET mode).
88 *
89 * Warning: all members of the Comm_Group must execute this function simultaneously.
90 *
91 */
93{
94
95
96 operator= (schema);
97}
98
99/*! @brief Copy operator: copies the list of communicating processors.
100 *
101 * The new schema is placed in the RESET state.
102 * Note: all members of the Comm_Group must execute this function simultaneously.
103 *
104 */
106{
107 assert(status_ == RESET);
108 ref_group_ = schema.ref_group_;
109 assert(&(ref_group_.valeur()) == &PE_Groups::current_group());
112 me_to_me_ = schema.me_to_me_;
114 const Comm_Group& group = ref_group_.valeur();
115 if (group.check_enabled()) group.barrier(COPY_OPERATOR_TAG);
116 return *this;
117}
118
119/*! @brief Obsolete method. The group associated with the schema is the current group at the time the schema is created.
120 *
121 * This method is only valid with the same group as the original group.
122 * The method does nothing.
123 *
124 */
126{
127 assert(&group == &(ref_group_.valeur()));
128 assert(&group == &PE_Groups::current_group());
129}
130
131/*! @brief Returns the group associated with the schema.
132 *
133 */
135{
136 assert(ref_group_);
137 return ref_group_.valeur();
138}
139
140/*! @brief Defines the list of processors to send data to and receive data from.
141 *
142 * If me_to_me is non-zero, messages to oneself are allowed;
143 * otherwise not (optional argument: default me_to_me=0).
144 *
145 */
146void Schema_Comm::set_send_recv_pe_list(const ArrOfInt& send_pe_list,
147 const ArrOfInt& recv_pe_list,
148 const int me_to_me)
149{
150 assert(status_ == RESET);
151 send_pe_list_ = send_pe_list;
152 recv_pe_list_ = recv_pe_list;
153 me_to_me_ = me_to_me;
154 // Verification of the principle "you listen when I speak"
155 const Comm_Group& group = ref_group_.valeur();
156 assert(&group == &PE_Groups::current_group());
158}
159
160/*! @brief Reserves communication buffers for a new communication.
161 *
162 * The schema transitions from RESET to WRITING; send_buffer() may now be called.
163 * It is forbidden to call begin_comm() again on all communication objects
164 * before finishing this communication with end_comm().
165 *
166 */
168{
169 // Verify that no other communication is in progress.
170 assert (status_ == RESET && ref_group_);
172 // Verify that all group members execute this.
173 // If this crashes here, it means that not all declared members
174 // are reaching the barrier.
175 const Comm_Group& group = ref_group_.valeur();
176 assert(&group == &PE_Groups::current_group());
177 if (group.check_enabled()) group.barrier(BEGIN_COMM_TAG);
178}
179
180static void exchange_data(const ArrOfInt& send_list,
181 const ArrOfInt& send_size,
182 const char * const * const send_buffers,
183 const ArrOfInt& recv_list,
184 const ArrOfInt& recv_size,
185 char * const * const recv_buffers,
186 const Comm_Group& group,
187 bool use_all_to_all)
188{
189// GF added ifdef for builds without MPI where comm_group_mpi does not exist
190#ifdef MPI_
191 if (!use_all_to_all || !sub_type(Comm_Group_MPI, group))
192#else
193 //if (!use_all_to_all || !sub_type(Comm_Group_MPI, group))
194 // do not use all_to_all as it does not exist without MPI
195 if (1)
196#endif
197 {
198 group.send_recv_start(send_list, send_size, send_buffers,
199 recv_list, recv_size, recv_buffers);
200 group.send_recv_finish();
201 }
202 else
203 {
204#ifdef MPI_
205 const int n = group.nproc();
206 const int nsend = send_list.size_array();
207 const int nrecv = recv_list.size_array();
208 // Pack all send data in a single buffer:
209 ArrOfInt a_send_size(n);
210 ArrOfInt a_send_offset(n);
211 ArrOfInt a_recv_size(n);
212 ArrOfInt a_recv_offset(n);
213 int i, offset;
214 // Compute send_size array
215 for (i = 0; i < nsend; i++)
216 a_send_size[send_list[i]] = send_size[i];
217 // Compute send_offset
218 for (i = 0, offset = 0; i < n; i++)
219 {
220 a_send_offset[i] = offset;
221 offset += a_send_size[i];
222 }
223 const int buf_size_send = offset;
224 // Compute recv_size array
225 for (i = 0; i < nrecv; i++)
226 a_recv_size[recv_list[i]] = recv_size[i];
227 // Compute recv_offset
228 for (i = 0, offset = 0; i < n; i++)
229 {
230 a_recv_offset[i] = offset;
231 offset += a_recv_size[i];
232 }
233 const int buf_size_recv = offset;
234 // Allocate contiguous send and recv buffer:
235 char *send_buffer = (char *) malloc(buf_size_send);
236 char *recv_buffer = (char *) malloc(buf_size_recv);
237 // Copy send data to send buffer
238 for (i = 0; i < nsend; i++)
239 memcpy(send_buffer + a_send_offset[send_list[i]], // dest
240 send_buffers[i], // source
241 send_size[i]); // size
242 // Exchange data
243 ref_cast(Comm_Group_MPI, group).all_to_allv(send_buffer, a_send_size.addr(), a_send_offset.addr(),
244 recv_buffer, a_recv_size.addr(), a_recv_offset.addr());
245 // Copy recv data to recv_buffers
246 for (i = 0; i < nrecv; i++)
247 memcpy(recv_buffers[i], // dest
248 recv_buffer + a_recv_offset[recv_list[i]], // source
249 recv_size[i]); // size
250 // [ABN] arrruhhhhhmmmmmm:
251 free(send_buffer);
252 free(recv_buffer);
253#endif
254 }
255}
256
257/*! @brief Transmits the size of messages to send to the processors that will receive them.
258 *
259 * The size is the number of bytes of the obuffers.
260 * send_pe_list and recv_pe_list must be initialized.
261 * The schema must be in the WRITING state.
262 *
263 */
264void Schema_Comm::echange_taille(const ArrOfInt& send_size,
265 ArrOfInt& recv_size) const
266{
267 static ArrOfInt send_sz;
268 static ArrOfInt recv_sz;
269
270
271
272 assert(status_ == WRITING && ref_group_);
273 const Comm_Group& group = ref_group_.valeur();
274 assert(&group == &PE_Groups::current_group());
275
276 // Verify that all group members execute this.
277 // If this crashes here, it means not all declared members are reaching the barrier.
278 if (group.check_enabled()) group.barrier(ECHANGE_MESSAGES_COMM_TAG);
279
280 const int n_send = send_pe_list_.size_array();
281 const int n_recv = recv_pe_list_.size_array();
282
283 recv_size.resize_array(n_recv);
284
285 const char ** send_buffers = new const char* [n_send];
286 char ** recv_buffers = new char* [n_recv];
287
288 send_sz.resize_array(n_send); // Size of an int (we exchange a size)
289 send_sz = sizeof(int);
290 int i;
291 for (i = 0; i < n_send; i++)
292 send_buffers[i] = (char*) (& send_size[i]);
293
294 recv_sz.resize_array(n_recv);
295 recv_sz = sizeof(int);
296 recv_size.resize_array(n_recv);
297 for (i = 0; i < n_recv; i++)
298 recv_buffers[i] = (char*) (& recv_size[i]);
299
300 exchange_data(send_pe_list_, send_sz, send_buffers,
301 recv_pe_list_, recv_sz, recv_buffers,
302 group,
304
305 delete[] recv_buffers;
306 delete[] send_buffers;
307}
308
309/*! @brief Launches the data exchange between all processors.
310 *
311 * The size of received messages must already be known.
312 * The schema transitions from WRITING to EXCHANGED.
313 *
314 */
315void Schema_Comm::echange_messages(const ArrOfInt& send_size,
316 const ArrOfInt& recv_size) const
317{
318 assert(status_ == WRITING && ref_group_);
319 const Comm_Group& group = ref_group_.valeur();
320 assert(&group == &PE_Groups::current_group());
321
322 // Verify that all group members execute this.
323 // If this crashes here, it means not all declared members are reaching the barrier.
324 if (group.check_enabled()) group.barrier(ECHANGE_MESSAGES_COMM_TAG);
325
326 const int n_send = send_pe_list_.size_array();
327 const int n_recv = recv_pe_list_.size_array();
328 const char ** send_buffers = new const char* [n_send];
329 char ** recv_buffers = new char* [n_recv];
330
331 int i;
332 for (i = 0; i < n_recv; i++)
333 {
334 int pe = recv_pe_list_[i];
335 int size = recv_size[i];
336 InputCommBuffer& buf = ebuffer(pe);
337 recv_buffers[i] = buf.reserve_buffer(size);
338 }
339 for (i = 0; i < n_send; i++)
340 {
341 int pe = send_pe_list_[i];
342 OutputCommBuffer& buf = obuffer(pe);
343 // Verify that the outgoing message size matches the size recorded in send_size_,
344 // which guarantees that the receive size is also correct.
345 assert(send_size[i] == buf.get_buffer_size());
346 send_buffers[i] = buf.get_buffer();
347 }
348
349 exchange_data(send_pe_list_, send_size, send_buffers,
350 recv_pe_list_, recv_size, recv_buffers,
351 group,
353
354 delete[] recv_buffers;
355 delete[] send_buffers;
356
357 // Create input streams from received buffers
358 for (i = 0; i < n_recv; i++)
359 {
360 int pe = recv_pe_list_[i];
361 InputCommBuffer& buf = ebuffer(pe);
362 buf.create_stream();
363 }
364
365 // Special case of messages sent to oneself:
366 if (me_to_me_)
367 {
368 int pe = Process::me();
369 InputCommBuffer& buf = ebuffer(pe);
370 OutputCommBuffer& obuf = obuffer(pe);
371 buf.create_stream_from_output_stream(obuf);
372 }
373
375}
376
377/*! @brief Launches the data exchange between all processors.
378 *
379 * The size of received messages does not need to be known a priori; it is transmitted.
380 * The schema transitions from WRITING to EXCHANGED.
381 *
382 */
384{
385 static ArrOfInt send_size;
386 static ArrOfInt recv_size;
387 const int n_send = send_pe_list_.size_array();
388 send_size.resize_array(n_send);
389 int i;
390 for (i = 0; i < n_send; i++)
391 {
392 int pe = send_pe_list_[i];
393 send_size[i] = obuffer(pe).get_buffer_size();
394 }
395
396 // Non-optimal method: in MPI one could use MPI_Probe but
397 // this method seems unreliable.
398 echange_taille(send_size, recv_size);
399 echange_messages(send_size, recv_size);
400}
401
402/*! @brief Launches the data exchange.
403 *
404 * The size in bytes of received messages is provided in recv_size (array of the same size as recv_pe_list).
405 * In check_enabled mode, verifies that the size is correct.
406 *
407 */
408void Schema_Comm::echange_messages(const ArrOfInt& recv_size) const
409{
410 const int n_send = send_pe_list_.size_array();
411 ArrOfInt send_size(n_send);
412 int i;
413 for (i = 0; i < n_send; i++)
414 {
415 int pe = send_pe_list_[i];
416 send_size[i] = obuffer(pe).get_buffer_size();
417 }
418 if (PE_Groups::current_group().check_enabled())
419 {
420 ArrOfInt check_recv_size;
421 echange_taille(send_size, check_recv_size);
422 if (!(check_recv_size == recv_size))
423 {
424 Cerr << "Error in Schema_Comm::echange_messages : bad recv_size" << finl;
426 }
427 }
428 echange_messages(send_size, recv_size);
429}
430
431/*! @brief Clears the buffers and releases resources: reading of received data from buffers is complete.
432 *
433 * The schema transitions from EXCHANGED to RESET.
434 *
435 */
437{
438 assert(status_ == EXCHANGED && ref_group_);
439 const Comm_Group& group = ref_group_.valeur();
440 assert(&group == &PE_Groups::current_group());
441
442 // Check that all group members are executing this.
443 // If it crashes here, it means not all declared members are at the barrier.
444 if (group.check_enabled()) group.barrier(END_COMM_TAG);
445
446 int i, n;
447 n = send_pe_list_.size_array();
448 for (i = 0; i < n; i++)
449 {
450 int pe = send_pe_list_[i];
451 obuffer(pe).clear();
452 }
453 n = recv_pe_list_.size_array();
454 for (i = 0; i < n; i++)
455 {
456 int pe = recv_pe_list_[i];
457 ebuffer(pe).clear();
458 }
459
460 if (me_to_me_)
461 {
462 int pe = Process::me();
463 obuffer(pe).clear();
464 ebuffer(pe).clear();
465 }
466
467 status_ = RESET;
468}
469
470#ifndef NDEBUG
471static int check_PE_in_list(int num_pe, const ArrOfInt& list)
472{
473 int i;
474 int n = list.size_array();
475 for (i = 0; i < n && list[i] != num_pe; i++);
476 return (i < n);
477}
478#endif
479
480/*! @brief Returns the buffer corresponding to processor num_PE to stack data to send.
481 *
482 * The schema must be in the WRITING state.
483 *
484 */
486{
487 // If the following assert fails, it means we are trying to
488 // put data in the buffer outside of the block
489 // begin_comm();
490 // ...
491 // echange_xxx();
492 assert(status_ == WRITING && ref_group_);
493
494 // Check that the requested PE is in the list of declared send PEs.
495 assert((me_to_me_&&num_PE==Process::me()) || check_PE_in_list(num_PE, send_pe_list_));
496 return obuffer(num_PE);
497}
498
499/*! @brief Returns the buffer corresponding to processor num_PE to read received data.
500 *
501 * The schema must be in the EXCHANGED state.
502 *
503 */
505{
506 // If the following assert fails, it means we are trying to
507 // read data from the buffers outside of the block
508 // echange_xxx();
509 // ...
510 // end_comm();
511 assert(status_ == EXCHANGED && ref_group_);
512 // Check that the requested PE is in the list of declared receive PEs.
513 assert((me_to_me_&&num_PE==Process::me()) || check_PE_in_list(num_PE, recv_pe_list_));
514 return ebuffer(num_PE);
515}
516
517const ArrOfInt& Schema_Comm::get_send_pe_list() const
518{
519 assert(ref_group_);
520 return send_pe_list_;
521}
522
523const ArrOfInt& Schema_Comm::get_recv_pe_list() const
524{
525 assert(ref_group_);
526 return recv_pe_list_;
527}
528
529/*! @brief Returns a reference to an array containing, for each processor in send_pe_list_, the size in bytes of the data
530 *
531 * to send.
532 * TODO: TO FINISH !!!!
533 *
534 */
536{
537 assert(0);
538 assert(status_ == EXCHANGED);
539 return send_size_;
540}
541
542/*! @brief Returns a reference to an array containing, for each processor in send_pe_list_, the size in bytes of the data
543 *
544 * received.
545 * TODO: TO FINISH !!!!
546 *
547 */
549{
550 assert(0);
551 assert(status_ == EXCHANGED);
552 return recv_size_;
553}
554
555/*! @brief Verifies that send/recv_pe_list satisfy the property "you listen when I speak".
556 *
557 */
559{
560 assert(status_ == RESET);
561 // Check that processor indices are in the group
562 int fail1 = 0;
563 const int np = Process::nproc();
564 const int n1 = send_pe_list_.size_array();
565 int i;
566 for (i = 0; i < n1; i++)
567 if (send_pe_list_[i] < 0 || send_pe_list_[i] >= np)
568 fail1 = 1;
569 const int n2 = recv_pe_list_.size_array();
570 for (i = 0; i < n2; i++)
571 if (recv_pe_list_[i] < 0 || recv_pe_list_[i] >= np)
572 fail1 = 1;
573 int fail2 = 0;
574 ArrOfInt recv_list;
575 if (!fail1)
576 {
577 reverse_send_recv_pe_list(send_pe_list_, recv_list);
578 // The array recv_pe_list_ is not necessarily sorted whereas recv_list is always sorted
579 // Sort before comparing
580 ArrOfInt copie(recv_pe_list_);
581 copie.ordonne_array();
582 fail2 = !(recv_list == copie);
583 }
584 if (Process::mp_sum(fail1+fail2))
585 {
587 Cerr << "Error in Schema_Comm::check_send_recv_pe_list(), see .log files" << finl;
588 Process::Journal() << "Error in Schema_Comm::check_send_recv_pe_list() :\n"
589 << "send_list:\n" << send_pe_list_
590 << "recv_list:\n" << recv_pe_list_;
591 if (fail1)
592 Process::Journal() << "processor ranks not in current group: current group size = " << np;
593 else if (fail2)
594 Process::Journal() << "recv_list should be this one:\n" << recv_list << finl;
595 else
596 Process::Journal() << "OK on this processor" << finl;
599 }
600}
: Class Comm_Group_MPI, derived from the abstract class Comm_Group.
: 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 send_recv_finish() const =0
int nproc() const
Returns the number of processors in the group *this.
Definition Comm_Group.h:185
virtual void send_recv_start(const ArrOfInt &send_list, const ArrOfInt &send_size, const char *const *const send_buffers, const ArrOfInt &recv_list, const ArrOfInt &recv_size, char *const *const recv_buffers, TypeHint typehint=CHAR) const =0
virtual void barrier(int tag) const =0
Class defining operators and methods for all reading operation in an input flow (file,...
Definition Entree.h:42
: Helper class used exclusively by Schema_Comm.
Tool class used exclusively by Schema_Comm.
static const Comm_Group & current_group()
Returns a reference to the current active processor group.
Definition PE_Groups.h:64
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 double mp_sum(double)
Computes the sum of x over all processors in the current group.
Definition Process.cpp:145
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
const ArrOfInt & get_send_size() const
Returns a reference to an array containing, for each processor in send_pe_list_, the size in bytes of...
const ArrOfInt & get_recv_size() const
Returns a reference to an array containing, for each processor in send_pe_list_, the size in bytes of...
void echange_messages(const ArrOfInt &recv_size) const
Launches the data exchange.
static InputCommBuffer & ebuffer(int pe)
Accessor to a member of the ebuffers_ array (with verification).
const Schema_Comm & operator=(const Schema_Comm &)
Copy operator: copies the list of communicating processors.
ArrOfInt recv_pe_list_
void set_group(const Comm_Group &group)
Obsolete method. The group associated with the schema is the current group at the time the schema is ...
const Comm_Group & get_group() const
Returns the group associated with the schema.
void echange_taille_et_messages() const
Launches the data exchange between all processors.
~Schema_Comm()
Destructs a communication schema.
Sortie & send_buffer(int num_PE) const
Returns the buffer corresponding to processor num_PE to stack data to send.
static OutputCommBuffer & obuffer(int pe)
Accessor to a member of the obuffers_ array (with verification).
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.
int use_all_to_allv_
void echange_taille(const ArrOfInt &send_size, ArrOfInt &recv_size) const
Transmits the size of messages to send to the processors that will receive them.
const ArrOfInt & get_recv_pe_list() const
ArrOfInt send_pe_list_
void begin_comm() const
Reserves communication buffers for a new communication.
void check_send_recv_pe_list() const
Verifies that send/recv_pe_list satisfy the property "you listen when I speak".
const ArrOfInt & get_send_pe_list() const
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.
static Static_Status status_
Schema_Comm()
Constructs a new communication schema.
Base class for output streams.
Definition Sortie.h:52
_SIZE_ size_array() const
void resize_array(_SIZE_ new_size, RESIZE_OPTIONS opt=RESIZE_OPTIONS::COPY_INIT)
void ordonne_array()