Arcane  4.1.12.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
471 SharedArray<BoundaryNodeInfo> boundary_node_list;
472 //boundary_node_list.reserve(boundary_nodes_uid_count);
473
474 IParallelMng* pm = m_parallel_mng;
475 Int32 my_rank = pm->commRank();
476 Int32 nb_rank = pm->commSize();
477
478 bool is_verbose = m_is_verbose;
479
480 ItemInternalMap& cells_map = m_mesh->cellsMap();
481 ItemInternalMap& nodes_map = m_mesh->nodesMap();
482
483 Int64 nb_added_for_different_rank = 0;
484 Int64 nb_added_for_in_layer = 0;
485
486 const Int32 max_local_id = m_mesh->nodeFamily()->maxLocalId();
487
488 // Arrays containing for each node the uid of the smallest connected cell
489 // and the associated rank. If the uid is A_NULL_UNIQUE_ID, this node should not be added.
490 UniqueArray<Int64> node_cell_uids(max_local_id, NULL_ITEM_UNIQUE_ID);
491
492 const bool do_only_minimal_uid = m_use_only_minimal_cell_uid;
493 // We must send all nodes whose layer number is different from (-1).
494 // NOTE: for the layer above 1, only one value must be sent.
495 cells_map.eachItem([&](Cell cell) {
496 // Do not process cells that do not belong to me
497 if (m_version >= 4 && cell.owner() != my_rank)
498 return;
499 Int64 cell_uid = cell.uniqueId();
500 for (Node node : cell.nodes()) {
501 Int32 node_lid = node.localId();
502 bool do_it = false;
503 if (cell.owner() != my_rank) {
504 do_it = true;
505 ++nb_added_for_different_rank;
506 }
507 else {
508 Integer layer = node_layer[node_lid];
509 do_it = layer <= current_layer;
510 if (do_it)
511 ++nb_added_for_in_layer;
512 }
513 if (do_it) {
514 Int32 node_lid = node.localId();
515 if (do_only_minimal_uid) {
516 Int64 current_uid = node_cell_uids[node_lid];
517 if ((current_uid == NULL_ITEM_UNIQUE_ID) || cell_uid < current_uid) {
518 node_cell_uids[node_lid] = cell_uid;
519 if (is_verbose)
520 info() << "AddNode node_uid=" << node.uniqueId() << " cell=" << cell_uid;
521 }
522 else if (is_verbose)
523 info() << "AddNode node_uid=" << node.uniqueId() << " cell=" << cell_uid << " not done current=" << current_uid;
524 }
525 else {
526 Int64 node_uid = node.uniqueId();
528 nci.node_uid = node_uid;
529 nci.cell_uid = cell_uid;
530 nci.cell_owner = my_rank;
531 boundary_node_list.add(nci);
532 if (is_verbose)
533 info() << "AddNode node_uid=" << node.uniqueId() << " cell=" << cell_uid;
534 }
535 }
536 }
537 });
538
539 if (do_only_minimal_uid) {
540 nodes_map.eachItem([&](Node node) {
541 Int32 lid = node.localId();
542 Int64 cell_uid = node_cell_uids[lid];
543 if (cell_uid != NULL_ITEM_UNIQUE_ID) {
544 Int64 node_uid = node.uniqueId();
546 nci.node_uid = node_uid;
547 nci.cell_uid = cell_uid;
548 nci.cell_owner = my_rank;
549 boundary_node_list.add(nci);
550 }
551 });
552 }
553
554 info() << "NB BOUNDARY NODE LIST=" << boundary_node_list.size()
555 << " nb_added_for_different_rank=" << nb_added_for_different_rank
556 << " nb_added_for_in_layer=" << nb_added_for_in_layer
557 << " do_only_minimal=" << do_only_minimal_uid;
558
559 _sortBoundaryNodeList(boundary_node_list);
560 SharedArray<BoundaryNodeInfo> all_boundary_node_info = boundary_node_list;
561
562 UniqueArray<BoundaryNodeToSendInfo> node_list_to_send;
563 {
564 ConstArrayView<BoundaryNodeInfo> all_bni = all_boundary_node_info;
565 Integer bi_n = all_bni.size();
566 for (Integer i = 0; i < bi_n; ++i) {
567 const BoundaryNodeInfo& bni = all_bni[i];
568 // Searches all elements of all_bni that have the same node.
569 // This represents all cells connected to this node.
570 Int64 node_uid = bni.node_uid;
571 Integer last_i = i;
572 for (; last_i < bi_n; ++last_i)
573 if (all_bni[last_i].node_uid != node_uid)
574 break;
575 Integer nb_same_node = (last_i - i);
576 if (is_verbose)
577 info() << "NB_SAME_NODE uid=" << node_uid << " n=" << nb_same_node << " last_i=" << last_i;
578 // Now, check if the cells connected to this node have the same owner.
579 // If this is the case, it is a true boundary node and nothing needs to be done.
580 // Otherwise, the list of cells must be sent to all PEs whose ranks appear in this list
581 Int32 owner = bni.cell_owner;
582 bool has_ghost = false;
583 for (Integer z = 0; z < nb_same_node; ++z)
584 if (all_bni[i + z].cell_owner != owner) {
585 has_ghost = true;
586 break;
587 }
588 if (has_ghost) {
590 si.m_index = i;
591 si.m_nb_cell = nb_same_node;
592 node_list_to_send.add(si);
593 if (is_verbose)
594 info() << "Add ghost uid=" << node_uid << " index=" << i << " nb_same_node=" << nb_same_node;
595 }
596 i = last_i - 1;
597 }
598 }
599
600 IntegerUniqueArray nb_info_to_send(nb_rank, 0);
601 {
602 ConstArrayView<BoundaryNodeInfo> all_bni = all_boundary_node_info;
603 Integer nb_node_to_send = node_list_to_send.size();
604 std::set<Int32> ranks_done;
605 for (Integer i = 0; i < nb_node_to_send; ++i) {
606 Integer index = node_list_to_send[i].m_index;
607 Integer nb_cell = node_list_to_send[i].m_nb_cell;
608
609 ranks_done.clear();
610
611 for (Integer kz = 0; kz < nb_cell; ++kz) {
612 Int32 krank = all_bni[index + kz].cell_owner;
613 if (ranks_done.find(krank) == ranks_done.end()) {
614 ranks_done.insert(krank);
615 // For each one, it will be necessary to send
616 // - the number of cells (1*Int64)
617 // - the node uid (1*Int64)
618 // - the uid and rank of each cell (2*Int64*nb_cell)
619 //TODO: it is possible to store the ranks as Int32
620 nb_info_to_send[krank] += (nb_cell * 2) + 2;
621 }
622 }
623 }
624 }
625
626 if (is_verbose) {
627 for (Integer i = 0; i < nb_rank; ++i) {
628 Integer nb_to_send = nb_info_to_send[i];
629 if (nb_to_send != 0)
630 info() << "NB_TO_SEND rank=" << i << " n=" << nb_to_send;
631 }
632 }
633
634 Integer total_nb_to_send = 0;
635 IntegerUniqueArray nb_info_to_send_indexes(nb_rank, 0);
636 for (Integer i = 0; i < nb_rank; ++i) {
637 nb_info_to_send_indexes[i] = total_nb_to_send;
638 total_nb_to_send += nb_info_to_send[i];
639 }
640 info() << "TOTAL_NB_TO_SEND=" << total_nb_to_send;
641
642 UniqueArray<Int64> resend_infos(total_nb_to_send);
643 {
644 ConstArrayView<BoundaryNodeInfo> all_bni = all_boundary_node_info;
645 Integer nb_node_to_send = node_list_to_send.size();
646 std::set<Int32> ranks_done;
647 for (Integer i = 0; i < nb_node_to_send; ++i) {
648 Integer node_index = node_list_to_send[i].m_index;
649 Integer nb_cell = node_list_to_send[i].m_nb_cell;
650 Int64 node_uid = all_bni[node_index].node_uid;
651
652 ranks_done.clear();
653
654 for (Integer kz = 0; kz < nb_cell; ++kz) {
655 Int32 krank = all_bni[node_index + kz].cell_owner;
656 if (ranks_done.find(krank) == ranks_done.end()) {
657 ranks_done.insert(krank);
658 Integer send_index = nb_info_to_send_indexes[krank];
659 resend_infos[send_index] = node_uid;
660 ++send_index;
661 resend_infos[send_index] = nb_cell;
662 ++send_index;
663 for (Integer zz = 0; zz < nb_cell; ++zz) {
664 resend_infos[send_index] = all_bni[node_index + zz].cell_uid;
665 ++send_index;
666 resend_infos[send_index] = all_bni[node_index + zz].cell_owner;
667 ++send_index;
668 }
669 nb_info_to_send_indexes[krank] = send_index;
670 }
671 }
672 }
673 }
674
675 IntegerUniqueArray nb_info_to_recv(nb_rank, 0);
676 {
677 Timer::SimplePrinter sp(traceMng(), "Sending size with AllToAll");
678 pm->allToAll(nb_info_to_send, nb_info_to_recv, 1);
679 }
680
681 if (is_verbose)
682 for (Integer i = 0; i < nb_rank; ++i)
683 info() << "NB_TO_RECV: I=" << i << " n=" << nb_info_to_recv[i];
684
685 Integer total_nb_to_recv = 0;
686 for (Integer i = 0; i < nb_rank; ++i)
687 total_nb_to_recv += nb_info_to_recv[i];
688
689 // There is a high chance that this will not work if the array is too large,
690 // one must proceed with arrays that do not exceed 2Go because of the
691 // MPI Int32.
692 // TODO: Perform the AllToAll in several stages if necessary.
693 // TODO: Merge this code with that of FaceUniqueIdBuilder2.
694 UniqueArray<Int64> recv_infos;
695 {
696 Int32 vsize = sizeof(Int64) / sizeof(Int64);
697 Int32UniqueArray send_counts(nb_rank);
698 Int32UniqueArray send_indexes(nb_rank);
699 Int32UniqueArray recv_counts(nb_rank);
700 Int32UniqueArray recv_indexes(nb_rank);
701 Int32 total_send = 0;
702 Int32 total_recv = 0;
703 for (Integer i = 0; i < nb_rank; ++i) {
704 send_counts[i] = (Int32)(nb_info_to_send[i] * vsize);
705 recv_counts[i] = (Int32)(nb_info_to_recv[i] * vsize);
706 send_indexes[i] = total_send;
707 recv_indexes[i] = total_recv;
708 total_send += send_counts[i];
709 total_recv += recv_counts[i];
710 }
711 recv_infos.resize(total_nb_to_recv);
712
713 Int64ConstArrayView send_buf(total_nb_to_send * vsize, (Int64*)resend_infos.data());
714 Int64ArrayView recv_buf(total_nb_to_recv * vsize, (Int64*)recv_infos.data());
715
716 info() << "BUF_SIZES: send=" << send_buf.size() << " recv=" << recv_buf.size();
717 {
718 Timer::SimplePrinter sp(traceMng(), "Send values with AllToAll");
719 pm->allToAllVariable(send_buf, send_counts, send_indexes, recv_buf, recv_counts, recv_indexes);
720 }
721 }
722
723 SubDomainItemMap cells_to_send(50, true);
724
725 // TODO: we don't necessarily need the cells here, but
726 // only the list of procs to whom it must be sent. Then,
727 // if the proc knows to whom it must send, it can send the cells
728 // at that time. This allows sending less information in the previous AllToAll.
729
730 {
731 Integer index = 0;
732 UniqueArray<Int32> my_cells;
733 SharedArray<Int32> ranks_to_send;
734 std::set<Int32> ranks_done;
735 while (index < total_nb_to_recv) {
736 Int64 node_uid = recv_infos[index];
737 ++index;
738 Int64 nb_cell = recv_infos[index];
739 ++index;
740 Node current_node(nodes_map.findItem(node_uid));
741 if (is_verbose)
742 info() << "NODE uid=" << node_uid << " nb_cell=" << nb_cell << " idx=" << (index - 2);
743 my_cells.clear();
744 ranks_to_send.clear();
745 ranks_done.clear();
746 for (Integer kk = 0; kk < nb_cell; ++kk) {
747 Int64 cell_uid = recv_infos[index];
748 ++index;
749 Int32 cell_owner = CheckedConvert::toInt32(recv_infos[index]);
750 ++index;
751 if (kk == 0 && current_layer == 1 && m_is_allocate)
752 // I am the cell with the smallest uid and therefore I
753 // position the node owner.
754 // TODO: do not do this here, but do it in a separate routine.
755 nodes_map.findItem(node_uid).toMutable().setOwner(cell_owner, my_rank);
756 if (is_verbose)
757 info() << " CELL=" << cell_uid << " owner=" << cell_owner;
758 if (cell_owner == my_rank) {
759 impl::ItemBase dcell = cells_map.tryFind(cell_uid);
760 if (dcell.null())
761 ARCANE_FATAL("Internal error: cell uid={0} is not in our mesh", cell_uid);
762 if (do_only_minimal_uid) {
763 // Add all cells around my node
764 for (CellLocalId c : current_node.cellIds())
765 my_cells.add(c);
766 }
767 else
768 my_cells.add(dcell.localId());
769 }
770 else {
771 if (ranks_done.find(cell_owner) == ranks_done.end()) {
772 ranks_to_send.add(cell_owner);
773 ranks_done.insert(cell_owner);
774 }
775 }
776 }
777
778 if (is_verbose) {
779 info() << "CELLS TO SEND: node_uid=" << node_uid
780 << " nb_rank=" << ranks_to_send.size()
781 << " nb_cell=" << my_cells.size();
782 info(4) << "CELLS TO SEND: node_uid=" << node_uid
783 << " rank=" << ranks_to_send
784 << " cell=" << my_cells;
785 }
786
787 for (Integer zrank = 0, zn = ranks_to_send.size(); zrank < zn; ++zrank) {
788 Int32 send_rank = ranks_to_send[zrank];
789 SubDomainItemMap::Data* d = cells_to_send.lookupAdd(send_rank);
790 Int32Array& c = d->value();
791 for (Integer zid = 0, zid_size = my_cells.size(); zid < zid_size; ++zid) {
792 // TODO: check if cell is already present and do not add it if it is not necessary.
793 c.add(my_cells[zid]);
794 }
795 }
796 }
797 }
798
799 info() << "GHOST V3 SERIALIZE CELLS";
800 _sendAndReceiveCells(cells_to_send);
801}
802
803/*---------------------------------------------------------------------------*/
804/*---------------------------------------------------------------------------*/
814{
815 IParallelMng* pm = m_parallel_mng;
816 Int32 my_rank = pm->commRank();
817 Int32 nb_rank = pm->commSize();
818 bool is_verbose = m_is_verbose;
819
821 boundary_node_sorter.setNeedIndexAndRank(false);
822
823 {
824 Timer::SimplePrinter sp(traceMng(), "Sorting boundary nodes");
825 boundary_node_sorter.sort(boundary_node_list);
826 }
827
828 if (is_verbose) {
829 ConstArrayView<BoundaryNodeInfo> all_bni = boundary_node_sorter.keys();
830 Integer n = all_bni.size();
831 for (Integer i = 0; i < n; ++i) {
832 const BoundaryNodeInfo& bni = all_bni[i];
833 info() << "NODES_KEY i=" << i
834 << " node=" << bni.node_uid
835 << " cell=" << bni.cell_uid
836 << " rank=" << bni.cell_owner;
837 }
838 }
839
840 // TODO: it is not necessary to send all the cells.
841 // to determine the owner of a node, it is sufficient
842 // for each PE to send its cell with the smallest UID.
843 // Then, each node needs to know the list
844 // of connected sub-domains to send the info. Each
845 // sub-domain, knowing this, will know to whom it must send
846 // the ghost cells.
847
848 {
849 ConstArrayView<BoundaryNodeInfo> all_bni = boundary_node_sorter.keys();
850 Integer n = all_bni.size();
851 // Since the same node may be present in the list of the previous proc, each PE
852 // (except 0) sends to the previous proc the beginning of its list which contains the same nodes.
853
854 UniqueArray<BoundaryNodeInfo> end_node_list;
855 Integer begin_own_list_index = 0;
856 if (n != 0 && my_rank != 0) {
857 if (BoundaryNodeBitonicSortTraits::isValid(all_bni[0])) {
858 Int64 node_uid = all_bni[0].node_uid;
859 for (Integer i = 0; i < n; ++i) {
860 if (all_bni[i].node_uid != node_uid) {
861 begin_own_list_index = i;
862 break;
863 }
864 else
865 end_node_list.add(all_bni[i]);
866 }
867 }
868 }
869 info() << "BEGIN_OWN_LIST_INDEX=" << begin_own_list_index << " end_node_list_size=" << end_node_list.size();
870 if (is_verbose) {
871 for (Integer k = 0, kn = end_node_list.size(); k < kn; ++k)
872 info() << " SEND node_uid=" << end_node_list[k].node_uid
873 << " cell_uid=" << end_node_list[k].cell_uid;
874 }
875
876 UniqueArray<BoundaryNodeInfo> end_node_list_recv;
877
879 Integer recv_message_size = 0;
880 Integer send_message_size = BoundaryNodeBitonicSortTraits::messageSize(end_node_list);
881
882 // Send and receive sizes first.
883 if (my_rank != (nb_rank - 1)) {
884 requests.add(pm->recv(IntegerArrayView(1, &recv_message_size), my_rank + 1, false));
885 }
886 if (my_rank != 0) {
887 requests.add(pm->send(IntegerConstArrayView(1, &send_message_size), my_rank - 1, false));
888 }
889 info() << "Send size=" << send_message_size << " Recv size=" << recv_message_size;
890 pm->waitAllRequests(requests);
891 requests.clear();
892
893 if (recv_message_size != 0) {
894 Int32 nb_element = BoundaryNodeInfo::nbElement(recv_message_size);
895 end_node_list_recv.resize(nb_element);
896 requests.add(BoundaryNodeBitonicSortTraits::recv(pm, my_rank + 1, end_node_list_recv));
897 }
898 if (send_message_size != 0)
899 requests.add(BoundaryNodeBitonicSortTraits::send(pm, my_rank - 1, end_node_list));
900
901 pm->waitAllRequests(requests);
902
903 boundary_node_list.clear();
904 boundary_node_list.addRange(all_bni.subConstView(begin_own_list_index, n - begin_own_list_index));
905 boundary_node_list.addRange(end_node_list_recv);
906 }
907}
908
909/*---------------------------------------------------------------------------*/
910/*---------------------------------------------------------------------------*/
911
912void GhostLayerBuilder2::
913_sendAndReceiveCells(SubDomainItemMap& cells_to_send)
914{
915 auto exchanger{ ParallelMngUtils::createExchangerRef(m_parallel_mng) };
916
917 const bool is_verbose = m_is_verbose;
918
919 // Envoie et réceptionne les mailles fantômes
920 for (SubDomainItemMap::Enumerator i_map(cells_to_send); ++i_map;) {
921 Int32 sd = i_map.data()->key();
922 Int32Array& items = i_map.data()->value();
923
924 // Comme la liste par sous-domaine peut contenir plusieurs
925 // fois la même maille, on trie la liste et on supprime les
926 // doublons
927 std::sort(std::begin(items), std::end(items));
928 auto new_end = std::unique(std::begin(items), std::end(items));
929 items.resize(CheckedConvert::toInteger(new_end - std::begin(items)));
930 if (is_verbose)
931 info(4) << "CELLS TO SEND SD=" << sd << " Items=" << items;
932 else
933 info(4) << "CELLS TO SEND SD=" << sd << " nb=" << items.size();
934 exchanger->addSender(sd);
935 }
936 exchanger->initializeCommunicationsMessages();
937 for (Integer i = 0, ns = exchanger->nbSender(); i < ns; ++i) {
938 ISerializeMessage* sm = exchanger->messageToSend(i);
939 Int32 rank = sm->destination().value();
940 ISerializer* s = sm->serializer();
941 Int32ConstArrayView items_to_send = cells_to_send[rank];
942 m_mesh->serializeCells(s, items_to_send);
943 }
944 exchanger->processExchange();
945 info(4) << "END EXCHANGE CELLS";
946 for (Integer i = 0, ns = exchanger->nbReceiver(); i < ns; ++i) {
947 ISerializeMessage* sm = exchanger->messageToReceive(i);
948 ISerializer* s = sm->serializer();
949 m_mesh->addCells(s);
950 }
951 m_mesh_builder->printStats();
952}
953
954/*---------------------------------------------------------------------------*/
955/*---------------------------------------------------------------------------*/
965{
966 IParallelMng* pm = m_mesh->parallelMng();
967 Int32 my_rank = pm->commRank();
968 ItemInternalMap& faces_map = m_mesh->facesMap();
969
970 const int shared_and_boundary_flags = ItemFlags::II_Shared | ItemFlags::II_SubDomainBoundary;
971
972 // Iterates over all faces and marks boundary nodes, edges and faces
973 faces_map.eachItem([&](Face face) {
974 bool is_sub_domain_boundary_face = false;
975 if (face.itemBase().flags() & ItemFlags::II_Boundary) {
976 is_sub_domain_boundary_face = true;
977 }
978 else {
979 if (face.nbCell() == 2 && (face.cell(0).owner() != my_rank || face.cell(1).owner() != my_rank))
980 is_sub_domain_boundary_face = true;
981 }
982 if (is_sub_domain_boundary_face) {
983 face.mutableItemBase().addFlags(shared_and_boundary_flags);
984 for (Item inode : face.nodes())
985 inode.mutableItemBase().addFlags(shared_and_boundary_flags);
986 for (Item iedge : face.edges())
987 iedge.mutableItemBase().addFlags(shared_and_boundary_flags);
988 }
989 });
990 _markBoundaryNodesFromEdges(node_layer);
991}
992
993/*---------------------------------------------------------------------------*/
994/*---------------------------------------------------------------------------*/
995
996void GhostLayerBuilder2::
997_markBoundaryNodesFromEdges(ArrayView<Int32> node_layer)
998{
999 const bool is_non_manifold = m_mesh->meshKind().isNonManifold();
1000 if (!is_non_manifold)
1001 return;
1002
1003 const int shared_and_boundary_flags = ItemFlags::II_Shared | ItemFlags::II_SubDomainBoundary;
1004
1005 info() << "Mark boundary nodes from edges for non-manifold mesh";
1006 // Iterates over all edges.
1007 // If an edge is connected to only one 2D cell
1008 // whose owner we are, then it is a boundary edge
1009 // and we mark the corresponding nodes.
1010 IParallelMng* pm = m_mesh->parallelMng();
1011 Int32 my_rank = pm->commRank();
1012 ItemInternalMap& edges_map = m_mesh->edgesMap();
1013 edges_map.eachItem([&](Edge edge) {
1014 Int32 nb_cell = edge.nbCell();
1015 Int32 nb_dim2_cell = 0;
1016 Int32 nb_own_dim2_cell = 0;
1017 for (Cell cell : edge.cells()) {
1018 Int32 dim = cell.typeInfo()->dimension();
1019 if (dim == 2) {
1020 ++nb_dim2_cell;
1021 if (cell.owner() == my_rank)
1022 ++nb_own_dim2_cell;
1023 }
1024 }
1025 if (nb_dim2_cell == nb_cell && nb_own_dim2_cell == 1) {
1026 edge.mutableItemBase().addFlags(shared_and_boundary_flags);
1027 for (Item inode : edge.nodes()) {
1028 inode.mutableItemBase().addFlags(shared_and_boundary_flags);
1029 node_layer[inode.localId()] = 1;
1030 }
1031 }
1032 });
1033}
1034
1035/*---------------------------------------------------------------------------*/
1036/*---------------------------------------------------------------------------*/
1037// This function handles versions 3 and 4 of ghost entity calculation.
1038extern "C++" void
1039_buildGhostLayerNewVersion(DynamicMesh* mesh, bool is_allocate, Int32 version)
1040{
1041 GhostLayerBuilder2 glb(mesh->m_mesh_builder, is_allocate, version);
1042 glb.addGhostLayers();
1043}
1044
1045/*---------------------------------------------------------------------------*/
1046/*---------------------------------------------------------------------------*/
1047
1048} // End namespace Arcane::mesh
1049
1050/*---------------------------------------------------------------------------*/
1051/*---------------------------------------------------------------------------*/
#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:451
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:482
ArrayView< Integer > IntegerArrayView
C equivalent of a 1D array of integers.
Definition UtilsTypes.h:457
ConstArrayView< Int64 > Int64ConstArrayView
C equivalent of a 1D array of 64-bit integers.
Definition UtilsTypes.h:480
UniqueArray< Int32 > Int32UniqueArray
Dynamic 1D array of 32-bit integers.
Definition UtilsTypes.h:341
Array< Int32 > Int32Array
Dynamic one-dimensional array of 32-bit integers.
Definition UtilsTypes.h:127
UniqueArray< Integer > IntegerUniqueArray
Dynamic 1D array of integers.
Definition UtilsTypes.h:347
ConstArrayView< Integer > IntegerConstArrayView
C equivalent of a 1D array of integers.
Definition UtilsTypes.h:486
std::int32_t Int32
Signed integer type of 32 bits.