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