Arcane  4.1.11.0
Documentation développeur
Chargement...
Recherche...
Aucune correspondance
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 meshes because if there are no meshes
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 meshes 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 mesh exchange
343 // This is guaranteed by the fact that the first element of family_exchangers
344 // is the mesh 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 envoyant une exception FatalErrorException.
#define ENUMERATE_ITEM(name, group)
Enumérateur générique d'un groupe de noeuds.
Échange des entités et leurs caractéristiques pour une famille donnée.
virtual bool computeExchangeInfos()=0
Détermine les informations nécessaires pour les échanges.
virtual void setExchangeItems(ConstArrayView< std::set< Int32 > > items_to_send)=0
Positionne la liste des entités à échanger.
Interface d'une famille d'entités.
Definition IItemFamily.h:84
virtual IParticleFamily * toParticleFamily()=0
Retourne l'interface de la famille de particule de cette famille.
virtual ItemGroup allItems() const =0
Groupe de toutes les entités.
virtual String name() const =0
Nom de la famille.
virtual void notifyItemsOwnerChanged()=0
Notifie que les entités propres au sous-domaine de la famille ont été modifiées.
virtual VariableItemInt32 & itemsNewOwner()=0
Variable contenant le numéro du nouveau sous-domaine propriétaire de l'entité.
virtual IPrimaryMesh * toPrimaryMesh()=0
Retourne l'instance sous la forme d'un IPrimaryMesh.
Interface d'une famille de particules.
virtual bool getEnableGhostItems() const =0
récupère le flag pour gérer les particules ghost de la famille
virtual IItemFamily * itemFamily()=0
Interface sur la famille.
Classe de base d'un élément de maillage.
Definition Item.h:83
impl::MutableItemBase mutableItemBase() const
Partie interne modifiable de l'entité.
Definition Item.h:380
void setOwner(Integer suid, Int32 current_sub_domain)
Positionne le numéro du sous-domaine propriétaire de l'entité.
Postionne le nom de l'action en cours d'exécution.
Definition Timer.h:110
TraceMessage info() const
Flot pour un message d'information.
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
Échangeur associé à la famille family. Lance une exception si non trouvé
ItemVariableScalarRefT< Int32 > VariableItemInt32
Grandeur de type entier 32 bits.
double getMemoryUsed()
Mémoire utilisée em octets.
String getCurrentDateTime()
Date et l'heure courante sous la forme ISO 8601.
Int32 Integer
Type représentant un entier.
Collection< IItemFamily * > IItemFamilyCollection
Collection de familles d'entités.
std::int32_t Int32
Type entier signé sur 32 bits.