Arcane  4.1.12.0
Developer documentation
Loading...
Searching...
No Matches
GetVariablesValuesParallelOperation.cc
1// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2//-----------------------------------------------------------------------------
3// Copyright 2000-2026 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com)
4// See the top-level COPYRIGHT file for details.
5// SPDX-License-Identifier: Apache-2.0
6//-----------------------------------------------------------------------------
7/*---------------------------------------------------------------------------*/
8/* GetVariablesValuesParallelOperation.cc (C) 2000-2025 */
9/* */
10/* Operations to access variable values from another subdomain. */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arcane/utils/ITraceMng.h"
15
16#include "arcane/core/Timer.h"
17#include "arcane/core/VariableTypes.h"
18#include "arcane/core/IParallelMng.h"
19#include "arcane/core/ISerializer.h"
20#include "arcane/core/IItemFamily.h"
21#include "arcane/core/ISerializeMessage.h"
22
23#include "arcane/impl/GetVariablesValuesParallelOperation.h"
24
25#include "arccore/message_passing/ISerializeMessageList.h"
26
27#include <map>
28
29/*---------------------------------------------------------------------------*/
30/*---------------------------------------------------------------------------*/
31
32namespace Arcane
33{
34using namespace MessagePassing;
35
36/*---------------------------------------------------------------------------*/
37/*---------------------------------------------------------------------------*/
38
39GetVariablesValuesParallelOperation::
40GetVariablesValuesParallelOperation(IParallelMng* pm)
41: m_parallel_mng(pm)
42{
43}
44
45/*---------------------------------------------------------------------------*/
46/*---------------------------------------------------------------------------*/
47
48IParallelMng* GetVariablesValuesParallelOperation::
49parallelMng()
50{
51 return m_parallel_mng;
52}
53
54/*---------------------------------------------------------------------------*/
55/*---------------------------------------------------------------------------*/
56
57void GetVariablesValuesParallelOperation::
58getVariableValues(VariableItemReal& variable,
59 ConstArrayView<Int64> unique_ids,
60 ConstArrayView<Int32> sub_domain_ids,
61 ArrayView<Real> values)
62{
63 IParallelMng* pm = m_parallel_mng;
64 Timer::Phase tphase(pm->timeStats(), TP_Communication);
65
66 if (!pm->isParallel()) {
67 _getVariableValuesSequential(variable, unique_ids, values);
68 return;
69 }
70
71 ItemGroup group = variable.itemGroup();
72 IItemFamily* item_family = variable.variable()->itemFamily();
73
74 if (group.null())
75 ARCANE_FATAL("The variable '{0}' is not defined on a group.", variable.name());
76
77 Int32 nb_item = unique_ids.size();
78 if (nb_item != values.size())
79 ARCANE_FATAL("The arrays 'unique_ids' and 'values' don't have the same "
80 "number of elements (respectively {0} and {1}).",
81 nb_item, values.size());
82
83 if (nb_item != sub_domain_ids.size())
84 ARCANE_FATAL("The arrays 'unique_ids' and 'sub_domains_ids' don't have the same "
85 "number of elements (respectively {0} and {1}).",
86 nb_item, sub_domain_ids.size());
87
88 using SubDomainUniqueIdMap = std::map<Int32, Helper>;
89 SubDomainUniqueIdMap sub_domain_list;
90
91 for (Integer i = 0; i < nb_item; ++i) {
92 Int32 sd = sub_domain_ids[i];
93 if (sd == NULL_SUB_DOMAIN_ID)
94 ARCANE_FATAL("Null SubDomainId for index {0}", i);
95 //TODO: Do not add elements from its own subdomain to the list
96 Helper& h = sub_domain_list[sd];
97 h.m_unique_ids.add(unique_ids[i]);
98 h.m_indexes.add(i);
99 }
100
101 UniqueArray<Int32> sub_domain_nb_to_send;
102 Int32 my_rank = pm->commRank();
103 for (auto& [sd, helper] : sub_domain_list) {
104 Integer n = helper.m_unique_ids.size();
105 sub_domain_nb_to_send.add(my_rank);
106 sub_domain_nb_to_send.add(sd);
107 sub_domain_nb_to_send.add(n);
108 }
109
110 UniqueArray<Int32> total_sub_domain_nb_to_send;
111 pm->allGatherVariable(sub_domain_nb_to_send, total_sub_domain_nb_to_send);
112 UniqueArray<Ref<ISerializeMessage>> messages;
113 Ref<ISerializeMessageList> message_list(pm->createSerializeMessageListRef());
114 for (Integer i = 0, is = total_sub_domain_nb_to_send.size(); i < is; i += 3) {
115 Int32 rank_send = total_sub_domain_nb_to_send[i];
116 Int32 rank_recv = total_sub_domain_nb_to_send[i + 1];
117 //Integer nb_exchange = total_sub_domain_nb_to_send[i+2];
118 //trace->info() << " SEND=" << rank_send
119 //<< " RECV= " << rank_recv
120 //<< " N= " << nb_exchange;
121 if (rank_send == rank_recv)
122 continue;
123 Ref<ISerializeMessage> sm;
124 if (rank_recv == my_rank) {
125 //trace->info() << " ADD RECV MESSAGE recv=" << rank_recv << " send=" << rank_send;
126 sm = message_list->createAndAddMessage(MessageRank(rank_send), ePointToPointMessageType::MsgReceive);
127 }
128 else if (rank_send == my_rank) {
129 //trace->info() << " ADD SEND MESSAGE recv=" << rank_recv << " send=" << rank_send;
130 sm = message_list->createAndAddMessage(MessageRank(rank_recv), ePointToPointMessageType::MsgSend);
131 ISerializer* s = sm->serializer();
132 s->setMode(ISerializer::ModeReserve);
133 auto xiter = sub_domain_list.find(rank_recv);
134 if (xiter == sub_domain_list.end())
135 ARCANE_FATAL("Can not find rank '{0}'", rank_recv);
136 Span<const Int64> z_unique_ids = xiter->second.m_unique_ids;
137 Int64 nb = z_unique_ids.size();
138 s->reserveInt64(1); // For size
139 s->reserveSpan(eBasicDataType::Int64, nb); // For the array
140 s->allocateBuffer();
141 s->setMode(ISerializer::ModePut);
142 s->putInt64(nb);
143 s->putSpan(z_unique_ids);
144 }
145 if (sm.get())
146 messages.add(sm);
147 }
148
149 message_list->waitMessages(eWaitType::WaitAll);
150
151 UniqueArray<Int64> tmp_unique_ids;
152 UniqueArray<Int32> tmp_local_ids;
153 UniqueArray<Real> tmp_values;
154
155 UniqueArray<Ref<ISerializeMessage>> values_messages;
156 ItemInfoListView items_internal(item_family);
157 for (Ref<ISerializeMessage> sm : messages) {
158 Ref<ISerializeMessage> new_sm;
159 if (sm->isSend()) {
160 // To receive the values
161 //trace->info() << " ADD RECV2 MESSAGE recv=" << my_rank << " send=" << sm->destSubDomain();
162 new_sm = message_list->createAndAddMessage(MessageRank(sm->destination().value()), ePointToPointMessageType::MsgReceive);
163 }
164 else {
165 ISerializer* s = sm->serializer();
166 s->setMode(ISerializer::ModeGet);
167 Int64 nb = s->getInt64();
168 tmp_unique_ids.resize(nb);
169 tmp_local_ids.resize(nb);
170 tmp_values.resize(nb);
171 s->getSpan(tmp_unique_ids);
172 item_family->itemsUniqueIdToLocalId(tmp_local_ids, tmp_unique_ids);
173 for (Integer z = 0; z < nb; ++z) {
174 Item item = items_internal[tmp_local_ids[z]];
175 tmp_values[z] = variable[item];
176 }
177
178 //trace->info() << " ADD SEND2 MESSAGE recv=" << my_rank << " send=" << sm->destSubDomain();
179 new_sm = message_list->createAndAddMessage(MessageRank(sm->destination().value()), ePointToPointMessageType::MsgSend);
180 ISerializer* s2 = new_sm->serializer();
181 s2->setMode(ISerializer::ModeReserve);
182 s2->reserveInt64(1);
183 s2->reserveSpan(eBasicDataType::Real, nb);
184 s2->allocateBuffer();
185 s2->setMode(ISerializer::ModePut);
186 s2->putInt64(nb);
187 s2->putSpan(tmp_values);
188 }
189 values_messages.add(new_sm);
190 }
191
192 // Delete messages that are no longer used
193 messages.clear();
194
195 message_list->waitMessages(eWaitType::WaitAll);
196
197 for (Ref<ISerializeMessage> sm : values_messages) {
198 if (sm->isSend()) {
199 }
200 else {
201 ISerializer* s = sm->serializer();
202 s->setMode(ISerializer::ModeGet);
203 Int64 nb = s->getInt64();
204 tmp_values.resize(nb);
205 Int32 sender = sm->destination().value();
206 s->getSpan(tmp_values);
207 //trace->info() << " GET VALUES from=" << sm->destSubDomain() << " n=" << nb;
208 Span<const Int32> indexes = sub_domain_list[sender].m_indexes;
209 for (Int64 z = 0; z < nb; ++z)
210 values[indexes[z]] = tmp_values[z];
211 }
212 }
213
214 // Finally, process its own elements
215 // TODO: PERFORM THIS PROCESSING WHILE WAITING FOR MESSAGES
216 {
217 Helper h(sub_domain_list[my_rank]);
218 Span<const Int32> indexes(h.m_indexes.constSpan());
219 Int64 nb = h.m_unique_ids.largeSize();
220 tmp_local_ids.resize(nb);
221 item_family->itemsUniqueIdToLocalId(tmp_local_ids, h.m_unique_ids);
222 for (Int64 z = 0; z < nb; ++z) {
223 Item item = items_internal[tmp_local_ids[z]];
224 values[indexes[z]] = variable[item];
225 }
226 }
227
228 // Delete messages that are no longer used
229 values_messages.clear();
230 //_deleteMessages(values_messages);
231
232#if 0
233 {
234 // To perform a small check
235 Integer nb_values = values.size();
236 RealUniqueArray ref_values(nb_values);
237 ref_values.fill(0.0);
238 getVariableValues(variable,unique_ids,ref_values);
239 bool has_error = false;
240 for( Integer i=0; i<nb_values; ++i ){
241 if (!math::isEqual(ref_values[i],values[i])){
242 trace->pinfo() << " Incorrect values ref=" << ref_values[i] << " v=" << values[i];
243 has_error = true;
244 }
245 }
246 if (has_error)
247 trace->fatal() << func_id << " incorrect values";
248 }
249#endif
250}
251
252/*---------------------------------------------------------------------------*/
253/*---------------------------------------------------------------------------*/
254
255void GetVariablesValuesParallelOperation::
256getVariableValues(VariableItemReal& variable, Int64ConstArrayView unique_ids,
257 RealArrayView values)
258{
259 IParallelMng* pm = m_parallel_mng;
260 Timer::Phase tphase(pm->timeStats(), TP_Communication);
261
262 if (!pm->isParallel()) {
263 _getVariableValuesSequential(variable, unique_ids, values);
264 return;
265 }
266
267 ItemGroup group = variable.itemGroup();
268 ITraceMng* trace = pm->traceMng();
269 if (group.null())
270 ARCANE_FATAL("The variable '{0}' is not defined on a group.", variable.name());
271
272 Int32 size = unique_ids.size();
273 if (size != values.size())
274 ARCANE_FATAL("The arrays 'unique_ids' and 'values' don't have the same "
275 "number of elements (respectively {0} and {1}).",
276 size, values.size());
277
278 Int32 nb_proc = pm->commSize();
279 Integer nb_phase = 0;
280 while (nb_proc != 0 && nb_phase < 32) {
281 nb_proc /= 2;
282 ++nb_phase;
283 }
284 if (nb_phase < 3)
285 nb_phase = 1;
286 trace->info() << " NB PHASE=" << nb_phase;
287 nb_phase = 1;
288 if (nb_phase == 1) {
289 _getVariableValues(variable, unique_ids, values);
290 }
291 else {
292 Integer nb_done = 0;
293 for (Integer i = 0; i < nb_phase; ++i) {
294 Integer first = (i * size) / nb_phase;
295 Integer last = ((i + 1) * size) / nb_phase;
296 if ((i + 1) == nb_phase)
297 last = size;
298 Integer n = last - first;
299 nb_done += n;
300 trace->debug() << "GetVariableValue: first=" << first << " last=" << last << " n=" << n
301 << " size=" << size;
302 RealArrayView local_values(n, values.data() + first);
303 Int64ConstArrayView local_unique_ids(n, unique_ids.data() + first);
304 _getVariableValues(variable, local_unique_ids, local_values);
305 }
306 if (nb_done != size) {
307 trace->fatal() << "MpiParallelMng::getVariableValue() Internal error in size: "
308 << " size=" << size << " done=" << nb_done;
309 }
310 }
311}
312
313/*---------------------------------------------------------------------------*/
314/*---------------------------------------------------------------------------*/
315
316template <class Type> void GetVariablesValuesParallelOperation::
317_getVariableValues(ItemVariableScalarRefT<Type>& variable,
318 Int64ConstArrayView unique_ids,
319 ArrayView<Type> values)
320{
321 IParallelMng* pm = m_parallel_mng;
322 ItemGroup group = variable.itemGroup();
323 ITraceMng* msg = pm->traceMng();
324 IItemFamily* item_family = group.itemFamily();
325
326 // To avoid an MPI bug on certain machines,
327 // if the list is empty, a temporary list is created
328 UniqueArray<Int64> dummy_unique_ids;
329 UniqueArray<Real> dummy_values;
330 if (unique_ids.empty()) {
331 dummy_unique_ids.resize(1);
332 dummy_values.resize(1);
333 dummy_unique_ids[0] = NULL_ITEM_ID;
334 unique_ids = dummy_unique_ids.view();
335 values = dummy_values.view();
336 }
337
338 // Operating principle.
339 // Each subdomain retrieves all unique_ids for which values are desired
340 // (allGatherVariable).
341 // Then an array is allocated, sized to this number of uniqueIds, which
342 // will hold the entity values (all_value array).
343 // Each subdomain fills this array as follows:
344 // * if the entity belongs to it, fill with the variable value
345 // * otherwise, fill with the minimum possible value according to \a Type.
346 // Processor 0 then performs a Max reduction on this array,
347 // which will then contain the correct value for each of its elements.
348 // All that remains is to perform a symmetric 'scatter' of the
349 // first 'gather'.
350
351 Int64UniqueArray all_unique_ids;
352 pm->allGatherVariable(unique_ids, all_unique_ids);
353 Integer all_size = all_unique_ids.size();
354 Int32UniqueArray all_local_ids(all_size);
355 item_family->itemsUniqueIdToLocalId(all_local_ids, all_unique_ids, false);
356
357 ConstArrayView<Type> variable_a(variable.asArray());
358 UniqueArray<Type> all_values(all_size);
359
360 msg->debug() << "MpiParallelMng::_getVariableValues(): size=" << all_size
361 << " values_size=" << sizeof(Type) * all_size;
362
363 // Fill the values array with the maximum possible value
364 // for the type. Then a ReduceMin is performed.
365 Type max_value = std::numeric_limits<Type>::max();
366 ItemInfoListView internal_items(item_family);
367
368 for (Integer i = 0; i < all_size; ++i) {
369 Integer lid = all_local_ids[i];
370 if (lid == NULL_ITEM_ID)
371 all_values[i] = max_value;
372 else {
373 all_values[i] = (internal_items[lid].isOwn()) ? variable_a[lid] : max_value;
374 }
375 }
376
377 pm->reduce(Parallel::ReduceMin, all_values);
378
379 // Split the array across the other processors
380 pm->scatterVariable(all_values, values, 0);
381}
382
383/*---------------------------------------------------------------------------*/
384/*---------------------------------------------------------------------------*/
385
386template <class Type> void GetVariablesValuesParallelOperation::
387_getVariableValuesSequential(ItemVariableScalarRefT<Type>& variable,
388 Int64ConstArrayView unique_ids,
389 ArrayView<Type> values)
390{
391 ItemGroup group = variable.itemGroup();
392 if (group.null())
393 ARCANE_FATAL("The variable '{0}' is not defined on a group.", variable.name());
394
395 IItemFamily* family = group.itemFamily();
396 Int32 size = unique_ids.size();
397 if (size != values.size())
398 ARCANE_FATAL("The arrays 'unique_ids' and 'values' don't have the same "
399 "number of elements (respectively {0} and {1}).",
400 size, values.size());
401
402 //TODO: do in chunks.
403 UniqueArray<Int32> local_ids(size);
404 family->itemsUniqueIdToLocalId(local_ids, unique_ids);
405 ConstArrayView<Type> variable_a(variable.asArray());
406 for (Integer i = 0; i < size; ++i)
407 values[i] = variable_a[local_ids[i]];
408}
409
410/*---------------------------------------------------------------------------*/
411/*---------------------------------------------------------------------------*/
412
413} // namespace Arcane
414
415/*---------------------------------------------------------------------------*/
416/*---------------------------------------------------------------------------*/
#define ARCANE_FATAL(...)
Macro throwing a FatalErrorException.
Modifiable view of an array of type T.
constexpr const_pointer data() const noexcept
Pointer to the start of the view.
constexpr Integer size() const noexcept
Returns the size of the array.
void resize(Int64 s)
Changes the number of elements in the array to s.
ArrayView< T > view() const
Mutable view of this array.
constexpr const_pointer data() const noexcept
Pointer to the allocated memory.
constexpr Integer size() const noexcept
Number of elements in the array.
constexpr bool empty() const noexcept
true if the array is empty (size()==0)
Interface of an entity family.
Definition IItemFamily.h:83
virtual void itemsUniqueIdToLocalId(Int32ArrayView local_ids, Int64ConstArrayView unique_ids, bool do_fatal=true) const =0
Converts an array of unique numbers to local numbers.
Interface of the parallelism manager for a subdomain.
virtual ITraceMng * traceMng() const =0
Trace manager.
virtual ITimeStats * timeStats() const =0
Associated statistics manager (can be null).
virtual void allGatherVariable(ConstArrayView< char > send_buf, Array< char > &recv_buf)=0
Performs an all-gather operation across all processors.
virtual Int32 commSize() const =0
Number of instances in the communicator.
virtual void scatterVariable(ConstArrayView< char > send_buf, ArrayView< char > recv_buf, Integer root)=0
Scatters an array across multiple processors.
virtual bool isParallel() const =0
Returns true if the execution is parallel.
virtual char reduce(eReduceType rt, char v)=0
Performs a reduction of type rt on the real v and returns the value.
virtual TraceMessage fatal()=0
Stream for a fatal error message.
virtual TraceMessage info()=0
Stream for an information message.
virtual TraceMessageDbg debug(Trace::eDebugLevel=Trace::Medium)=0
Stream for a debug message.
Mesh entity group.
Definition ItemGroup.h:51
IItemFamily * itemFamily() const
Entity family to which this group belongs (0 for the null group).
Definition ItemGroup.h:128
bool null() const
true means the group is the null group
Definition ItemGroup.h:75
Scalar variable on a mesh entity type.
Positions the phase of the currently executing action.
Definition Timer.h:142
1D data vector with value semantics (STL style).
String name() const
Variable name.
ItemVariableScalarRefT< Real > VariableItemReal
Real type quantity.
Declarations of types and methods used by message exchange mechanisms.
-- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature --
Int32 Integer
Type representing an integer.
ConstArrayView< Int64 > Int64ConstArrayView
C equivalent of a 1D array of 64-bit integers.
Definition UtilsTypes.h:480
UniqueArray< Real > RealUniqueArray
Dynamic 1D array of reals.
Definition UtilsTypes.h:349
ArrayView< Real > RealArrayView
C equivalent of a 1D array of reals.
Definition UtilsTypes.h:459
std::int32_t Int32
Signed integer type of 32 bits.
Type
Type of JSON value.
Definition rapidjson.h:730