Arcane  4.1.12.0
Developer documentation
Loading...
Searching...
No Matches
MeshExchanger.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/* MeshExchanger.cc (C) 2000-2025 */
9/* */
10/* Management of a mesh exchange between sub-domains. */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arcane/utils/FatalErrorException.h"
15#include "arcane/utils/TraceInfo.h"
16#include "arcane/utils/PlatformUtils.h"
17#include "arcane/utils/ValueConvert.h"
18
19#include "arcane/core/IParallelMng.h"
20#include "arcane/core/Timer.h"
21#include "arcane/core/IItemFamilyPolicyMng.h"
22#include "arcane/core/IItemFamilyExchanger.h"
23#include "arcane/core/IParticleFamily.h"
24
25#include "arcane/mesh/MeshExchanger.h"
26#include "arcane/mesh/DynamicMesh.h"
27#include "arcane/mesh/MeshExchange.h"
28#include "arcane/core/internal/IMeshModifierInternal.h"
29#include "arcane/core/internal/IItemFamilySerializerMngInternal.h"
30#include "arcane/core/internal/IMeshInternal.h"
31
32/*---------------------------------------------------------------------------*/
33/*---------------------------------------------------------------------------*/
34
35namespace Arcane::mesh
36{
37
38/*---------------------------------------------------------------------------*/
39/*---------------------------------------------------------------------------*/
40
41MeshExchanger::
42MeshExchanger(IMesh* mesh, ITimeStats* stats)
43: TraceAccessor(mesh->traceMng())
44, m_mesh(mesh)
45, m_time_stats(stats)
46, m_phase(ePhase::Init)
47{
48 // Temporarily uses an environment variable to specify the
49 // maximum number of pending messages or if collective operations should be used
50 String max_pending_str = platform::getEnvironmentVariable("ARCANE_MESH_EXCHANGE_MAX_PENDING_MESSAGE");
51 if (!max_pending_str.null()) {
52 Int32 max_pending = 0;
53 if (!builtInGetValue(max_pending, max_pending_str))
54 m_exchanger_option.setMaxPendingMessage(max_pending);
55 }
56
57 String use_collective_str = platform::getEnvironmentVariable("ARCANE_MESH_EXCHANGE_USE_COLLECTIVE");
58 if (use_collective_str == "1" || use_collective_str == "TRUE")
59 m_exchanger_option.setExchangeMode(ParallelExchangerOptions::EM_Collective);
60
61 m_exchanger_option.setVerbosityLevel(1);
62}
63
64/*---------------------------------------------------------------------------*/
65/*---------------------------------------------------------------------------*/
66
67MeshExchanger::
68~MeshExchanger()
69{
70 for (IItemFamilyExchanger* exchanger : m_family_exchangers)
71 delete exchanger;
72}
73
74/*---------------------------------------------------------------------------*/
75/*---------------------------------------------------------------------------*/
76
77/*---------------------------------------------------------------------------*/
78/*---------------------------------------------------------------------------*/
79
80void MeshExchanger::
81build()
82{
83 if (!m_mesh->itemFamilyNetwork() || !IItemFamilyNetwork::plug_serializer) { // handle family order by hand
84 // Sorted list of families specifically ordered to guarantee a certain order
85 // during exchanges. For now, the order is determined as follows:
86 // - first Cell, then Face, Edge, and Node
87 // - then, Particle families must be handled before DualNode families.
88 UniqueArray<IItemFamily*> sorted_families;
89 IItemFamilyCollection families(m_mesh->itemFamilies());
90 sorted_families.reserve(families.count());
91 sorted_families.add(m_mesh->cellFamily());
92 sorted_families.add(m_mesh->faceFamily());
93 sorted_families.add(m_mesh->edgeFamily());
94 sorted_families.add(m_mesh->nodeFamily());
95 for (IItemFamily* family : families) {
96 IParticleFamily* particle_family = family->toParticleFamily();
97 if (particle_family)
98 sorted_families.add(family);
99 }
100
101 // List of instances managing the exchange of a family.
102 // WARNING: It is necessary to ensure the associated pointers are released.
103 //m_family_exchangers.reserve(families.count());
104
105 // Creation of each exchanger associated with a family.
106 std::map<IItemFamily*, IItemFamilyExchanger*> family_exchanger_map;
107 for (IItemFamily* family : sorted_families) {
108 _addItemFamilyExchanger(family);
109 }
110 }
111 else {
112 if (m_mesh->useMeshItemFamilyDependencies()) {
113 _buildWithItemFamilyNetwork();
114 }
115 else {
116 std::set<String> family_set;
117 UniqueArray<IItemFamily*> sorted_families;
118 IItemFamilyCollection families(m_mesh->itemFamilies());
119 sorted_families.reserve(families.count());
120 sorted_families.add(m_mesh->cellFamily());
121 family_set.insert(m_mesh->cellFamily()->name());
122 sorted_families.add(m_mesh->faceFamily());
123 family_set.insert(m_mesh->faceFamily()->name());
124 sorted_families.add(m_mesh->edgeFamily());
125 family_set.insert(m_mesh->edgeFamily()->name());
126 sorted_families.add(m_mesh->nodeFamily());
127 family_set.insert(m_mesh->nodeFamily()->name());
128 for (IItemFamily* family : families) {
129 IParticleFamily* particle_family = family->toParticleFamily();
130 if (particle_family) {
131 sorted_families.add(family);
132 family_set.insert(family->name());
133 }
134 }
135
136 for (auto family : m_mesh->itemFamilyNetwork()->getFamilies(IItemFamilyNetwork::InverseTopologicalOrder)) {
137 auto value = family_set.insert(family->name());
138 if (value.second) {
139 sorted_families.add(family);
140 }
141 }
142
143 // List of instances managing the exchange of a family.
144 // WARNING: It is necessary to ensure the associated pointers are released.
145 //m_family_exchangers.reserve(families.count());
146
147 // Creation of each exchanger associated with a family.
148 std::map<IItemFamily*, IItemFamilyExchanger*> family_exchanger_map;
149 for (IItemFamily* family : sorted_families) {
150 _addItemFamilyExchanger(family);
151 }
152 }
153 }
154 m_phase = ePhase::ComputeInfos;
155}
156
157/*---------------------------------------------------------------------------*/
158/*---------------------------------------------------------------------------*/
159
160void MeshExchanger::
161_buildWithItemFamilyNetwork()
162{
163 m_mesh->itemFamilyNetwork()->schedule([&](IItemFamily* family) {
164 _addItemFamilyExchanger(family);
165 },
166 IItemFamilyNetwork::InverseTopologicalOrder);
167 // Particle should be handled soon
168 for (IItemFamily* family : m_mesh->itemFamilies()) {
169 IParticleFamily* particle_family = family->toParticleFamily();
170 if (particle_family)
171 _addItemFamilyExchanger(family);
172 }
173}
174
175/*---------------------------------------------------------------------------*/
176/*---------------------------------------------------------------------------*/
177
178void MeshExchanger::
179_addItemFamilyExchanger(IItemFamily* family)
180{
181 IItemFamilyExchanger* exchanger = family->policyMng()->createExchanger();
182 m_family_exchangers.add(exchanger);
183 m_family_exchanger_map.insert(std::make_pair(family, exchanger));
184 exchanger->setParallelExchangerOption(m_exchanger_option);
185}
186
187/*---------------------------------------------------------------------------*/
188/*---------------------------------------------------------------------------*/
189
190void MeshExchanger::
191_checkPhase(ePhase wanted_phase)
192{
193 if (m_phase != wanted_phase)
194 ARCANE_FATAL("Invalid exchange phase wanted={0} current={1}",
195 (int)wanted_phase, (int)m_phase);
196}
197
198/*---------------------------------------------------------------------------*/
199/*---------------------------------------------------------------------------*/
200
201bool MeshExchanger::
202computeExchangeInfos()
203{
204 _checkPhase(ePhase::ComputeInfos);
205
206 // TODO: make it possible to customize the exchange calculation
207 // and do it by family if possible.
208 MeshExchange mesh_exchange(m_mesh);
209 info() << "MeshExchange begin date=" << platform::getCurrentDateTime();
210 {
211 Timer::Action ts_action1(m_time_stats, "MeshExchangeComputeInfos", true);
212 mesh_exchange.computeInfos();
213 // MeshExchange has set a NeedRemove mark on cells leaving this proc completely
214 }
215
216 IItemFamily* cell_family = m_mesh->cellFamily();
217 IItemFamilyExchanger* cell_exchanger = findExchanger(cell_family);
218
219 // First determine the info to exchange on the cells because if there are no cells
220 // to exchange, the partitioning stops.
221 // NOTE GG: we need to see if this remains true with links and dual nodes.
222 cell_exchanger->setExchangeItems(mesh_exchange.getItemsToSend(cell_family));
223 if (cell_exchanger->computeExchangeInfos()) {
224 pwarning() << "No load balance is performed";
225 return true;
226 }
227
228 // Determine the list of information to exchange for each family.
229 for (IItemFamilyExchanger* exchanger : m_family_exchangers) {
230 // The mesh exchange has already been done.
231 if (exchanger == cell_exchanger)
232 continue;
233 IItemFamily* family = exchanger->itemFamily();
234 info() << "ComputeExchange family=" << family->name()
235 << " date=" << platform::getCurrentDateTime();
236 // For particle families that do not support the notion
237 // of ghost items, it is necessary to explicitly determine the list of entities to exchange
238 // via the call to computeExchangeItems().
239 // For other families where particle families do have the notion
240 // of ghost items, this list has already been determined during the call to
241 // mesh_exchange.computeInfos().
242 IParticleFamily* particle_family = family->toParticleFamily();
243 if (particle_family && particle_family->getEnableGhostItems() == false)
244 exchanger->computeExchangeItems();
245 else
246 exchanger->setExchangeItems(mesh_exchange.getItemsToSend(family));
247 exchanger->computeExchangeInfos();
248 }
249
250 // Copy the owner() field into the ItemInternal so that it
251 // is consistent with the corresponding variable
252
253 // WARNING: It is absolutely necessary that the owner() of the ItemInternal
254 // are correct before sending the cells that belonged to us
255 // to the sub-domains to which they will then belong.
256
257 // Note that we cannot merge this loop with the previous one because
258 // the families need the info from other families to determine
259 // the list of entities to send.
260 Int32 rank = m_mesh->meshPartInfo().partRank();
261 for (IItemFamilyExchanger* exchanger : m_family_exchangers) {
262 IItemFamily* family = exchanger->itemFamily();
263 VariableItemInt32& owners(family->itemsNewOwner());
264 ENUMERATE_ITEM (i, family->allItems()) {
265 Item item = *i;
266 Integer new_owner = owners[item];
267 item.mutableItemBase().setOwner(new_owner, rank);
268 }
269 family->notifyItemsOwnerChanged();
270 }
271
272 m_phase = ePhase::ProcessExchange;
273
274 return false;
275}
276
277/*---------------------------------------------------------------------------*/
278/*---------------------------------------------------------------------------*/
279
280void MeshExchanger::
281processExchange()
282{
283 _checkPhase(ePhase::ProcessExchange);
284
285 info() << "ExchangeItems date=" << platform::getCurrentDateTime()
286 << " MemUsed=" << platform::getMemoryUsed();
287
288 Timer::Action ts_action1(m_time_stats, "MessagesExchange", true);
289 for (IItemFamilyExchanger* e : m_family_exchangers) {
290 // NOTE: To be able to send all messages at once and receive
291 // them as well, it might be necessary to plan using MPI tags.
292 e->prepareToSend(); // Preparation of all data to send then serialization
293 e->processExchange(); // Actual sending
294 e->releaseBuffer();
295 }
296 m_phase = ePhase::RemoveItems;
297}
298
299/*---------------------------------------------------------------------------*/
300/*---------------------------------------------------------------------------*/
301
302void MeshExchanger::
303removeNeededItems()
304{
305 _checkPhase(ePhase::RemoveItems);
306
307 // Now that all messages with the mesh before modification
308 // have been sent and received, we can modify this mesh by
309 // removing the elements that no longer belong to it and adding
310 // the new ones.
311
312 // TODO: implement removal by family method.
313
314 // For families other than particle families without ghosts, this
315 // is done in the DynamicMeshIncrementalBuilder.
316 // For particle families without ghosts, this is done here.
317 info() << "RemoveItems date=" << platform::getCurrentDateTime();
318 Timer::Action ts_action1(m_time_stats, "RemoveSendedItems", true);
319
320 for (IItemFamilyExchanger* exchanger : m_family_exchangers) {
321 IParticleFamily* particle_family = exchanger->itemFamily()->toParticleFamily();
322 if (particle_family && particle_family->getEnableGhostItems() == false)
323 exchanger->removeSentItems(); // integrates the treatment of sub-meshes (for particles)
324 }
325
326 // Remove entities that are no longer linked to the sub-domain
327 m_mesh->modifier()->_modifierInternalApi()->removeNeedRemoveMarkedItems();
328
329 m_phase = ePhase::AllocateItems;
330}
331
332/*---------------------------------------------------------------------------*/
333/*---------------------------------------------------------------------------*/
334
335void MeshExchanger::
336allocateReceivedItems()
337{
338 _checkPhase(ePhase::AllocateItems);
339 {
340 info() << "AllocItems date=" << platform::getCurrentDateTime();
341 Timer::Action ts_action1(m_time_stats, "ReadAndAllocItems", true);
342 // We must first perform the cell exchange
343 // This is guaranteed by the fact that the first element of family_exchangers
344 // is the cell family.
345 for (IItemFamilyExchanger* e : m_family_exchangers) {
346 e->readAndAllocItems(); // Caution, no longer proceeds on different sub-meshes
347 }
348 // If needed, finalize item allocations (for polyhedral meshes)
349 auto* family_serializer_mng = m_mesh->_internalApi()->familySerializerMng();
350 if (family_serializer_mng)
351 family_serializer_mng->finalizeItemAllocation();
352
353 // Build item relations (only dependencies are built in readAndAllocItems)
354 // only for families registered in the graph
355 if (m_mesh->itemFamilyNetwork() && m_mesh->itemFamilyNetwork()->isActivated()) {
356 auto family_set = m_mesh->itemFamilyNetwork()->getFamilies();
357 for (auto family : family_set) {
358 m_family_exchanger_map[family]->readAndAllocItemRelations();
359 }
360 }
361
362 // Separate mesh and submesh
363 for (IItemFamilyExchanger* e : m_family_exchangers) {
364 e->readAndAllocSubMeshItems(); // Proceeds on different sub-meshes
365 }
366 }
367
368 // It is possible that the owners of the entities have changed
369 // following readAndAllocItems() even if no entity was added.
370 // Therefore, it must be indicated to the families.
371 for (IItemFamilyExchanger* e : m_family_exchangers) {
372 e->itemFamily()->notifyItemsOwnerChanged(); // applied up to a sub-mesh level
373 }
374
375 m_phase = ePhase::UpdateItemGroups;
376}
377
378/*---------------------------------------------------------------------------*/
379/*---------------------------------------------------------------------------*/
380
381void MeshExchanger::
382updateItemGroups()
383{
384 _checkPhase(ePhase::UpdateItemGroups);
385
386 info() << "ReadGroups date=" << platform::getCurrentDateTime();
387 // Now that the new mesh is created, we read the groups
388 for (IItemFamilyExchanger* e : m_family_exchangers)
389 e->readGroups();
390
391 m_phase = ePhase::UpdateVariables;
392}
393
394/*---------------------------------------------------------------------------*/
395/*---------------------------------------------------------------------------*/
396
397void MeshExchanger::
398updateVariables()
399{
400 _checkPhase(ePhase::UpdateVariables);
401
402 info() << "ReadVariables date=" << platform::getCurrentDateTime();
403 Timer::Action ts(m_time_stats, "ReadVariables", true);
404 // Now that the entities are created and the groups updated,
405 // we can update the variables.
406 for (IItemFamilyExchanger* e : m_family_exchangers)
407 e->readVariables();
408
409 m_phase = ePhase::Finalize;
410}
411
412/*---------------------------------------------------------------------------*/
413/*---------------------------------------------------------------------------*/
414
415void MeshExchanger::
416finalizeExchange()
417{
418 _checkPhase(ePhase::Finalize);
419
420 // Finalize the exchanges
421 // This must be done after compaction because in the case of linked interfaces,
422 // the localId() numbering must not be changed once the
423 // TiedInterface structures are updated.
424 // TODO: this will need to be removed by doing this treatment before, but
425 // for that, TiedInterfaceMng must be notified of the compaction to
426 // update the localId() of its faces and nodes
427 for (IItemFamilyExchanger* e : m_family_exchangers)
428 e->finalizeExchange();
429
430 m_phase = ePhase::Ended;
431}
432
433/*---------------------------------------------------------------------------*/
434/*---------------------------------------------------------------------------*/
435
436IItemFamilyExchanger* MeshExchanger::
437findExchanger(IItemFamily* family)
438{
439 auto x = m_family_exchanger_map.find(family);
440 if (x == m_family_exchanger_map.end())
441 ARCANE_FATAL("No exchanger for family name={0}", family->name());
442 return x->second;
443}
444
445/*---------------------------------------------------------------------------*/
446/*---------------------------------------------------------------------------*/
447
448IPrimaryMesh* MeshExchanger::
449mesh() const
450{
451 return m_mesh->toPrimaryMesh();
452}
453
454/*---------------------------------------------------------------------------*/
455/*---------------------------------------------------------------------------*/
456
457void MeshExchanger::
458_setNextPhase(ePhase next_phase)
459{
460 m_phase = next_phase;
461}
462
463/*---------------------------------------------------------------------------*/
464/*---------------------------------------------------------------------------*/
465
466} // End namespace Arcane::mesh
467
468/*---------------------------------------------------------------------------*/
469/*---------------------------------------------------------------------------*/
#define ARCANE_FATAL(...)
Macro throwing a FatalErrorException.
#define ENUMERATE_ITEM(name, group)
Generic enumerator for a node group.
Exchange of entities and their characteristics for a given family.
virtual bool computeExchangeInfos()=0
Determines the information necessary for the exchanges.
virtual void setExchangeItems(ConstArrayView< std::set< Int32 > > items_to_send)=0
Positions the list of entities to exchange.
Interface of an entity family.
Definition IItemFamily.h:83
virtual IParticleFamily * toParticleFamily()=0
Returns the interface of the particle family for this family.
virtual ItemGroup allItems() const =0
Group of all entities.
virtual String name() const =0
Family name.
virtual void notifyItemsOwnerChanged()=0
Notifies that the entities specific to the family's subdomain have been modified.
virtual VariableItemInt32 & itemsNewOwner()=0
Variable containing the number of the new subdomain owning the entity.
virtual IPrimaryMesh * toPrimaryMesh()=0
Returns the instance in the form of an IPrimaryMesh.
Interface of a particle family.
virtual bool getEnableGhostItems() const =0
Retrieves the flag to manage ghost particles for the family.
virtual IItemFamily * itemFamily()=0
Interface on the family.
Base class for a mesh element.
Definition Item.h:84
impl::MutableItemBase mutableItemBase() const
Mutable internal part of the entity.
Definition Item.h:394
void setOwner(Integer suid, Int32 current_sub_domain)
Sets the sub-domain number of the entity owner.
Positions the name of the currently executing action.
Definition Timer.h:119
TraceMessage info() const
Flow for an information message.
TraceMessage pwarning() const
Exchange of mesh entities between subdomains.
ConstArrayView< std::set< Int32 > > getItemsToSend(IItemFamily *family) const
List of entities to send by subdomain for the family family.
void computeInfos()
Compute the info.
IItemFamilyExchanger * findExchanger(IItemFamily *family) override
Exchanger associated with the family. Throws an exception if not found.
ItemVariableScalarRefT< Int32 > VariableItemInt32
32-bit integer type quantity
double getMemoryUsed()
Memory used in bytes.
String getCurrentDateTime()
Current date and time in ISO 8601 format.
Int32 Integer
Type representing an integer.
Collection< IItemFamily * > IItemFamilyCollection
Collection of item families.
std::int32_t Int32
Signed integer type of 32 bits.