Arcane  4.1.12.0
Developer documentation
Loading...
Searching...
No Matches
SubMeshTools.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/* SubMeshTools.cc (C) 2000-2024 */
9/* */
10/* Specific algorithms for sub-meshing. */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arcane/mesh/SubMeshTools.h"
15
16#include "arcane/utils/ITraceMng.h"
17#include "arcane/utils/ScopedPtr.h"
18
19// Generic includes not specific to the DynamicMesh implementation
20#include "arcane/core/Variable.h"
21#include "arcane/core/SharedVariable.h"
22#include "arcane/core/IParallelMng.h"
23#include "arcane/core/MeshToMeshTransposer.h"
24#include "arcane/core/IVariableSynchronizer.h"
25#include "arcane/core/IParallelExchanger.h"
26#include "arcane/core/ISerializer.h"
27#include "arcane/core/ISerializeMessage.h"
28#include "arcane/core/ItemPrinter.h"
29#include "arcane/core/TemporaryVariableBuildInfo.h"
30#include "arcane/core/ParallelMngUtils.h"
31
32// Includes specific to the DynamicMesh implementation
33#include "arcane/mesh/DynamicMesh.h"
34#include "arcane/mesh/DynamicMeshIncrementalBuilder.h"
35#include "arcane/mesh/ItemFamily.h"
36#include "arcane/mesh/CellFamily.h"
37#include "arcane/mesh/FaceFamily.h"
38#include "arcane/mesh/EdgeFamily.h"
39#include "arcane/mesh/NodeFamily.h"
40
41/*---------------------------------------------------------------------------*/
42/*---------------------------------------------------------------------------*/
43
44namespace Arcane::mesh
45{
46
47/*---------------------------------------------------------------------------*/
48/*---------------------------------------------------------------------------*/
49
50SubMeshTools::
51SubMeshTools(DynamicMesh* mesh, DynamicMeshIncrementalBuilder* mesh_builder)
52: TraceAccessor(mesh->traceMng())
53, m_mesh(mesh)
54, m_mesh_builder(mesh_builder)
55, m_parallel_mng(mesh->parallelMng())
56{
57 ;
58}
59
60/*---------------------------------------------------------------------------*/
61/*---------------------------------------------------------------------------*/
62
63SubMeshTools::
64~SubMeshTools()
65{
66 ;
67}
68
69/*---------------------------------------------------------------------------*/
70/*---------------------------------------------------------------------------*/
71
72void SubMeshTools::
73_updateGroups()
74{
75 // Adjust groups by removing entities that are no longer in the mesh
76 for (IItemFamilyCollection::Enumerator i_family(m_mesh->itemFamilies()); ++i_family;) {
77 IItemFamily* family = *i_family;
78 for (ItemGroupCollection::Enumerator i_group((*i_family)->groups()); ++i_group;) {
79 ItemGroup group = *i_group;
80 // GG: the following method is equivalent to what is in the OLD define.
81 family->partialEndUpdateGroup(group);
82#ifdef OLD
83 // GG: one must not modify the group of all entities because
84 // it will be modified in DynamicMeshKindInfos::finalizeMeshChanged()
85 // and therefore if we modify it here, it will be done twice.
86 if (group.isAllItems())
87 continue;
88 // Previously: if (group.isLocalToSubDomain() || group.isOwn())
89 if (group.internal()->hasComputeFunctor())
90 group.invalidate();
91 else
92 group.internal()->removeSuppressedItems();
93#endif
94 }
95 }
96}
97
98/*---------------------------------------------------------------------------*/
99/*---------------------------------------------------------------------------*/
101void SubMeshTools::
102_fillGhostItems(ItemFamily* family, Array<Int32>& items_local_id)
103{
104 items_local_id.clear();
105 ItemInternalMap& items_map = family->itemsMap();
106 Int32 rank = m_parallel_mng->commRank();
107
108 items_map.eachItem([&](Item item) {
109 if (item.owner() != rank) {
110 items_local_id.add(item.localId());
111 }
112 });
113}
114
115/*---------------------------------------------------------------------------*/
116/*---------------------------------------------------------------------------*/
117
118void SubMeshTools::
119_fillFloatingItems(ItemFamily* family, Array<Int32>& items_local_id)
120{
121 items_local_id.reserve(1000);
122 ItemInternalMap& items_map = family->itemsMap();
123 items_map.eachItem([&](impl::ItemBase item) {
124 if (!item.isSuppressed() && item.nbCell() == 0 && !item.isOwn()) {
125 items_local_id.add(item.localId());
126 }
127 });
128}
129
130/*---------------------------------------------------------------------------*/
131/*---------------------------------------------------------------------------*/
132
133void SubMeshTools::
134_removeCell(Cell cell)
135{
136 CellFamily& cell_family = m_mesh->trueCellFamily();
137 FaceFamily& face_family = m_mesh->trueFaceFamily();
138 EdgeFamily& edge_family = m_mesh->trueEdgeFamily();
139 NodeFamily& node_family = m_mesh->trueNodeFamily();
140
141 ItemLocalId cell_lid(cell);
142 for (Face face : cell.faces())
143 face_family.removeCellFromFace(face, cell_lid);
144 for (Edge edge : cell.edges())
145 edge_family.removeCellFromEdge(edge, cell_lid);
146 for (Node node : cell.nodes())
147 node_family.removeCellFromNode(node, cell_lid);
148 cell_family.removeItem(cell);
149}
150
151/*---------------------------------------------------------------------------*/
152/*---------------------------------------------------------------------------*/
153
154void SubMeshTools::
155removeDeadGhostCells()
156{
157 // TODO
158 CellFamily& cell_family = m_mesh->trueCellFamily();
159 FaceFamily& face_family = m_mesh->trueFaceFamily();
160 EdgeFamily& edge_family = m_mesh->trueEdgeFamily();
161 NodeFamily& node_family = m_mesh->trueNodeFamily();
162
163 // We look for ghost items whose parents are deleted
164 UniqueArray<Int32> items_to_remove;
165 _fillGhostItems(&cell_family, items_to_remove);
166 ENUMERATE_ (Cell, icell, cell_family.view(items_to_remove)) {
167 impl::ItemBase item(icell->itemBase());
168 ARCANE_ASSERT((!item.parentBase(0).isSuppressed()), ("SubMesh cell not synchronized with its support group"));
169
170 // set no_destroy=true => the sub-items will handle the destruction
171 if (item.parentBase(0).isSuppressed())
172 _removeCell(item);
173 }
174
175 _fillGhostItems(&face_family, items_to_remove);
176 ENUMERATE_ (Face, iface, face_family.view(items_to_remove)) {
177 impl::ItemBase item(iface->itemBase());
178 if (item.parentBase(0).isSuppressed()) {
179 ARCANE_ASSERT((item.nbCell() == 0), ("Cannot remove connected sub-item"));
180 face_family.removeFaceIfNotConnected(item);
181 }
182 }
183
184 _fillGhostItems(&edge_family, items_to_remove);
185 EdgeInfoListView edges(&edge_family);
186 ENUMERATE_ (Edge, iedge, edge_family.view(items_to_remove)) {
187 impl::ItemBase item(iedge->itemBase());
188 if (item.parentBase(0).isSuppressed()) {
189 ARCANE_ASSERT((item.nbCell() == 0 && item.nbFace() == 0), ("Cannot remove connected sub-item"));
190 edge_family.removeEdgeIfNotConnected(item);
191 }
192 }
193
194 _fillGhostItems(&node_family, items_to_remove);
195 NodeInfoListView nodes(&node_family);
196 ENUMERATE_ (Node, inode, node_family.view(items_to_remove)) {
197 impl::ItemBase item(inode->itemBase());
198 if (item.parentBase(0).isSuppressed()) {
199 ARCANE_ASSERT((item.nbCell() == 0 && item.nbFace() == 0 && item.nbEdge() == 0), ("Cannot remove connected sub-item"));
200 node_family.removeItem(item);
201 }
202 }
203
204 _updateGroups();
205}
206
207/*---------------------------------------------------------------------------*/
208/*---------------------------------------------------------------------------*/
209
210void SubMeshTools::
211removeGhostMesh()
212{
213 CellFamily& cell_family = m_mesh->trueCellFamily();
214 FaceFamily& face_family = m_mesh->trueFaceFamily();
215 EdgeFamily& edge_family = m_mesh->trueEdgeFamily();
216 NodeFamily& node_family = m_mesh->trueNodeFamily();
217
218 // NOTE GG: normally we should be able to replace the entire
219 // destruction code with:
220 // for( ItemInternal* item : items_to_remove ){
221 // cell_family.removeCell(item);
222 // }
223 // For now, it works on the tests since I don't have all the IFPEN tests
224 // so I prefer to leave it as is.
225
226 // The order is important to correctly disconnect the connectivities
227 // The methods are written directly with the primitives of the *Family
228 // because the ready-made methods (like CellFamily::removeCell)
229 // do not take into account the particular ghost mechanism of sub-meshing
230 // where a sub-item can live without an over-item attached to it
231 // (e.g., 'own' Face without surrounding Cell)
232 UniqueArray<Int32> items_to_remove;
233
234 _fillGhostItems(&cell_family, items_to_remove);
235 ENUMERATE_ (Cell, icell, cell_family.view(items_to_remove)) {
236 _removeCell(*icell);
237 }
238
239 _fillGhostItems(&face_family, items_to_remove);
240 ENUMERATE_ (Face, iface, face_family.view(items_to_remove)) {
241 face_family.removeFaceIfNotConnected(*iface);
242 }
243
244 _fillGhostItems(&edge_family, items_to_remove);
245 ENUMERATE_ (Edge, iedge, edge_family.view(items_to_remove)) {
246 Edge edge(*iedge);
247 if (edge.nbCell() == 0 && edge.nbFace() == 0)
248 edge_family.removeEdgeIfNotConnected(edge);
249 }
250
251 _fillGhostItems(&node_family, items_to_remove);
252 ENUMERATE_ (Node, inode, node_family.view(items_to_remove)) {
253 Node node(*inode);
254 if (node.nbCell() == 0 && node.nbFace() == 0 && node.nbEdge() == 0)
255 node_family.removeItem(node);
256 }
257
258 _updateGroups();
259}
260
261/*---------------------------------------------------------------------------*/
262/*---------------------------------------------------------------------------*/
263
264void SubMeshTools::
265removeFloatingItems()
266{
267 FaceFamily& face_family = m_mesh->trueFaceFamily();
268 EdgeFamily& edge_family = m_mesh->trueEdgeFamily();
269 NodeFamily& node_family = m_mesh->trueNodeFamily();
270
271 // The order is important to correctly disconnect the connectivities
272 // The methods are written here directly with the primitives of the *Family
273 // because the ready-made methods (like CellFamily::removeCell)
274 // do not take into account the particular ghost mechanism of sub-meshes
275 // where a sub-item can live without a super-item attached to it
276 // (e.g.: 'own' Face without surrounding Cell)
277 SharedArray<Int32> items_to_remove;
278 _fillFloatingItems(&face_family, items_to_remove);
279 ENUMERATE_ (Face, iface, face_family.view(items_to_remove)) {
280 Face face(*iface);
281 if (face.nbCell() == 0)
282 face_family.removeFaceIfNotConnected(face);
283 }
284 items_to_remove.clear();
285 _fillFloatingItems(&edge_family, items_to_remove);
286 ENUMERATE_ (Edge, iedge, edge_family.view(items_to_remove)) {
287 Edge edge(*iedge);
288 if (edge.nbCell() == 0 && edge.nbFace() == 0)
289 edge_family.removeEdgeIfNotConnected(edge);
290 }
291 items_to_remove.clear();
292 _fillFloatingItems(&node_family, items_to_remove);
293 ENUMERATE_ (Node, inode, node_family.view(items_to_remove)) {
294 Node node(*inode);
295 if (node.nbCell() == 0 && node.nbFace() == 0 && node.nbEdge() == 0)
296 node_family.removeItem(node);
297 }
298
299 _updateGroups();
300}
301
302/*---------------------------------------------------------------------------*/
303/*---------------------------------------------------------------------------*/
304
305void SubMeshTools::
306updateGhostMesh()
307{
308 removeGhostMesh();
309
310 _checkValidItemOwner();
311
312 eItemKind kinds[] = { IK_Cell, IK_Face, IK_Edge, IK_Node };
313 Integer nb_kind = sizeof(kinds) / sizeof(eItemKind);
314 for (Integer i_kind = 0; i_kind < nb_kind; ++i_kind) {
315 IItemFamily* family = m_mesh->itemFamily(kinds[i_kind]);
316 updateGhostFamily(family);
317 }
318
319 removeFloatingItems();
320 _checkFloatingItems();
321 _checkValidItemOwner();
322
323 // The finalization of families and the construction
324 // of their synchronizers is delegated externally
325}
326
327/*---------------------------------------------------------------------------*/
328/*---------------------------------------------------------------------------*/
329
330void SubMeshTools::
331updateGhostFamily(IItemFamily* family)
332{
333 const eItemKind kind = family->itemKind();
334 // IMesh * parent_mesh = m_mesh->parentMesh();
335 IItemFamily* parent_family = family->parentFamily();
336
337 debug(Trace::High) << "Process ghost on submesh " << m_mesh->name() << " with kind=" << kind;
338
339 auto exchanger{ ParallelMngUtils::createExchangerRef(m_parallel_mng) };
340 IVariableSynchronizer* synchronizer = parent_family->allItemsSynchronizer();
341 Int32ConstArrayView ranks = synchronizer->communicatingRanks();
342 std::map<Integer, SharedArray<Int64>> to_send_items;
343 for (Integer i = 0; i < ranks.size(); ++i) {
344 const Integer rank = ranks[i];
345 debug(Trace::High) << "Has " << kind << " comm with " << rank << " : " << i << " / " << ranks.size() << " ranks";
346
347 // The shared items must be owned => consistency of requests
348 ItemVectorView shared_items(parent_family->view(synchronizer->sharedItems(i)));
349 ItemVector shared_submesh_items = MeshToMeshTransposer::transpose(parent_family, family, shared_items);
350 SharedArray<Int64> current_to_send_items;
351
352 ENUMERATE_ITEM (iitem, shared_submesh_items) {
353 if (iitem.localId() != NULL_ITEM_LOCAL_ID) {
354 const Item& item = *iitem;
355 ARCANE_ASSERT((item.uniqueId() == item.parent().uniqueId()), ("Inconsistent item/parent uid"));
356 debug(Trace::Highest) << "Send shared submesh item to " << rank << " " << ItemPrinter(item);
357 current_to_send_items.add(item.parent().uniqueId());
358 }
359 }
360
361 debug(Trace::High) << "SubMesh ghost comm " << kind << " with " << rank << " : "
362 << shared_items.size() << " / " << current_to_send_items.size();
363
364 // For localized sub-meshes, we only consider the
365 // recipients where there is something to send
366 if (!current_to_send_items.empty()) {
367 exchanger->addSender(rank);
368 to_send_items[rank] = current_to_send_items;
369 }
370 }
371
372 exchanger->initializeCommunicationsMessages();
373
374 for (Integer i = 0, ns = exchanger->nbSender(); i < ns; ++i) {
375 ISerializeMessage* sm = exchanger->messageToSend(i);
376 const Int32 rank = sm->destination().value();
377 ISerializer* s = sm->serializer();
378 const Int64Array& current_to_send_items = to_send_items[rank];
379 s->setMode(ISerializer::ModeReserve);
380
381 s->reserveArray(current_to_send_items);
382
383 s->allocateBuffer();
384 s->setMode(ISerializer::ModePut);
385
386 s->putArray(current_to_send_items);
387 }
388
389 to_send_items.clear(); // destruction of temporary data before sending
390 exchanger->processExchange();
391
392 for (Integer i = 0, ns = exchanger->nbReceiver(); i < ns; ++i) {
393 ISerializeMessage* sm = exchanger->messageToReceive(i);
394 const Int32 rank = sm->destination().value();
395 ISerializer* s = sm->serializer();
396 //Integer new_submesh_ghost_size = s->getInteger();
397 Int64UniqueArray uids;
398 s->getArray(uids);
399 Int32UniqueArray lids(uids.size());
400 parent_family->itemsUniqueIdToLocalId(lids, uids);
401 ItemVectorView new_submesh_ghosts = parent_family->view(lids);
402 ENUMERATE_ITEM (iitem, new_submesh_ghosts) {
403 if (iitem->owner() != rank)
404 fatal() << "Bad ghost owner " << ItemPrinter(*iitem) << " : expected owner=" << rank;
405 debug(Trace::Highest) << "Add ghost submesh item from " << rank << " : " << FullItemPrinter(*iitem);
406 }
407 m_mesh_builder->addParentItems(new_submesh_ghosts, kind);
408 }
409}
410
411/*---------------------------------------------------------------------------*/
412/*---------------------------------------------------------------------------*/
413
414void SubMeshTools::
415display(IMesh* mesh, const String msg)
416{
417 ITraceMng* traceMng = mesh->traceMng();
418 traceMng->info() << "Display mesh " << mesh->name() << " : " << msg;
419 eItemKind kinds[] = { IK_Cell, IK_Face, IK_Edge, IK_Node };
420 Integer nb_kind = sizeof(kinds) / sizeof(eItemKind);
421 for (Integer i_kind = 0; i_kind < nb_kind; ++i_kind) {
422 IItemFamily* family = mesh->itemFamily(kinds[i_kind]);
423 ItemInfoListView items(family);
424 Integer count = 0;
425 for (Integer z = 0, zs = family->maxLocalId(); z < zs; ++z)
426 if (!items[z].itemBase().isSuppressed())
427 ++count;
428 traceMng->info() << "\t" << family->itemKind() << " " << count;
429
430 for (Integer z = 0, zs = family->maxLocalId(); z < zs; ++z) {
431 Item item = items[z];
432 if (!item.itemBase().isSuppressed()) {
433 traceMng->info() << ItemPrinter(item);
434 }
435 }
436 }
437}
438
439/*---------------------------------------------------------------------------*/
440/*---------------------------------------------------------------------------*/
441
442void SubMeshTools::
443_checkValidItemOwner()
444{
445 if (!arcaneIsCheck())
446 return;
447
448 eItemKind kinds[] = { IK_Cell, IK_Face, IK_Edge, IK_Node };
449 Integer nb_kind = sizeof(kinds) / sizeof(eItemKind);
450 for (Integer i_kind = 0; i_kind < nb_kind; ++i_kind) {
451 IItemFamily* family = m_mesh->itemFamily(kinds[i_kind]);
452 IItemFamily* parent_family = family->parentFamily();
453 ItemInfoListView items(family);
454 for (Integer z = 0, zs = family->maxLocalId(); z < zs; ++z) {
455 Item item = items[z];
456 if (!item.itemBase().isSuppressed()) {
457 if (item.uniqueId() != item.itemBase().parentBase(0).uniqueId()) {
458 Int64UniqueArray uids;
459 uids.add(item.uniqueId());
460 Int32UniqueArray lids(1);
461 parent_family->itemsUniqueIdToLocalId(lids, uids, false);
462 ARCANE_FATAL("Inconsistent parent uid '{0}' now located in '{1}'",
463 ItemPrinter(item), lids[0]);
464 }
465 }
466 }
467 }
468}
469
470/*---------------------------------------------------------------------------*/
471/*---------------------------------------------------------------------------*/
472
473void SubMeshTools::
474_checkFloatingItems()
475{
476 Integer nerror = 0;
477 eItemKind kinds[] = { IK_Face, IK_Edge, IK_Node };
478 Integer nb_kind = sizeof(kinds) / sizeof(eItemKind);
479 for (Integer i_kind = 0; i_kind < nb_kind; ++i_kind) {
480 IItemFamily* family = m_mesh->itemFamily(kinds[i_kind]);
481 // Calculation of orphaned cell items
482 ItemInfoListView items(family);
483 for (Integer z = 0, zs = family->maxLocalId(); z < zs; ++z) {
484 Item item = items[z];
485 if (!item.itemBase().isSuppressed() && item.itemBase().nbCell() == 0) {
486 error() << "Floating item detected : " << ItemPrinter(item);
487 ++nerror;
488 }
489 }
490 }
491 if (nerror)
492 fatal() << "ERROR " << String::plural(nerror, "floating item") << " detected; see above";
493}
494
495/*---------------------------------------------------------------------------*/
496/*---------------------------------------------------------------------------*/
497
498} // namespace Arcane::mesh
499
500/*---------------------------------------------------------------------------*/
501/*---------------------------------------------------------------------------*/
#define ARCANE_FATAL(...)
Macro throwing a FatalErrorException.
#define ENUMERATE_(type, name, group)
Generic enumerator for an entity group.
#define ENUMERATE_ITEM(name, group)
Generic enumerator for a node group.
Base class for 1D data vectors.
void clear()
Removes the elements from the array.
void add(ConstReferenceType val)
Adds element val to the end of the array.
void reserve(Int64 new_capacity)
Reserves memory for new_capacity elements.
constexpr Integer size() const noexcept
Number of elements in the array.
Integer nbEdge() const
Number of edges of the entity or number of edges connected to the entity (for nodes).
bool isSuppressed() const
True if the entity is suppressed.
Integer nbCell() const
Number of cells connected to the entity (for nodes, edges, and faces).
Integer nbFace() const
Number of faces of the entity or number of faces connected to the entity (for nodes and edges).
bool isOwn() const
True if the entity belongs to the subdomain.
Int32 localId() const
Local number (in the subdomain) of the entity.
Base class for a mesh element.
Definition Item.h:84
constexpr Int32 localId() const
Local identifier of the entity in the processor subdomain.
Definition Item.h:233
Int32 owner() const
Owner subdomain number of the entity.
Definition Item.h:252
Implementation of a mesh.
Definition DynamicMesh.h:98
Associative array of ItemInternal.
void eachItem(const Lambda &lambda)
Template function to iterate over the instance's entities.
Array< Int64 > Int64Array
Dynamic one-dimensional array of 64-bit integers.
Definition UtilsTypes.h:125
bool arcaneIsCheck()
True if running in check mode.
Definition Misc.cc:66
UniqueArray< Int64 > Int64UniqueArray
Dynamic 1D array of 64-bit integers.
Definition UtilsTypes.h:339
Int32 Integer
Type representing an integer.
ConstArrayView< Int32 > Int32ConstArrayView
C equivalent of a 1D array of 32-bit integers.
Definition UtilsTypes.h:482
UniqueArray< Int32 > Int32UniqueArray
Dynamic 1D array of 32-bit integers.
Definition UtilsTypes.h:341
eItemKind
Mesh entity type.
@ IK_Node
Node mesh entity.
@ IK_Cell
Cell mesh entity.
@ IK_Face
Face mesh entity.
@ IK_Edge
Edge mesh entity.
std::int32_t Int32
Signed integer type of 32 bits.