Arcane  4.2.3.0
Developer documentation
Loading...
Searching...
No Matches
GhostLayerBuilder2.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/* GhostLayerBuilder2.cc (C) 2000-2025 */
9/* */
10/* Construction of ghost layers. */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arcane/utils/HashTableMap.h"
15#include "arcane/utils/ITraceMng.h"
16#include "arcane/utils/CheckedConvert.h"
17#include "arcane/utils/ValueConvert.h"
18
19#include "arcane/core/parallel/BitonicSortT.H"
20
21#include "arcane/core/IParallelExchanger.h"
22#include "arcane/core/ISerializeMessage.h"
23#include "arcane/core/SerializeBuffer.h"
24#include "arcane/core/ISerializer.h"
25#include "arcane/core/ItemPrinter.h"
26#include "arcane/core/Timer.h"
27#include "arcane/core/IGhostLayerMng.h"
28#include "arcane/core/IItemFamilyPolicyMng.h"
29#include "arcane/core/IItemFamilySerializer.h"
30#include "arcane/core/ParallelMngUtils.h"
31#include "arcane/core/internal/IMeshInternal.h"
32
33#include "arcane/mesh/DynamicMesh.h"
34#include "arcane/mesh/DynamicMeshIncrementalBuilder.h"
35#include "arcane/utils/ScopedPtr.h"
36
37#include <algorithm>
38#include <set>
39
40/*---------------------------------------------------------------------------*/
41/*---------------------------------------------------------------------------*/
42
43namespace Arcane::mesh
44{
45
46/*---------------------------------------------------------------------------*/
47/*---------------------------------------------------------------------------*/
52: public TraceAccessor
53{
54 class BoundaryNodeInfo;
57
58 public:
59
60 using ItemInternalMap = DynamicMeshKindInfos::ItemInternalMap;
61 using SubDomainItemMap = HashTableMapT<Int32, SharedArray<Int32>>;
62
63 public:
64
66 GhostLayerBuilder2(IMesh* mesh, bool is_allocate, Int32 version);
67
68 public:
69
70 void addGhostLayers();
71
72 private:
73
74 IMesh* m_mesh = nullptr;
75 IMeshInternal* m_mesh_internal = nullptr;
76 IParallelMng* m_parallel_mng = nullptr;
77 bool m_is_verbose = false;
78 bool m_is_allocate = false;
79 Int32 m_version = -1;
80 bool m_use_optimized_node_layer = true;
81 bool m_use_only_minimal_cell_uid = true;
82
83 private:
84
85 void _printItem(ItemInternal* ii, std::ostream& o);
86 void _markBoundaryItems(ArrayView<Int32> node_layer);
87 void _sendAndReceiveCells(SubDomainItemMap& cells_to_send);
88 void _sortBoundaryNodeList(Array<BoundaryNodeInfo>& boundary_node_list);
89 void _addGhostLayer(Integer current_layer, Int32ConstArrayView node_layer);
90 void _markBoundaryNodes(ArrayView<Int32> node_layer);
91 void _markBoundaryNodesFromEdges(ArrayView<Int32> node_layer);
92};
93
94/*---------------------------------------------------------------------------*/
95/*---------------------------------------------------------------------------*/
96
98GhostLayerBuilder2(IMesh* mesh, bool is_allocate, Int32 version)
100, m_mesh(mesh)
101, m_mesh_internal(mesh->_internalApi())
102, m_parallel_mng(m_mesh->parallelMng())
103, m_is_allocate(is_allocate)
104, m_version(version)
105{
106 if (auto v = Convert::Type<Int32>::tryParseFromEnvironment("ARCANE_GHOSTLAYER_USE_OPTIMIZED_LAYER", true)) {
107 Int32 vv = v.value();
108 m_use_optimized_node_layer = (vv == 1 || vv == 3);
109 m_use_only_minimal_cell_uid = (v == 2 || vv == 3);
110 }
111 if (auto v = Convert::Type<Int32>::tryParseFromEnvironment("ARCANE_GHOSTLAYER_VERBOSE", true)) {
112 m_is_verbose = (v.value() != 0);
113 }
114}
115
116/*---------------------------------------------------------------------------*/
117/*---------------------------------------------------------------------------*/
118
119void GhostLayerBuilder2::
120_printItem(ItemInternal* ii, std::ostream& o)
121{
122 o << ItemPrinter(ii);
123}
124
125/*---------------------------------------------------------------------------*/
126/*---------------------------------------------------------------------------*/
127
128/*---------------------------------------------------------------------------*/
129/*---------------------------------------------------------------------------*/
139{
140 public:
141
142 using BasicType = Int64;
143 static constexpr Int64 nbBasicTypeSize() { return 3; }
144
145 public:
146
148 {
149 Int32 message_size = messageSize(values);
150 const BoundaryNodeInfo* fsi_base = values.data();
151 auto* ptr = reinterpret_cast<const Int64*>(fsi_base);
152 return ConstArrayView<BasicType>(message_size, ptr);
153 }
154
155 static ArrayView<BasicType> asBasicBuffer(ArrayView<BoundaryNodeInfo> values)
156 {
157 Int32 message_size = messageSize(values);
158 BoundaryNodeInfo* fsi_base = values.data();
159 auto* ptr = reinterpret_cast<Int64*>(fsi_base);
160 return ArrayView<BasicType>(message_size, ptr);
161 }
162
163 static Int32 messageSize(ConstArrayView<BoundaryNodeInfo> values)
164 {
165 static_assert((sizeof(Int64) * nbBasicTypeSize()) == sizeof(BoundaryNodeInfo));
166 Int64 message_size_i64 = values.size() * nbBasicTypeSize();
167 Int32 message_size = CheckedConvert::toInteger(message_size_i64);
168 return message_size;
169 }
170
171 static Int32 nbElement(Int32 message_size)
172 {
173 if ((message_size % nbBasicTypeSize()) != 0)
174 ARCANE_FATAL("Message size '{0}' is not a multiple of basic size '{1}'", message_size, nbBasicTypeSize());
175 Int32 nb_element = message_size / nbBasicTypeSize();
176 return nb_element;
177 }
178
179 public:
180
182 {
183 size_t operator()(const BoundaryNodeInfo& a) const
184 {
185 size_t h1 = std::hash<Int64>{}(a.node_uid);
186 size_t h2 = std::hash<Int64>{}(a.cell_uid);
187 size_t h3 = std::hash<Int32>{}(a.cell_owner);
188 return h1 ^ h2 ^ h3;
189 }
190 };
191 friend bool operator==(const BoundaryNodeInfo& a, const BoundaryNodeInfo& b)
192 {
193 return (a.node_uid == b.node_uid && a.cell_uid == b.cell_uid && a.cell_owner == b.cell_owner);
194 }
195
196 public:
197
198 Int64 node_uid = NULL_ITEM_UNIQUE_ID;
199 Int64 cell_uid = NULL_ITEM_UNIQUE_ID;
200 Int32 cell_owner = -1;
201 Int32 padding = 0;
202};
203
204/*---------------------------------------------------------------------------*/
205/*---------------------------------------------------------------------------*/
210{
211 public:
212
213 static bool compareLess(const BoundaryNodeInfo& k1, const BoundaryNodeInfo& k2)
214 {
215 Int64 k1_node_uid = k1.node_uid;
216 Int64 k2_node_uid = k2.node_uid;
217 if (k1_node_uid < k2_node_uid)
218 return true;
219 if (k1_node_uid > k2_node_uid)
220 return false;
221
222 Int64 k1_cell_uid = k1.cell_uid;
223 Int64 k2_cell_uid = k2.cell_uid;
224 if (k1_cell_uid < k2_cell_uid)
225 return true;
226 if (k1_cell_uid > k2_cell_uid)
227 return false;
228
229 return (k1.cell_owner < k2.cell_owner);
230 }
231
233 {
234 auto buf_view = BoundaryNodeInfo::asBasicBuffer(values);
235 return pm->send(buf_view, rank, false);
236 }
237
239 {
240 auto buf_view = BoundaryNodeInfo::asBasicBuffer(values);
241 return pm->recv(buf_view, rank, false);
242 }
243
244 static Integer messageSize(ConstArrayView<BoundaryNodeInfo> values)
245 {
246 return BoundaryNodeInfo::messageSize(values);
247 }
248
249 static BoundaryNodeInfo maxValue()
250 {
252 bni.node_uid = INT64_MAX;
253 bni.cell_uid = INT64_MAX;
254 bni.cell_owner = -1;
255 return bni;
256 }
257
258 static bool isValid(const BoundaryNodeInfo& bni)
259 {
260 return bni.node_uid != INT64_MAX;
261 }
262};
263
264/*---------------------------------------------------------------------------*/
265/*---------------------------------------------------------------------------*/
266
268{
269 public:
270
271 Integer m_index;
272 Integer m_nb_cell;
273};
274
275/*---------------------------------------------------------------------------*/
276/*---------------------------------------------------------------------------*/
277
301{
302 IParallelMng* pm = m_parallel_mng;
303 if (!pm->isParallel())
304 return;
305 Integer nb_ghost_layer = m_mesh->ghostLayerMng()->nbGhostLayer();
306 info() << "** GHOST LAYER BUILDER V" << m_version << " with sort (nb_ghost_layer=" << nb_ghost_layer << ")";
307
308 // Ghost layer to which the node belongs.
309 UniqueArray<Integer> node_layer(m_mesh->nodeFamily()->maxLocalId(), -1);
310
311 // Mark boundary items
312 // We do this even if we do not want ghost cell layers
313 _markBoundaryItems(node_layer);
314
315 if (nb_ghost_layer == 0)
316 return;
317 const Int32 my_rank = pm->commRank();
318
319 const bool is_non_manifold = m_mesh->meshKind().isNonManifold();
320 if (is_non_manifold && (m_version != 3))
321 ARCANE_FATAL("Only version 3 of ghostlayer builder is supported for non manifold meshes");
322
323 ItemInternalMap& cells_map = m_mesh_internal->cellsMap();
324 ItemInternalMap& nodes_map = m_mesh_internal->nodesMap();
325
326 Integer boundary_nodes_uid_count = 0;
327
328 // Check that there are no ghost cells with version 3.
329 // If so, display a warning and indicate to use version 4.
330 if (m_version == 3) {
331 Integer nb_ghost = 0;
332 cells_map.eachItem([&](Item cell) {
333 if (!cell.isOwn())
334 ++nb_ghost;
335 });
336 if (nb_ghost != 0)
337 warning() << "Invalid call to addGhostLayers() with version 3 because mesh "
338 << " already has '" << nb_ghost << "' ghost cells. The computed ghost cells"
339 << " may be wrong. Use version 4 of ghost layer builder if you want to handle this case";
340 }
341
342 // Ghost layer to which the cell belongs.
343 UniqueArray<Integer> cell_layer(m_mesh->cellFamily()->maxLocalId(), -1);
344
345 if (m_version >= 4) {
346 _markBoundaryNodes(node_layer);
347 nodes_map.eachItem([&](Item node) {
348 if (node_layer[node.localId()] == 1)
349 ++boundary_nodes_uid_count;
350 });
351 }
352 else {
353 // Iterate over nodes and calculate the number of boundary nodes
354 // and marks the first layer
355 nodes_map.eachItem([&](Item node) {
356 Int32 f = node.itemBase().flags();
357 if (f & ItemFlags::II_Shared) {
358 node_layer[node.localId()] = 1;
359 ++boundary_nodes_uid_count;
360 }
361 });
362 }
363
364 info() << "NB BOUNDARY NODE=" << boundary_nodes_uid_count;
365
366 for (Integer current_layer = 1; current_layer <= nb_ghost_layer; ++current_layer) {
367 //Integer current_layer = 1;
368 info() << "Processing layer " << current_layer;
369 cells_map.eachItem([&](Cell cell) {
370 // Do not process cells that do not belong to me
371 if (m_version >= 4 && cell.owner() != my_rank)
372 return;
373 //Int64 cell_uid = cell->uniqueId();
374 Int32 cell_lid = cell.localId();
375 if (cell_layer[cell_lid] != (-1))
376 return;
377 bool is_current_layer = false;
378 for (Int32 inode_local_id : cell.nodeIds()) {
379 Integer layer = node_layer[inode_local_id];
380 //info() << "NODE_LAYER lid=" << i_node->localId() << " layer=" << layer;
381 if (layer == current_layer) {
382 is_current_layer = true;
383 break;
384 }
385 }
386 if (is_current_layer) {
387 cell_layer[cell_lid] = current_layer;
388 //info() << "Current layer celluid=" << cell_uid;
389 // If not marked, initialize to the current layer + 1.
390 for (Int32 inode_local_id : cell.nodeIds()) {
391 Integer layer = node_layer[inode_local_id];
392 if (layer == (-1)) {
393 //info() << "Marks node uid=" << i_node->uniqueId();
394 node_layer[inode_local_id] = current_layer + 1;
395 }
396 }
397 }
398 });
399 }
400
401 // Marks the nodes for which the ghost layer has not yet been assigned.
402 // For them, we indicate that we are on layer 'nb_ghost_layer+1'.
403 // The goal is never to transfer these nodes.
404 // NOTE: This mechanism was added in July 2024 for version 3.14.
405 // If it works well, we might only keep this method.
406 if (m_use_optimized_node_layer) {
407 Integer nb_no_layer = 0;
408 nodes_map.eachItem([&](Node node) {
409 Int32 lid = node.localId();
410 Int32 layer = node_layer[lid];
411 if (layer <= 0) {
412 node_layer[lid] = nb_ghost_layer + 1;
413 ++nb_no_layer;
414 }
415 });
416 info() << "Mark remaining nodes nb=" << nb_no_layer;
417 }
418
419 for (Integer i = 1; i <= nb_ghost_layer; ++i)
420 _addGhostLayer(i, node_layer);
421}
422
423/*---------------------------------------------------------------------------*/
424/*---------------------------------------------------------------------------*/
439{
440 IParallelMng* pm = m_mesh->parallelMng();
441 const Int32 my_rank = pm->commRank();
442 ItemInternalMap& faces_map = m_mesh_internal->facesMap();
443 // TODO: check if it is correct to modify ItemFlags::II_SubDomainBoundary
444 const int shared_and_boundary_flags = ItemFlags::II_Shared | ItemFlags::II_SubDomainBoundary;
445 // Iterates over faces and marks boundary nodes, edges and faces
446 faces_map.eachItem([&](Face face) {
447 Int32 nb_own = 0;
448 for (Integer i = 0, n = face.nbCell(); i < n; ++i)
449 if (face.cell(i).owner() == my_rank)
450 ++nb_own;
451 if (nb_own == 1) {
452 face.mutableItemBase().addFlags(shared_and_boundary_flags);
453 //++nb_sub_domain_boundary_face;
454 for (Item inode : face.nodes()) {
455 inode.mutableItemBase().addFlags(shared_and_boundary_flags);
456 node_layer[inode.localId()] = 1;
457 }
458 for (Item iedge : face.edges())
459 iedge.mutableItemBase().addFlags(shared_and_boundary_flags);
460 }
461 });
462 _markBoundaryNodesFromEdges(node_layer);
463}
464
465/*---------------------------------------------------------------------------*/
466/*---------------------------------------------------------------------------*/
467
468void GhostLayerBuilder2::
469_addGhostLayer(Integer current_layer, Int32ConstArrayView node_layer)
470{
471 info() << "Processing ghost layer " << current_layer
472 << " node_layer_size=" << node_layer.size();
473
474 SharedArray<BoundaryNodeInfo> boundary_node_list;
475 //boundary_node_list.reserve(boundary_nodes_uid_count);
476
477 IParallelMng* pm = m_parallel_mng;
478 Int32 my_rank = pm->commRank();
479 Int32 nb_rank = pm->commSize();
480
481 bool is_verbose = m_is_verbose;
482
483 ItemInternalMap& cells_map = m_mesh_internal->cellsMap();
484 ItemInternalMap& nodes_map = m_mesh_internal->nodesMap();
485
486 Int64 nb_added_for_different_rank = 0;
487 Int64 nb_added_for_in_layer = 0;
488
489 const Int32 max_local_id = m_mesh->nodeFamily()->maxLocalId();
490
491 // Arrays containing for each node the uid of the smallest connected cell
492 // and the associated rank. If the uid is A_NULL_UNIQUE_ID, this node should not be added.
493 UniqueArray<Int64> node_cell_uids(max_local_id, NULL_ITEM_UNIQUE_ID);
494
495 const bool do_only_minimal_uid = m_use_only_minimal_cell_uid;
496 // We must send all nodes whose layer number is different from (-1).
497 // NOTE: for the layer above 1, only one value must be sent.
498 cells_map.eachItem([&](Cell cell) {
499 // Do not process cells that do not belong to me
500 // FIXME: (july 2026) temporarily disable this check because it does not work
501 // when we have 2 or more ghost layers.
502 //if (m_version >= 4 && cell.owner() != my_rank)
503 //return;
504 Int64 cell_uid = cell.uniqueId();
505 for (Node node : cell.nodes()) {
506 Int32 node_lid = node.localId();
507 bool do_it = false;
508 if (cell.owner() != my_rank) {
509 do_it = true;
510 ++nb_added_for_different_rank;
511 }
512 else {
513 Integer layer = node_layer[node_lid];
514 do_it = layer <= current_layer;
515 if (do_it)
516 ++nb_added_for_in_layer;
517 }
518 if (do_it) {
519 Int32 node_lid = node.localId();
520 if (do_only_minimal_uid) {
521 Int64 current_uid = node_cell_uids[node_lid];
522 if ((current_uid == NULL_ITEM_UNIQUE_ID) || cell_uid < current_uid) {
523 node_cell_uids[node_lid] = cell_uid;
524 if (is_verbose)
525 info() << "AddNode node_uid=" << node.uniqueId() << " cell=" << cell_uid;
526 }
527 else if (is_verbose)
528 info() << "AddNode node_uid=" << node.uniqueId() << " cell=" << cell_uid << " not done current=" << current_uid;
529 }
530 else {
531 Int64 node_uid = node.uniqueId();
533 nci.node_uid = node_uid;
534 nci.cell_uid = cell_uid;
535 nci.cell_owner = my_rank;
536 boundary_node_list.add(nci);
537 if (is_verbose)
538 info() << "AddNode node_uid=" << node.uniqueId() << " cell=" << cell_uid;
539 }
540 }
541 }
542 });
543
544 if (do_only_minimal_uid) {
545 nodes_map.eachItem([&](Node node) {
546 Int32 lid = node.localId();
547 Int64 cell_uid = node_cell_uids[lid];
548 if (cell_uid != NULL_ITEM_UNIQUE_ID) {
549 Int64 node_uid = node.uniqueId();
551 nci.node_uid = node_uid;
552 nci.cell_uid = cell_uid;
553 nci.cell_owner = my_rank;
554 boundary_node_list.add(nci);
555 }
556 });
557 }
558
559 info() << "NB BOUNDARY NODE LIST=" << boundary_node_list.size()
560 << " nb_added_for_different_rank=" << nb_added_for_different_rank
561 << " nb_added_for_in_layer=" << nb_added_for_in_layer
562 << " do_only_minimal=" << do_only_minimal_uid;
563
564 _sortBoundaryNodeList(boundary_node_list);
565 SharedArray<BoundaryNodeInfo> all_boundary_node_info = boundary_node_list;
566
567 UniqueArray<BoundaryNodeToSendInfo> node_list_to_send;
568 {
569 ConstArrayView<BoundaryNodeInfo> all_bni = all_boundary_node_info;
570 Integer bi_n = all_bni.size();
571 for (Integer i = 0; i < bi_n; ++i) {
572 const BoundaryNodeInfo& bni = all_bni[i];
573 // Searches all elements of all_bni that have the same node.
574 // This represents all cells connected to this node.
575 Int64 node_uid = bni.node_uid;
576 Integer last_i = i;
577 for (; last_i < bi_n; ++last_i)
578 if (all_bni[last_i].node_uid != node_uid)
579 break;
580 Integer nb_same_node = (last_i - i);
581 if (is_verbose)
582 info() << "NB_SAME_NODE uid=" << node_uid << " n=" << nb_same_node << " last_i=" << last_i;
583 // Now, check if the cells connected to this node have the same owner.
584 // If this is the case, it is a true boundary node and nothing needs to be done.
585 // Otherwise, the list of cells must be sent to all PEs whose ranks appear in this list
586 Int32 owner = bni.cell_owner;
587 bool has_ghost = false;
588 for (Integer z = 0; z < nb_same_node; ++z)
589 if (all_bni[i + z].cell_owner != owner) {
590 has_ghost = true;
591 break;
592 }
593 if (has_ghost) {
595 si.m_index = i;
596 si.m_nb_cell = nb_same_node;
597 node_list_to_send.add(si);
598 if (is_verbose)
599 info() << "Add ghost uid=" << node_uid << " index=" << i << " nb_same_node=" << nb_same_node;
600 }
601 i = last_i - 1;
602 }
603 }
604
605 IntegerUniqueArray nb_info_to_send(nb_rank, 0);
606 {
607 ConstArrayView<BoundaryNodeInfo> all_bni = all_boundary_node_info;
608 Integer nb_node_to_send = node_list_to_send.size();
609 std::set<Int32> ranks_done;
610 for (Integer i = 0; i < nb_node_to_send; ++i) {
611 Integer index = node_list_to_send[i].m_index;
612 Integer nb_cell = node_list_to_send[i].m_nb_cell;
613
614 ranks_done.clear();
615
616 for (Integer kz = 0; kz < nb_cell; ++kz) {
617 Int32 krank = all_bni[index + kz].cell_owner;
618 if (ranks_done.find(krank) == ranks_done.end()) {
619 ranks_done.insert(krank);
620 // For each one, it will be necessary to send
621 // - the number of cells (1*Int64)
622 // - the node uid (1*Int64)
623 // - the uid and rank of each cell (2*Int64*nb_cell)
624 //TODO: it is possible to store the ranks as Int32
625 nb_info_to_send[krank] += (nb_cell * 2) + 2;
626 }
627 }
628 }
629 }
630
631 if (is_verbose) {
632 for (Integer i = 0; i < nb_rank; ++i) {
633 Integer nb_to_send = nb_info_to_send[i];
634 if (nb_to_send != 0)
635 info() << "NB_TO_SEND rank=" << i << " n=" << nb_to_send;
636 }
637 }
638
639 Integer total_nb_to_send = 0;
640 IntegerUniqueArray nb_info_to_send_indexes(nb_rank, 0);
641 for (Integer i = 0; i < nb_rank; ++i) {
642 nb_info_to_send_indexes[i] = total_nb_to_send;
643 total_nb_to_send += nb_info_to_send[i];
644 }
645 info() << "TOTAL_NB_TO_SEND=" << total_nb_to_send;
646
647 UniqueArray<Int64> resend_infos(total_nb_to_send);
648 {
649 ConstArrayView<BoundaryNodeInfo> all_bni = all_boundary_node_info;
650 Integer nb_node_to_send = node_list_to_send.size();
651 std::set<Int32> ranks_done;
652 for (Integer i = 0; i < nb_node_to_send; ++i) {
653 Integer node_index = node_list_to_send[i].m_index;
654 Integer nb_cell = node_list_to_send[i].m_nb_cell;
655 Int64 node_uid = all_bni[node_index].node_uid;
656
657 ranks_done.clear();
658
659 for (Integer kz = 0; kz < nb_cell; ++kz) {
660 Int32 krank = all_bni[node_index + kz].cell_owner;
661 if (ranks_done.find(krank) == ranks_done.end()) {
662 ranks_done.insert(krank);
663 Integer send_index = nb_info_to_send_indexes[krank];
664 resend_infos[send_index] = node_uid;
665 ++send_index;
666 resend_infos[send_index] = nb_cell;
667 ++send_index;
668 for (Integer zz = 0; zz < nb_cell; ++zz) {
669 resend_infos[send_index] = all_bni[node_index + zz].cell_uid;
670 ++send_index;
671 resend_infos[send_index] = all_bni[node_index + zz].cell_owner;
672 ++send_index;
673 }
674 nb_info_to_send_indexes[krank] = send_index;
675 }
676 }
677 }
678 }
679
680 IntegerUniqueArray nb_info_to_recv(nb_rank, 0);
681 {
682 Timer::SimplePrinter sp(traceMng(), "Sending size with AllToAll");
683 pm->allToAll(nb_info_to_send, nb_info_to_recv, 1);
684 }
685
686 if (is_verbose)
687 for (Integer i = 0; i < nb_rank; ++i)
688 info() << "NB_TO_RECV: I=" << i << " n=" << nb_info_to_recv[i];
689
690 Integer total_nb_to_recv = 0;
691 for (Integer i = 0; i < nb_rank; ++i)
692 total_nb_to_recv += nb_info_to_recv[i];
693
694 // There is a high chance that this will not work if the array is too large,
695 // one must proceed with arrays that do not exceed 2Go because of the
696 // MPI Int32.
697 // TODO: Perform the AllToAll in several stages if necessary.
698 // TODO: Merge this code with that of FaceUniqueIdBuilder2.
699 UniqueArray<Int64> recv_infos;
700 {
701 Int32 vsize = sizeof(Int64) / sizeof(Int64);
702 Int32UniqueArray send_counts(nb_rank);
703 Int32UniqueArray send_indexes(nb_rank);
704 Int32UniqueArray recv_counts(nb_rank);
705 Int32UniqueArray recv_indexes(nb_rank);
706 Int32 total_send = 0;
707 Int32 total_recv = 0;
708 for (Integer i = 0; i < nb_rank; ++i) {
709 send_counts[i] = (Int32)(nb_info_to_send[i] * vsize);
710 recv_counts[i] = (Int32)(nb_info_to_recv[i] * vsize);
711 send_indexes[i] = total_send;
712 recv_indexes[i] = total_recv;
713 total_send += send_counts[i];
714 total_recv += recv_counts[i];
715 }
716 recv_infos.resize(total_nb_to_recv);
717
718 Int64ConstArrayView send_buf(total_nb_to_send * vsize, (Int64*)resend_infos.data());
719 Int64ArrayView recv_buf(total_nb_to_recv * vsize, (Int64*)recv_infos.data());
720
721 info() << "BUF_SIZES: send=" << send_buf.size() << " recv=" << recv_buf.size();
722 {
723 Timer::SimplePrinter sp(traceMng(), "Send values with AllToAll");
724 pm->allToAllVariable(send_buf, send_counts, send_indexes, recv_buf, recv_counts, recv_indexes);
725 }
726 }
727
728 SubDomainItemMap cells_to_send(50, true);
729
730 // TODO: we don't necessarily need the cells here, but
731 // only the list of procs to whom it must be sent. Then,
732 // if the proc knows to whom it must send, it can send the cells
733 // at that time. This allows sending less information in the previous AllToAll.
734
735 {
736 Integer index = 0;
737 UniqueArray<Int32> my_cells;
738 SharedArray<Int32> ranks_to_send;
739 std::set<Int32> ranks_done;
740 while (index < total_nb_to_recv) {
741 Int64 node_uid = recv_infos[index];
742 ++index;
743 Int64 nb_cell = recv_infos[index];
744 ++index;
745 Node current_node(nodes_map.findItem(node_uid));
746 if (is_verbose)
747 info() << "NODE uid=" << node_uid << " nb_cell=" << nb_cell << " idx=" << (index - 2);
748 my_cells.clear();
749 ranks_to_send.clear();
750 ranks_done.clear();
751 for (Integer kk = 0; kk < nb_cell; ++kk) {
752 Int64 cell_uid = recv_infos[index];
753 ++index;
754 Int32 cell_owner = CheckedConvert::toInt32(recv_infos[index]);
755 ++index;
756 if (kk == 0 && current_layer == 1 && m_is_allocate)
757 // I am the cell with the smallest uid and therefore I
758 // position the node owner.
759 // TODO: do not do this here, but do it in a separate routine.
760 nodes_map.findItem(node_uid).toMutable().setOwner(cell_owner, my_rank);
761 if (is_verbose)
762 info() << " CELL=" << cell_uid << " owner=" << cell_owner;
763 if (cell_owner == my_rank) {
764 impl::ItemBase dcell = cells_map.tryFind(cell_uid);
765 if (dcell.null())
766 ARCANE_FATAL("Internal error: cell uid={0} is not in our mesh", cell_uid);
767 if (do_only_minimal_uid) {
768 // Add all cells around my node
769 for (CellLocalId c : current_node.cellIds())
770 my_cells.add(c);
771 }
772 else
773 my_cells.add(dcell.localId());
774 }
775 else {
776 if (ranks_done.find(cell_owner) == ranks_done.end()) {
777 ranks_to_send.add(cell_owner);
778 ranks_done.insert(cell_owner);
779 }
780 }
781 }
782
783 if (is_verbose) {
784 info() << "CELLS TO SEND: node_uid=" << node_uid
785 << " nb_rank=" << ranks_to_send.size()
786 << " nb_cell=" << my_cells.size();
787 info(4) << "CELLS TO SEND: node_uid=" << node_uid
788 << " rank=" << ranks_to_send
789 << " cell=" << my_cells;
790 }
791
792 for (Integer zrank = 0, zn = ranks_to_send.size(); zrank < zn; ++zrank) {
793 Int32 send_rank = ranks_to_send[zrank];
794 SubDomainItemMap::Data* d = cells_to_send.lookupAdd(send_rank);
795 Int32Array& c = d->value();
796 for (Integer zid = 0, zid_size = my_cells.size(); zid < zid_size; ++zid) {
797 // TODO: check if cell is already present and do not add it if it is not necessary.
798 c.add(my_cells[zid]);
799 }
800 }
801 }
802 }
803
804 info() << "GHOST V3 SERIALIZE CELLS";
805 _sendAndReceiveCells(cells_to_send);
806}
807
808/*---------------------------------------------------------------------------*/
809/*---------------------------------------------------------------------------*/
819{
820 IParallelMng* pm = m_parallel_mng;
821 Int32 my_rank = pm->commRank();
822 Int32 nb_rank = pm->commSize();
823 bool is_verbose = m_is_verbose;
824
826 boundary_node_sorter.setNeedIndexAndRank(false);
827
828 {
829 Timer::SimplePrinter sp(traceMng(), "Sorting boundary nodes");
830 boundary_node_sorter.sort(boundary_node_list);
831 }
832
833 if (is_verbose) {
834 ConstArrayView<BoundaryNodeInfo> all_bni = boundary_node_sorter.keys();
835 Integer n = all_bni.size();
836 for (Integer i = 0; i < n; ++i) {
837 const BoundaryNodeInfo& bni = all_bni[i];
838 info() << "NODES_KEY i=" << i
839 << " node=" << bni.node_uid
840 << " cell=" << bni.cell_uid
841 << " rank=" << bni.cell_owner;
842 }
843 }
844
845 // TODO: it is not necessary to send all the cells.
846 // to determine the owner of a node, it is sufficient
847 // for each PE to send its cell with the smallest UID.
848 // Then, each node needs to know the list
849 // of connected sub-domains to send the info. Each
850 // sub-domain, knowing this, will know to whom it must send
851 // the ghost cells.
852
853 {
854 ConstArrayView<BoundaryNodeInfo> all_bni = boundary_node_sorter.keys();
855 Integer n = all_bni.size();
856 // Since the same node may be present in the list of the previous proc, each PE
857 // (except 0) sends to the previous proc the beginning of its list which contains the same nodes.
858
859 UniqueArray<BoundaryNodeInfo> end_node_list;
860 Integer begin_own_list_index = 0;
861 if (n != 0 && my_rank != 0) {
862 if (BoundaryNodeBitonicSortTraits::isValid(all_bni[0])) {
863 Int64 node_uid = all_bni[0].node_uid;
864 for (Integer i = 0; i < n; ++i) {
865 if (all_bni[i].node_uid != node_uid) {
866 begin_own_list_index = i;
867 break;
868 }
869 else
870 end_node_list.add(all_bni[i]);
871 }
872 }
873 }
874 info() << "BEGIN_OWN_LIST_INDEX=" << begin_own_list_index << " end_node_list_size=" << end_node_list.size();
875 if (is_verbose) {
876 for (Integer k = 0, kn = end_node_list.size(); k < kn; ++k)
877 info() << " SEND node_uid=" << end_node_list[k].node_uid
878 << " cell_uid=" << end_node_list[k].cell_uid;
879 }
880
881 UniqueArray<BoundaryNodeInfo> end_node_list_recv;
882
884 Integer recv_message_size = 0;
885 Integer send_message_size = BoundaryNodeBitonicSortTraits::messageSize(end_node_list);
886
887 // Send and receive sizes first.
888 if (my_rank != (nb_rank - 1)) {
889 requests.add(pm->recv(IntegerArrayView(1, &recv_message_size), my_rank + 1, false));
890 }
891 if (my_rank != 0) {
892 requests.add(pm->send(IntegerConstArrayView(1, &send_message_size), my_rank - 1, false));
893 }
894 info() << "Send size=" << send_message_size << " Recv size=" << recv_message_size;
895 pm->waitAllRequests(requests);
896 requests.clear();
897
898 if (recv_message_size != 0) {
899 Int32 nb_element = BoundaryNodeInfo::nbElement(recv_message_size);
900 end_node_list_recv.resize(nb_element);
901 requests.add(BoundaryNodeBitonicSortTraits::recv(pm, my_rank + 1, end_node_list_recv));
902 }
903 if (send_message_size != 0)
904 requests.add(BoundaryNodeBitonicSortTraits::send(pm, my_rank - 1, end_node_list));
905
906 pm->waitAllRequests(requests);
907
908 boundary_node_list.clear();
909 boundary_node_list.addRange(all_bni.subConstView(begin_own_list_index, n - begin_own_list_index));
910 boundary_node_list.addRange(end_node_list_recv);
911 }
912}
913
914/*---------------------------------------------------------------------------*/
915/*---------------------------------------------------------------------------*/
916
917void GhostLayerBuilder2::
918_sendAndReceiveCells(SubDomainItemMap& cells_to_send)
919{
920 auto exchanger{ ParallelMngUtils::createExchangerRef(m_parallel_mng) };
921
922 const bool is_verbose = m_is_verbose;
923
924 // Envoie et réceptionne les mailles fantômes
925 for (SubDomainItemMap::Enumerator i_map(cells_to_send); ++i_map;) {
926 Int32 sd = i_map.data()->key();
927 Int32Array& items = i_map.data()->value();
928
929 // Comme la liste par sous-domaine peut contenir plusieurs
930 // fois la même maille, on trie la liste et on supprime les
931 // doublons
932 std::sort(std::begin(items), std::end(items));
933 auto new_end = std::unique(std::begin(items), std::end(items));
934 items.resize(CheckedConvert::toInteger(new_end - std::begin(items)));
935 if (is_verbose)
936 info(4) << "CELLS TO SEND SD=" << sd << " Items=" << items;
937 else
938 info(4) << "CELLS TO SEND SD=" << sd << " nb=" << items.size();
939 exchanger->addSender(sd);
940 }
941 exchanger->initializeCommunicationsMessages();
942 for (Integer i = 0, ns = exchanger->nbSender(); i < ns; ++i) {
943 ISerializeMessage* sm = exchanger->messageToSend(i);
944 Int32 rank = sm->destination().value();
945 ISerializer* s = sm->serializer();
946 Int32ConstArrayView items_to_send = cells_to_send[rank];
947 m_mesh->serializeCells(s, items_to_send);
948 }
949 exchanger->processExchange();
950 info(4) << "END EXCHANGE CELLS";
951 for (Integer i = 0, ns = exchanger->nbReceiver(); i < ns; ++i) {
952 ISerializeMessage* sm = exchanger->messageToReceive(i);
953 ISerializer* s = sm->serializer();
954 // Do not use DynamicMesh but use cell serializer as in GhostLayerBuilder v1
955 // m_mesh->addCells(s);
956 s->setMode(ISerializer::ModeGet);
957 ScopedPtrT<IItemFamilySerializer> cell_serializer(m_mesh->cellFamily()->policyMng()->createSerializer());
958 cell_serializer->deserializeItems(s, nullptr);
959 }
960 m_mesh_internal->printStats(TraceMessage::DEFAULT_LEVEL);
961}
962
963/*---------------------------------------------------------------------------*/
964/*---------------------------------------------------------------------------*/
974{
975 IParallelMng* pm = m_mesh->parallelMng();
976 Int32 my_rank = pm->commRank();
977 ItemInternalMap& faces_map = m_mesh_internal->facesMap();
978
979 const int shared_and_boundary_flags = ItemFlags::II_Shared | ItemFlags::II_SubDomainBoundary;
980
981 // Iterates over all faces and marks boundary nodes, edges and faces
982 faces_map.eachItem([&](Face face) {
983 bool is_sub_domain_boundary_face = false;
984 if (face.itemBase().flags() & ItemFlags::II_Boundary) {
985 is_sub_domain_boundary_face = true;
986 }
987 else {
988 if (face.nbCell() == 2 && (face.cell(0).owner() != my_rank || face.cell(1).owner() != my_rank))
989 is_sub_domain_boundary_face = true;
990 }
991 if (is_sub_domain_boundary_face) {
992 face.mutableItemBase().addFlags(shared_and_boundary_flags);
993 for (Item inode : face.nodes())
994 inode.mutableItemBase().addFlags(shared_and_boundary_flags);
995 for (Item iedge : face.edges())
996 iedge.mutableItemBase().addFlags(shared_and_boundary_flags);
997 }
998 });
999 _markBoundaryNodesFromEdges(node_layer);
1000}
1001
1002/*---------------------------------------------------------------------------*/
1003/*---------------------------------------------------------------------------*/
1004
1005void GhostLayerBuilder2::
1006_markBoundaryNodesFromEdges(ArrayView<Int32> node_layer)
1007{
1008 const bool is_non_manifold = m_mesh->meshKind().isNonManifold();
1009 if (!is_non_manifold)
1010 return;
1011
1012 const int shared_and_boundary_flags = ItemFlags::II_Shared | ItemFlags::II_SubDomainBoundary;
1013
1014 info() << "Mark boundary nodes from edges for non-manifold mesh";
1015 // Iterates over all edges.
1016 // If an edge is connected to only one 2D cell
1017 // whose owner we are, then it is a boundary edge
1018 // and we mark the corresponding nodes.
1019 IParallelMng* pm = m_mesh->parallelMng();
1020 Int32 my_rank = pm->commRank();
1021 ItemInternalMap& edges_map = m_mesh_internal->edgesMap();
1022 edges_map.eachItem([&](Edge edge) {
1023 Int32 nb_cell = edge.nbCell();
1024 Int32 nb_dim2_cell = 0;
1025 Int32 nb_own_dim2_cell = 0;
1026 for (Cell cell : edge.cells()) {
1027 Int32 dim = cell.typeInfo()->dimension();
1028 if (dim == 2) {
1029 ++nb_dim2_cell;
1030 if (cell.owner() == my_rank)
1031 ++nb_own_dim2_cell;
1032 }
1033 }
1034 if (nb_dim2_cell == nb_cell && nb_own_dim2_cell == 1) {
1035 edge.mutableItemBase().addFlags(shared_and_boundary_flags);
1036 for (Item inode : edge.nodes()) {
1037 inode.mutableItemBase().addFlags(shared_and_boundary_flags);
1038 node_layer[inode.localId()] = 1;
1039 }
1040 }
1041 });
1042}
1043
1044/*---------------------------------------------------------------------------*/
1045/*---------------------------------------------------------------------------*/
1046// This function handles versions 3 and 4 of ghost entity calculation.
1047extern "C++" void
1048_buildGhostLayerNewVersion(IMesh* mesh, bool is_allocate, Int32 version)
1049{
1050 GhostLayerBuilder2 glb(mesh, is_allocate, version);
1051 glb.addGhostLayers();
1052}
1053
1054/*---------------------------------------------------------------------------*/
1055/*---------------------------------------------------------------------------*/
1056
1057} // End namespace Arcane::mesh
1058
1059/*---------------------------------------------------------------------------*/
1060/*---------------------------------------------------------------------------*/
#define ARCANE_FATAL(...)
Macro throwing a FatalErrorException.
Integer size() const
Number of elements in the vector.
Modifiable view of an array of type T.
constexpr const_pointer data() const noexcept
Pointer to the start of the view.
Base class for 1D data vectors.
void addRange(ConstReferenceType val, Int64 n)
Adds n elements of value val to the end of the array.
void resize(Int64 s)
Changes the number of elements in the array to s.
void clear()
Removes the elements from the array.
void add(ConstReferenceType val)
Adds element val to the end of the array.
Cell of a mesh.
Definition Item.h:1300
Constant view of an array of type T.
constexpr const_pointer data() const noexcept
Pointer to the allocated memory.
constexpr Integer size() const noexcept
Number of elements in the array.
constexpr ConstArrayView< T > subConstView(Integer abegin, Integer asize) const noexcept
Sub-view (constant) starting from element abegin and containing asize elements.
Template class for converting a type.
Edge of a cell.
Definition Item.h:875
CellConnectedListViewType cells() const
List of edge cells.
Definition Item.h:984
Int32 nbCell() const
Number of cells connected to the edge.
Definition Item.h:978
Face of a cell.
Definition Item.h:1032
Cell cell(Int32 i) const
i-th cell of the face
Definition Item.h:1793
Int32 nbCell() const
Number of cells of the face (1 or 2).
Definition Item.h:1129
EdgeConnectedListViewType edges() const
List of edges of the face.
Definition Item.h:1246
Hash table for associative arrays.
virtual Int32 maxLocalId() const =0
virtual IItemFamily * nodeFamily()=0
Returns the node family.
Internal part of IMesh.
virtual mesh::ItemInternalMap & nodesMap()=0
virtual IParallelMng * parallelMng()=0
Parallelism manager.
virtual ARCANE_DEPRECATED_240 void serializeCells(ISerializer *buffer, Int32ConstArrayView cells_local_id)=0
virtual const MeshKind meshKind() const =0
Mesh characteristics.
Interface of the parallelism manager for a subdomain.
virtual Int32 commRank() const =0
Rank of this instance in the communicator.
virtual void recv(ArrayView< char > values, Int32 rank)=0
virtual Int32 commSize() const =0
Number of instances in the communicator.
virtual void waitAllRequests(ArrayView< Request > rvalues)=0
Blocks while waiting for the rvalues requests to complete.
virtual bool isParallel() const =0
Returns true if the execution is parallel.
MutableItemBase toMutable()
Mutable interface of this entity.
Int32 flags() const
Flags of the entity.
@ II_Shared
The entity is shared by another subdomain.
Definition ItemFlags.h:59
@ II_SubDomainBoundary
The entity is at the boundary of two subdomains.
Definition ItemFlags.h:60
@ II_Boundary
The entity is on the boundary.
Definition ItemFlags.h:51
Internal structure of a mesh entity.
Utility class for printing information about an entity.
Definition ItemPrinter.h:35
Int16 dimension() const
Dimension of the element (<0 if unknown).
NodeConnectedListViewType nodes() const
List of nodes of the entity.
Definition Item.h:843
NodeLocalIdView nodeIds() const
List of nodes of the entity.
Definition Item.h:846
Base class for a mesh element.
Definition Item.h:84
const ItemTypeInfo * typeInfo() const
Information about the entity type.
Definition Item.h:406
impl::MutableItemBase mutableItemBase() const
Mutable internal part of the entity.
Definition Item.h:394
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
ItemUniqueId uniqueId() const
Unique identifier across all domains.
Definition Item.h:239
constexpr bool isOwn() const
true if the entity belongs to the subdomain
Definition Item.h:267
impl::ItemBase itemBase() const
Internal part of the entity.
Definition Item.h:383
bool isNonManifold() const
True if the mesh structure is eMeshCellDimensionKind::NonManifold.
Definition MeshKind.h:120
void setOwner(Integer suid, Int32 current_sub_domain)
Sets the sub-domain number of the entity owner.
void addFlags(Int32 added_flags)
Adds the flags added_flags to those of the entity.
Node of a mesh.
Definition Item.h:598
Parallel bitonic sort algorithm.
ConstArrayView< KeyType > keys() const override
After a sort, returns the list of elements on this rank.
void sort(ConstArrayView< KeyType > keys) override
Parallelly sorts the elements of keys on all ranks.
1D vector of data with reference semantics.
Displays the time elapsed between the call to the constructor and the destructor.
Definition Timer.h:175
TraceAccessor(ITraceMng *m)
Constructs an accessor via the trace manager m.
TraceMessage info() const
Flow for an information message.
TraceMessage warning() const
Flow for a warning message.
ITraceMng * traceMng() const
Trace manager.
1D data vector with value semantics (STL style).
Functor for sorting BoundaryNodeInfo via bitonic sort.
Structure containing boundary node information.
Construction of ghost layers.
void _sortBoundaryNodeList(Array< BoundaryNodeInfo > &boundary_node_list)
Parallel sorting of the list of boundary node information.
void _markBoundaryItems(ArrayView< Int32 > node_layer)
Marks the entities at the edge of the sub-domain.
void addGhostLayers()
Adds ghost cell layers.
GhostLayerBuilder2(IMesh *mesh, bool is_allocate, Int32 version)
Constructs an instance for the mesh mesh.
void _markBoundaryNodes(ArrayView< Int32 > node_layer)
Determines the boundary nodes.
Associative array of ItemInternal.
impl::ItemBase findItem(Int64 uid) const
Returns the unique ID entity uid.
void eachItem(const Lambda &lambda)
Template function to iterate over the instance's entities.
impl::ItemBase tryFind(Int64 key) const
Returns the entity associated with key if found, or the null entity otherwise.
Ref< IParallelExchanger > createExchangerRef(IParallelMng *pm)
Returns an interface to transfer messages between ranks.
ArrayView< Int64 > Int64ArrayView
C equivalent of a 1D array of 64-bit integers.
Definition UtilsTypes.h:445
std::int64_t Int64
Signed integer type of 64 bits.
Int32 Integer
Type representing an integer.
ConstArrayView< Int32 > Int32ConstArrayView
C equivalent of a 1D array of 32-bit integers.
Definition UtilsTypes.h:476
ArrayView< Integer > IntegerArrayView
C equivalent of a 1D array of integers.
Definition UtilsTypes.h:451
ConstArrayView< Int64 > Int64ConstArrayView
C equivalent of a 1D array of 64-bit integers.
Definition UtilsTypes.h:474
UniqueArray< Int32 > Int32UniqueArray
Dynamic 1D array of 32-bit integers.
Definition UtilsTypes.h:335
Array< Int32 > Int32Array
Dynamic one-dimensional array of 32-bit integers.
Definition UtilsTypes.h:121
UniqueArray< Integer > IntegerUniqueArray
Dynamic 1D array of integers.
Definition UtilsTypes.h:341
ConstArrayView< Integer > IntegerConstArrayView
C equivalent of a 1D array of integers.
Definition UtilsTypes.h:480
std::int32_t Int32
Signed integer type of 32 bits.