Arcane  4.1.12.0
Developer documentation
Loading...
Searching...
No Matches
DynamicMeshMerger.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/* DynamicMeshMerger.cc (C) 2000-2025 */
9/* */
10/* Merging multiple meshes. */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arcane/mesh/DynamicMeshMerger.h"
15#include "arcane/mesh/DynamicMesh.h"
16
17#include "arcane/utils/ITraceMng.h"
18#include "arcane/utils/ScopedPtr.h"
19
20#include "arcane/core/IParallelSuperMng.h"
21#include "arcane/core/IParallelMng.h"
22#include "arcane/core/ServiceBuilder.h"
23#include "arcane/core/IArcaneMain.h"
24#include "arcane/core/IMainFactory.h"
25
26#include "arcane/mesh/MeshExchanger.h"
27#include "arcane/mesh/MeshExchangeMng.h"
28#include "arcane/mesh/MeshExchanger.h"
29#include "arcane/core/IMeshExchanger.h"
30#include "arcane/core/IMeshExchangeMng.h"
31#include "arcane/core/IItemFamilyExchanger.h"
32#include "arcane/core/ItemPrinter.h"
33#include "arcane/core/MeshVisitor.h"
34
35#include "arccore/base/ReferenceCounter.h"
36
37#include <thread>
38
39/*---------------------------------------------------------------------------*/
40/*---------------------------------------------------------------------------*/
41
42namespace Arcane::mesh
43{
44
45/*---------------------------------------------------------------------------*/
46/*---------------------------------------------------------------------------*/
47
48class DynamicMeshMergerHelper
49: public TraceAccessor
50{
51 public:
52
53 DynamicMeshMergerHelper(ITraceMng* tm, DynamicMesh* mesh,
54 IParallelMng* pm, Int32 local_rank)
55 : TraceAccessor(tm)
56 , m_mesh(mesh)
57 , m_parallel_mng(pm)
58 , m_local_rank(local_rank)
59 {}
60
61 public:
62
63 void doMerge();
64
65 private:
66
67 DynamicMesh* m_mesh;
68 IParallelMng* m_parallel_mng;
69 Int32 m_local_rank;
70};
71
72/*---------------------------------------------------------------------------*/
73/*---------------------------------------------------------------------------*/
74
75DynamicMeshMerger::
76DynamicMeshMerger(DynamicMesh* mesh)
77: TraceAccessor(mesh->traceMng())
78, m_mesh(mesh)
79{
80}
81
82/*---------------------------------------------------------------------------*/
83/*---------------------------------------------------------------------------*/
84
85DynamicMeshMerger::
86~DynamicMeshMerger()
87{
88}
89
90/*---------------------------------------------------------------------------*/
91/*---------------------------------------------------------------------------*/
92
93namespace
94{
95 class LauncherThreadInfo
96 {
97 public:
98
99 LauncherThreadInfo()
100 : m_mesh(nullptr)
101 , m_local_rank(-1)
102 , m_has_error(false)
103 {}
104
105 public:
106
107 DynamicMesh* m_mesh;
108 Int32 m_local_rank;
109 bool m_has_error;
110 ReferenceCounter<ITraceMng> m_trace_mng;
111 Ref<IParallelMng> m_parallel_mng;
112 };
113
114 void
115 _doLaunch(LauncherThreadInfo* lti)
116 {
117 Int32 local_rank = lti->m_local_rank;
118 IParallelMng* pm = lti->m_parallel_mng.get();
119 ITraceMng* tm = lti->m_trace_mng.get();
120 pm->barrier();
121 tm->info() << "BEGIN LAUNCHER rank=" << local_rank << "!\n";
122 pm->barrier();
123 DynamicMeshMergerHelper helper(lti->m_trace_mng.get(), lti->m_mesh, pm, local_rank);
124 helper.doMerge();
125 tm->info() << "END EXCHANGE!\n";
126 pm->barrier();
127 }
128
129 void
130 _Launcher(LauncherThreadInfo* lti)
131 {
132 ITraceMng* tm = lti->m_trace_mng.get();
133 try {
134 _doLaunch(lti);
135 }
136 catch (const Exception& ex) {
137 tm->info() << "FATAL: " << tm->traceId()
138 << " ex=" << ex
139 << " stack=" << ex.stackTrace();
140 lti->m_has_error = true;
141 throw;
142 }
143 catch (const std::exception& ex) {
144 tm->info() << "FATAL: " << ex.what();
145 lti->m_has_error = true;
146 throw;
147 }
148 catch (...) {
149 tm->info() << "UNKNOWN FATAL";
150 lti->m_has_error = true;
151 throw;
152 }
153 }
154 class MergeMeshExchanger
155 : public MeshExchanger
156 {
157 public:
158
159 // TODO: implement own timeStats()
160 MergeMeshExchanger(DynamicMesh* mesh, Int32 local_rank)
161 : MeshExchanger(mesh, mesh->subDomain()->timeStats())
162 , m_local_rank(local_rank)
163 {}
164
165 public:
166
173 bool computeExchangeInfos() override
174 {
175 mesh()->traceMng()->info() << "OVERRIDE COMPUTE EXCHANGE INFOS";
176 // If I am not the local rank 0, I give all my entities.
177 UniqueArray<std::set<Int32>> items_to_send;
178 Int32 nb_rank = mesh()->parallelMng()->commSize();
179 items_to_send.resize(nb_rank);
180
181 for (IItemFamily* family : mesh()->itemFamilies()) {
182 IItemFamilyExchanger* family_exchanger = this->findExchanger(family);
183 if (m_local_rank != 0) {
184 items_to_send[0].clear();
185 ItemGroup all_items = family->allItems();
186 // Change the owners to match the new decomposition.
187 ENUMERATE_ITEM (iitem, all_items) {
188 Item ii = *iitem;
189 items_to_send[0].insert(iitem.itemLocalId());
190 // The transferred entities will be deleted.
191 // NOTE: it would be possible to make this optional if we
192 // do not want to modify the mesh being merged.
193 ii.mutableItemBase().addFlags(ItemFlags::II_NeedRemove);
194 }
195 family_exchanger->setExchangeItems(items_to_send);
196 }
197 family_exchanger->computeExchangeInfos();
198 }
199
200 _setNextPhase(ePhase::ProcessExchange);
201 return false;
202 }
203
204 private:
205
206 Int32 m_local_rank;
207 };
208
209 class MergerExchangeMng
210 : public MeshExchangeMng
211 {
212 public:
213
214 MergerExchangeMng(DynamicMesh* mesh, Int32 local_rank)
215 : MeshExchangeMng(mesh)
216 , m_mesh(mesh)
217 , m_local_rank(local_rank)
218 {}
219
220 protected:
221
222 IMeshExchanger* _createExchanger() override
223 {
224 m_mesh->traceMng()->info() << "CREATE MERGE MESH_EXCHANGER";
225 MeshExchanger* ex = new MergeMeshExchanger(m_mesh, m_local_rank);
226 ex->build();
227 return ex;
228 }
229
230 private:
231
232 DynamicMesh* m_mesh;
233 Int32 m_local_rank;
234 };
235} // namespace
236
237/*---------------------------------------------------------------------------*/
238/*---------------------------------------------------------------------------*/
239
240void DynamicMeshMergerHelper::
241doMerge()
242{
243 // TODO: this routine is similar to DynamicMesh::_exchangeItemsNew()
244 // and the two should be merged. This will be easier when the
245 // old connectivities have disappeared.
246
247 // Temporarily override the IParallelMng
248 // TODO: Check that it is restored to the correct value in case
249 // of an exception
250
251 IParallelMng* old_parallel_mng = m_mesh->m_parallel_mng;
252 m_mesh->m_parallel_mng = this->m_parallel_mng;
253
254 // TODO: Check that everyone has the same families and in the same order.
255 info() << "DOING MERGE pm_rank=" << m_mesh->parallelMng()->commRank()
256 << " sd_part=" << m_mesh->meshPartInfo().partRank();
257
258 // Cascade all meshes associated with this mesh
259 typedef Collection<DynamicMesh*> DynamicMeshCollection;
260 DynamicMeshCollection all_cascade_meshes = List<DynamicMesh*>();
261 all_cascade_meshes.add(m_mesh);
262 for (Integer i = 0; i < m_mesh->m_child_meshes.size(); ++i)
263 all_cascade_meshes.add(m_mesh->m_child_meshes[i]);
264
265 MergerExchangeMng exchange_mng(m_mesh, m_local_rank);
266 IMeshExchanger* iexchanger = exchange_mng.beginExchange();
267 IMeshExchanger* mesh_exchanger = iexchanger;
268
269 // If there are no entities to exchange, stop the exchange immediately.
270 if (mesh_exchanger->computeExchangeInfos()) {
271 pwarning() << "No load balance is performed";
272 exchange_mng.endExchange();
273 m_mesh->m_parallel_mng = old_parallel_mng;
274 return;
275 }
276
277 // Perform the info exchange
278 mesh_exchanger->processExchange();
279
280 // Delete entities that should no longer be in our sub-domain.
281 mesh_exchanger->removeNeededItems();
282
283 // Readjust the groups by deleting entities that are no longer in the mesh or by
284 // invalidating calculated groups.
285 // TODO: make a family method that does this.
286 {
287 auto action = [](ItemGroup& group) {
288 if (group.internal()->hasComputeFunctor() || group.isLocalToSubDomain())
289 group.invalidate();
290 else
291 group.internal()->removeSuppressedItems();
292 };
293 for (DynamicMesh* mesh : all_cascade_meshes) {
294 meshvisitor::visitGroups(mesh, action);
295 }
296 }
297
298 // Create the entities received from other sub-domains.
299 mesh_exchanger->allocateReceivedItems();
300
301 // Now we resume a standard endUpdate cycle
302 // but interleaving the sub-mesh levels
303 for (DynamicMesh* mesh : all_cascade_meshes)
304 mesh->_internalEndUpdateInit(true);
305
306 mesh_exchanger->updateItemGroups();
307
308 // Recalculate synchronizers on groups.
309 for (DynamicMesh* mesh : all_cascade_meshes)
310 mesh->_computeGroupSynchronizeInfos();
311
312 // Update the values of the variables of the received entities
313 mesh_exchanger->updateVariables();
314
315 // Finalize the modifications whose sorting and compaction
316 for (DynamicMesh* mesh : all_cascade_meshes) {
317 // Request info display for the current mesh
318 bool print_info = (mesh == m_mesh);
319 mesh->_internalEndUpdateFinal(print_info);
320 }
321
322 // Finalize the exchanges
323 // For now, this is only useful for the TiedInterface but
324 // this should be removed.
325 mesh_exchanger->finalizeExchange();
326
327 // TODO: guarantee this call in case of an exception.
328 exchange_mng.endExchange();
329
330 m_mesh->endUpdate();
331
332 m_mesh->m_parallel_mng = old_parallel_mng;
333}
334
335/*---------------------------------------------------------------------------*/
336/*---------------------------------------------------------------------------*/
337
338void DynamicMeshMerger::
339mergeMeshes(ConstArrayView<DynamicMesh*> meshes)
340{
341 UniqueArray<DynamicMesh*> all_meshes;
342 // The first mesh in \a all_meshes is the one that will contain
343 // the merge of the meshes in \a meshes at the end.
344 all_meshes.add(m_mesh);
345 for (auto mesh : meshes)
346 all_meshes.add(mesh);
347
348 // The merging algorithm uses the same mechanism as for entity exchanges
349 // (MeshExchanger). To function, it is necessary to create
350 // an IParallelMng per merged mesh. We therefore use a
351 // shared memory IParallelMng, and launch a thread per
352 // mesh. The rank 0 mesh will receive all entities.
353
354 IArcaneMain* am = IArcaneMain::arcaneMain();
355 Int32 nb_local_rank = all_meshes.size();
356 // Search for the service used for parallelism
357 String message_passing_service = "SharedMemoryParallelMngContainerFactory";
358 ServiceBuilder<IParallelMngContainerFactory> sf(am->application());
359 auto pbf = sf.createReference(message_passing_service, SB_AllowNull);
360 if (!pbf)
361 ARCANE_FATAL("Can not find service '{0}' implementing IParallelMngContainerFactory", message_passing_service);
362 Parallel::Communicator comm = m_mesh->parallelMng()->communicator();
363 Parallel::Communicator machine_comm = m_mesh->parallelMng()->machineCommunicator();
364 Ref<IParallelMngContainer> parallel_builder(pbf->_createParallelMngBuilder(nb_local_rank, comm, machine_comm));
365
366 IApplication* app = am->application();
367 UniqueArray<LauncherThreadInfo> launch_infos(nb_local_rank);
368 for (Integer i = 0; i < nb_local_rank; ++i) {
369 LauncherThreadInfo& lti = launch_infos[i];
370
371 Int32 local_rank = i;
372 Int32 mesh_part_rank = all_meshes[i]->meshPartInfo().partRank();
373 String file_suffix = String::format("mm_{0}", mesh_part_rank);
374 lti.m_trace_mng = app->createAndInitializeTraceMng(m_mesh->traceMng(), file_suffix);
375 ITraceMng* tm = lti.m_trace_mng.get();
376 lti.m_parallel_mng = parallel_builder->_createParallelMng(local_rank, tm);
377 tm->setTraceId(String::format("Exchanger part={0} pm_rank={1}", mesh_part_rank, local_rank));
378
379 lti.m_mesh = all_meshes[i];
380 lti.m_local_rank = local_rank;
381 }
382
383 UniqueArray<std::thread*> gths(nb_local_rank);
384 for (Integer i = 0; i < nb_local_rank; ++i) {
385 gths[i] = new std::thread(_Launcher, &launch_infos[i]);
386 }
387 bool has_error = false;
388 for (Integer i = 0; i < nb_local_rank; ++i) {
389 gths[i]->join();
390 if (launch_infos[i].m_has_error)
391 has_error = true;
392 delete gths[i];
393 }
394 // TODO: propagate the exception via std::exception_ptr
395 if (has_error)
396 ARCANE_FATAL("Error during mesh merge");
397}
398
399/*---------------------------------------------------------------------------*/
400/*---------------------------------------------------------------------------*/
401
402} // End namespace Arcane::mesh
403
404/*---------------------------------------------------------------------------*/
405/*---------------------------------------------------------------------------*/
#define ARCANE_FATAL(...)
Macro throwing a FatalErrorException.
#define ENUMERATE_ITEM(name, group)
Generic enumerator for a node group.
static IArcaneMain * arcaneMain()
Interface of the parallelism manager for a subdomain.
@ II_NeedRemove
The entity must be removed.
Definition ItemFlags.h:63
TraceAccessor(ITraceMng *m)
Constructs an accessor via the trace manager m.
TraceMessage info() const
Flow for an information message.
TraceMessage pwarning() const
Implementation of a mesh.
Definition DynamicMesh.h:98
Interface for the mesh exchange manager between subdomains.
Information for a mesh exchange between sub-domains.
@ SB_AllowNull
Allows the service to be absent.
Int32 Integer
Type representing an integer.
std::int32_t Int32
Signed integer type of 32 bits.