Arcane  4.2.1.0
Documentation développeur
Chargement...
Recherche...
Aucune correspondance
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 des couches fantômes. */
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
300{
301 IParallelMng* pm = m_parallel_mng;
302 if (!pm->isParallel())
303 return;
304 Integer nb_ghost_layer = m_mesh->ghostLayerMng()->nbGhostLayer();
305 info() << "** GHOST LAYER BUILDER V" << m_version << " with sort (nb_ghost_layer=" << nb_ghost_layer << ")";
306
307 // Couche fantôme à laquelle appartient le noeud.
308 UniqueArray<Integer> node_layer(m_mesh->nodeFamily()->maxLocalId(), -1);
309
310 // Marque les noeuds frontières
311 // On le fait toujours même si on ne veut pas de couche de mailles fantômes
312 _markBoundaryItems(node_layer);
313
314 if (nb_ghost_layer == 0)
315 return;
316 const Int32 my_rank = pm->commRank();
317
318 const bool is_non_manifold = m_mesh->meshKind().isNonManifold();
319 if (is_non_manifold && (m_version != 3))
320 ARCANE_FATAL("Only version 3 of ghostlayer builder is supported for non manifold meshes");
321
322 ItemInternalMap& cells_map = m_mesh->cellsMap();
323 ItemInternalMap& nodes_map = m_mesh->nodesMap();
324
325 Integer boundary_nodes_uid_count = 0;
326
327 // Vérifie qu'il n'y a pas de mailles fantômes avec la version 3.
328 // Si c'est le cas, affiche un avertissement et indique de passer à la version 4.
329 if (m_version == 3) {
330 Integer nb_ghost = 0;
331 cells_map.eachItem([&](Item cell) {
332 if (!cell.isOwn())
333 ++nb_ghost;
334 });
335 if (nb_ghost != 0)
336 warning() << "Invalid call to addGhostLayers() with version 3 because mesh "
337 << " already has '" << nb_ghost << "' ghost cells. The computed ghost cells"
338 << " may be wrong. Use version 4 of ghost layer builder if you want to handle this case";
339 }
340
341 // Couche fantôme à laquelle appartient la maille.
342 UniqueArray<Integer> cell_layer(m_mesh->cellFamily()->maxLocalId(), -1);
343
344 if (m_version >= 4) {
345 _markBoundaryNodes(node_layer);
346 nodes_map.eachItem([&](Item node) {
347 if (node_layer[node.localId()] == 1)
348 ++boundary_nodes_uid_count;
349 });
350 }
351 else {
352 // Parcours les nœuds et calcule le nombre de nœuds frontières
353 // et marque la première couche
354 nodes_map.eachItem([&](Item node) {
355 Int32 f = node.itemBase().flags();
356 if (f & ItemFlags::II_Shared) {
357 node_layer[node.localId()] = 1;
358 ++boundary_nodes_uid_count;
359 }
360 });
361 }
362
363 info() << "NB BOUNDARY NODE=" << boundary_nodes_uid_count;
364
365 for (Integer current_layer = 1; current_layer <= nb_ghost_layer; ++current_layer) {
366 //Integer current_layer = 1;
367 info() << "Processing layer " << current_layer;
368 cells_map.eachItem([&](Cell cell) {
369 // Ne traite pas les mailles qui ne m'appartiennent pas
370 if (m_version >= 4 && cell.owner() != my_rank)
371 return;
372 //Int64 cell_uid = cell->uniqueId();
373 Int32 cell_lid = cell.localId();
374 if (cell_layer[cell_lid] != (-1))
375 return;
376 bool is_current_layer = false;
377 for (Int32 inode_local_id : cell.nodeIds()) {
378 Integer layer = node_layer[inode_local_id];
379 //info() << "NODE_LAYER lid=" << i_node->localId() << " layer=" << layer;
380 if (layer == current_layer) {
381 is_current_layer = true;
382 break;
383 }
384 }
385 if (is_current_layer) {
386 cell_layer[cell_lid] = current_layer;
387 //info() << "Current layer celluid=" << cell_uid;
388 // Si non marqué, initialise à la couche courante + 1.
389 for (Int32 inode_local_id : cell.nodeIds()) {
390 Integer layer = node_layer[inode_local_id];
391 if (layer == (-1)) {
392 //info() << "Marque node uid=" << i_node->uniqueId();
393 node_layer[inode_local_id] = current_layer + 1;
394 }
395 }
396 }
397 });
398 }
399
400 // Marque les nœuds pour lesquels on n'a pas encore assigné la couche fantôme.
401 // Pour eux, on indique qu'on est sur la couche 'nb_ghost_layer+1'.
402 // Le but est de ne jamais transférer ces noeuds.
403 // NOTE: Ce mécanisme a été ajouté en juillet 2024 pour la version 3.14.
404 // S'il fonctionne bien on pourra ne conserver que cette méthode.
405 if (m_use_optimized_node_layer) {
406 Integer nb_no_layer = 0;
407 nodes_map.eachItem([&](Node node) {
408 Int32 lid = node.localId();
409 Int32 layer = node_layer[lid];
410 if (layer <= 0) {
411 node_layer[lid] = nb_ghost_layer + 1;
412 ++nb_no_layer;
413 }
414 });
415 info() << "Mark remaining nodes nb=" << nb_no_layer;
416 }
417
418 for (Integer i = 1; i <= nb_ghost_layer; ++i)
419 _addGhostLayer(i, node_layer);
420}
421
422/*---------------------------------------------------------------------------*/
423/*---------------------------------------------------------------------------*/
438{
439 IParallelMng* pm = m_mesh->parallelMng();
440 const Int32 my_rank = pm->commRank();
441 ItemInternalMap& faces_map = m_mesh->facesMap();
442 // TODO: regarder s'il est correcte de modifier ItemFlags::II_SubDomainBoundary
443 const int shared_and_boundary_flags = ItemFlags::II_Shared | ItemFlags::II_SubDomainBoundary;
444 // Parcours les faces et marque les nœuds, arêtes et faces frontières
445 faces_map.eachItem([&](Face face) {
446 Int32 nb_own = 0;
447 for (Integer i = 0, n = face.nbCell(); i < n; ++i)
448 if (face.cell(i).owner() == my_rank)
449 ++nb_own;
450 if (nb_own == 1) {
451 face.mutableItemBase().addFlags(shared_and_boundary_flags);
452 //++nb_sub_domain_boundary_face;
453 for (Item inode : face.nodes()) {
454 inode.mutableItemBase().addFlags(shared_and_boundary_flags);
455 node_layer[inode.localId()] = 1;
456 }
457 for (Item iedge : face.edges())
458 iedge.mutableItemBase().addFlags(shared_and_boundary_flags);
459 }
460 });
461 _markBoundaryNodesFromEdges(node_layer);
462}
463
464/*---------------------------------------------------------------------------*/
465/*---------------------------------------------------------------------------*/
466
467void GhostLayerBuilder2::
468_addGhostLayer(Integer current_layer, Int32ConstArrayView node_layer)
469{
470 info() << "Processing ghost layer " << current_layer
471 << " node_layer_size=" << node_layer.size();
472
473 SharedArray<BoundaryNodeInfo> boundary_node_list;
474 //boundary_node_list.reserve(boundary_nodes_uid_count);
475
476 IParallelMng* pm = m_parallel_mng;
477 Int32 my_rank = pm->commRank();
478 Int32 nb_rank = pm->commSize();
479
480 bool is_verbose = m_is_verbose;
481
482 ItemInternalMap& cells_map = m_mesh->cellsMap();
483 ItemInternalMap& nodes_map = m_mesh->nodesMap();
484
485 Int64 nb_added_for_different_rank = 0;
486 Int64 nb_added_for_in_layer = 0;
487
488 const Int32 max_local_id = m_mesh->nodeFamily()->maxLocalId();
489
490 // Tableaux contenant pour chaque nœud le uid de la plus petite maille connectée
491 // et le rang associé. Si le uid est A_NULL_UNIQUE_ID il ne faut pas ajouter ce nœud.
492 UniqueArray<Int64> node_cell_uids(max_local_id, NULL_ITEM_UNIQUE_ID);
493
494 const bool do_only_minimal_uid = m_use_only_minimal_cell_uid;
495 // On doit envoyer tous les nœuds dont le numéro de couche est différent de (-1).
496 // NOTE: pour la couche au dessus de 1, il ne faut envoyer qu'une seule valeur.
497 cells_map.eachItem([&](Cell cell) {
498 // Ne traite pas les mailles qui ne m'appartiennent pas
499 // FIXME: (juillet 2026) désactiver temporairement cette vérification car elle ne fonctionne pas
500 // lorsque nous avons 2 ou plus de couches fantômes.
501 //if (m_version >= 4 && cell.owner() != my_rank)
502 //return;
503 Int64 cell_uid = cell.uniqueId();
504 for (Node node : cell.nodes()) {
505 Int32 node_lid = node.localId();
506 bool do_it = false;
507 if (cell.owner() != my_rank) {
508 do_it = true;
509 ++nb_added_for_different_rank;
510 }
511 else {
512 Integer layer = node_layer[node_lid];
513 do_it = layer <= current_layer;
514 if (do_it)
515 ++nb_added_for_in_layer;
516 }
517 if (do_it) {
518 Int32 node_lid = node.localId();
519 if (do_only_minimal_uid) {
520 Int64 current_uid = node_cell_uids[node_lid];
521 if ((current_uid == NULL_ITEM_UNIQUE_ID) || cell_uid < current_uid) {
522 node_cell_uids[node_lid] = cell_uid;
523 if (is_verbose)
524 info() << "AddNode node_uid=" << node.uniqueId() << " cell=" << cell_uid;
525 }
526 else if (is_verbose)
527 info() << "AddNode node_uid=" << node.uniqueId() << " cell=" << cell_uid << " not done current=" << current_uid;
528 }
529 else {
530 Int64 node_uid = node.uniqueId();
532 nci.node_uid = node_uid;
533 nci.cell_uid = cell_uid;
534 nci.cell_owner = my_rank;
535 boundary_node_list.add(nci);
536 if (is_verbose)
537 info() << "AddNode node_uid=" << node.uniqueId() << " cell=" << cell_uid;
538 }
539 }
540 }
541 });
542
543 if (do_only_minimal_uid) {
544 nodes_map.eachItem([&](Node node) {
545 Int32 lid = node.localId();
546 Int64 cell_uid = node_cell_uids[lid];
547 if (cell_uid != NULL_ITEM_UNIQUE_ID) {
548 Int64 node_uid = node.uniqueId();
550 nci.node_uid = node_uid;
551 nci.cell_uid = cell_uid;
552 nci.cell_owner = my_rank;
553 boundary_node_list.add(nci);
554 }
555 });
556 }
557
558 info() << "NB BOUNDARY NODE LIST=" << boundary_node_list.size()
559 << " nb_added_for_different_rank=" << nb_added_for_different_rank
560 << " nb_added_for_in_layer=" << nb_added_for_in_layer
561 << " do_only_minimal=" << do_only_minimal_uid;
562
563 _sortBoundaryNodeList(boundary_node_list);
564 SharedArray<BoundaryNodeInfo> all_boundary_node_info = boundary_node_list;
565
566 UniqueArray<BoundaryNodeToSendInfo> node_list_to_send;
567 {
568 ConstArrayView<BoundaryNodeInfo> all_bni = all_boundary_node_info;
569 Integer bi_n = all_bni.size();
570 for (Integer i = 0; i < bi_n; ++i) {
571 const BoundaryNodeInfo& bni = all_bni[i];
572 // Recherche tous les éléments de all_bni qui ont le même noeud.
573 // Cela représente toutes les mailles connectées à ce noeud.
574 Int64 node_uid = bni.node_uid;
575 Integer last_i = i;
576 for (; last_i < bi_n; ++last_i)
577 if (all_bni[last_i].node_uid != node_uid)
578 break;
579 Integer nb_same_node = (last_i - i);
580 if (is_verbose)
581 info() << "NB_SAME_NODE uid=" << node_uid << " n=" << nb_same_node << " last_i=" << last_i;
582 // Maintenant, regarde si les mailles connectées à ce noeud ont le même propriétaire.
583 // Si c'est le cas, il s'agit d'un vrai noeud frontière et il n'y a donc rien à faire.
584 // Sinon, il faudra envoyer la liste des mailles à tous les PE dont les rangs apparaissent dans cette liste
585 Int32 owner = bni.cell_owner;
586 bool has_ghost = false;
587 for (Integer z = 0; z < nb_same_node; ++z)
588 if (all_bni[i + z].cell_owner != owner) {
589 has_ghost = true;
590 break;
591 }
592 if (has_ghost) {
594 si.m_index = i;
595 si.m_nb_cell = nb_same_node;
596 node_list_to_send.add(si);
597 if (is_verbose)
598 info() << "Add ghost uid=" << node_uid << " index=" << i << " nb_same_node=" << nb_same_node;
599 }
600 i = last_i - 1;
601 }
602 }
603
604 IntegerUniqueArray nb_info_to_send(nb_rank, 0);
605 {
606 ConstArrayView<BoundaryNodeInfo> all_bni = all_boundary_node_info;
607 Integer nb_node_to_send = node_list_to_send.size();
608 std::set<Int32> ranks_done;
609 for (Integer i = 0; i < nb_node_to_send; ++i) {
610 Integer index = node_list_to_send[i].m_index;
611 Integer nb_cell = node_list_to_send[i].m_nb_cell;
612
613 ranks_done.clear();
614
615 for (Integer kz = 0; kz < nb_cell; ++kz) {
616 Int32 krank = all_bni[index + kz].cell_owner;
617 if (ranks_done.find(krank) == ranks_done.end()) {
618 ranks_done.insert(krank);
619 // Pour chacun, il faudra envoyer
620 // - le nombre de mailles (1*Int64)
621 // - le uid du noeud (1*Int64)
622 // - le uid et le rank de chaque maille (2*Int64*nb_cell)
623 //TODO: il est possible de stocker les rangs sur Int32
624 nb_info_to_send[krank] += (nb_cell * 2) + 2;
625 }
626 }
627 }
628 }
629
630 if (is_verbose) {
631 for (Integer i = 0; i < nb_rank; ++i) {
632 Integer nb_to_send = nb_info_to_send[i];
633 if (nb_to_send != 0)
634 info() << "NB_TO_SEND rank=" << i << " n=" << nb_to_send;
635 }
636 }
637
638 Integer total_nb_to_send = 0;
639 IntegerUniqueArray nb_info_to_send_indexes(nb_rank, 0);
640 for (Integer i = 0; i < nb_rank; ++i) {
641 nb_info_to_send_indexes[i] = total_nb_to_send;
642 total_nb_to_send += nb_info_to_send[i];
643 }
644 info() << "TOTAL_NB_TO_SEND=" << total_nb_to_send;
645
646 UniqueArray<Int64> resend_infos(total_nb_to_send);
647 {
648 ConstArrayView<BoundaryNodeInfo> all_bni = all_boundary_node_info;
649 Integer nb_node_to_send = node_list_to_send.size();
650 std::set<Int32> ranks_done;
651 for (Integer i = 0; i < nb_node_to_send; ++i) {
652 Integer node_index = node_list_to_send[i].m_index;
653 Integer nb_cell = node_list_to_send[i].m_nb_cell;
654 Int64 node_uid = all_bni[node_index].node_uid;
655
656 ranks_done.clear();
657
658 for (Integer kz = 0; kz < nb_cell; ++kz) {
659 Int32 krank = all_bni[node_index + kz].cell_owner;
660 if (ranks_done.find(krank) == ranks_done.end()) {
661 ranks_done.insert(krank);
662 Integer send_index = nb_info_to_send_indexes[krank];
663 resend_infos[send_index] = node_uid;
664 ++send_index;
665 resend_infos[send_index] = nb_cell;
666 ++send_index;
667 for (Integer zz = 0; zz < nb_cell; ++zz) {
668 resend_infos[send_index] = all_bni[node_index + zz].cell_uid;
669 ++send_index;
670 resend_infos[send_index] = all_bni[node_index + zz].cell_owner;
671 ++send_index;
672 }
673 nb_info_to_send_indexes[krank] = send_index;
674 }
675 }
676 }
677 }
678
679 IntegerUniqueArray nb_info_to_recv(nb_rank, 0);
680 {
681 Timer::SimplePrinter sp(traceMng(), "Sending size with AllToAll");
682 pm->allToAll(nb_info_to_send, nb_info_to_recv, 1);
683 }
684
685 if (is_verbose)
686 for (Integer i = 0; i < nb_rank; ++i)
687 info() << "NB_TO_RECV: I=" << i << " n=" << nb_info_to_recv[i];
688
689 Integer total_nb_to_recv = 0;
690 for (Integer i = 0; i < nb_rank; ++i)
691 total_nb_to_recv += nb_info_to_recv[i];
692
693 // Il y a de fortes chances que cela ne marche pas si le tableau est trop grand,
694 // il faut proceder avec des tableaux qui ne depassent pas 2Go a cause des
695 // Int32 de MPI.
696 // TODO: Faire le AllToAll en plusieurs fois si besoin.
697 // TOOD: Fusionner ce code avec celui de FaceUniqueIdBuilder2.
698 UniqueArray<Int64> recv_infos;
699 {
700 Int32 vsize = sizeof(Int64) / sizeof(Int64);
701 Int32UniqueArray send_counts(nb_rank);
702 Int32UniqueArray send_indexes(nb_rank);
703 Int32UniqueArray recv_counts(nb_rank);
704 Int32UniqueArray recv_indexes(nb_rank);
705 Int32 total_send = 0;
706 Int32 total_recv = 0;
707 for (Integer i = 0; i < nb_rank; ++i) {
708 send_counts[i] = (Int32)(nb_info_to_send[i] * vsize);
709 recv_counts[i] = (Int32)(nb_info_to_recv[i] * vsize);
710 send_indexes[i] = total_send;
711 recv_indexes[i] = total_recv;
712 total_send += send_counts[i];
713 total_recv += recv_counts[i];
714 }
715 recv_infos.resize(total_nb_to_recv);
716
717 Int64ConstArrayView send_buf(total_nb_to_send * vsize, (Int64*)resend_infos.data());
718 Int64ArrayView recv_buf(total_nb_to_recv * vsize, (Int64*)recv_infos.data());
719
720 info() << "BUF_SIZES: send=" << send_buf.size() << " recv=" << recv_buf.size();
721 {
722 Timer::SimplePrinter sp(traceMng(), "Send values with AllToAll");
723 pm->allToAllVariable(send_buf, send_counts, send_indexes, recv_buf, recv_counts, recv_indexes);
724 }
725 }
726
727 SubDomainItemMap cells_to_send(50, true);
728
729 // TODO: il n'y a a priori pas besoin d'avoir les mailles ici mais
730 // seulement la liste des procs a qui il faut envoyer. Ensuite,
731 // si le proc connait a qui il doit envoyer, il peut envoyer les mailles
732 // à ce moment la. Cela permet d'envoyer moins d'infos dans le AllToAll précédent
733
734 {
735 Integer index = 0;
736 UniqueArray<Int32> my_cells;
737 SharedArray<Int32> ranks_to_send;
738 std::set<Int32> ranks_done;
739 while (index < total_nb_to_recv) {
740 Int64 node_uid = recv_infos[index];
741 ++index;
742 Int64 nb_cell = recv_infos[index];
743 ++index;
744 Node current_node(nodes_map.findItem(node_uid));
745 if (is_verbose)
746 info() << "NODE uid=" << node_uid << " nb_cell=" << nb_cell << " idx=" << (index - 2);
747 my_cells.clear();
748 ranks_to_send.clear();
749 ranks_done.clear();
750 for (Integer kk = 0; kk < nb_cell; ++kk) {
751 Int64 cell_uid = recv_infos[index];
752 ++index;
753 Int32 cell_owner = CheckedConvert::toInt32(recv_infos[index]);
754 ++index;
755 if (kk == 0 && current_layer == 1 && m_is_allocate)
756 // Je suis la maille de plus petit uid et donc je
757 // positionne le propriétaire du noeud.
758 // TODO: ne pas faire cela ici, mais le faire dans une routine à part.
759 nodes_map.findItem(node_uid).toMutable().setOwner(cell_owner, my_rank);
760 if (is_verbose)
761 info() << " CELL=" << cell_uid << " owner=" << cell_owner;
762 if (cell_owner == my_rank) {
763 impl::ItemBase dcell = cells_map.tryFind(cell_uid);
764 if (dcell.null())
765 ARCANE_FATAL("Internal error: cell uid={0} is not in our mesh", cell_uid);
766 if (do_only_minimal_uid) {
767 // Ajoute toutes les mailles autour de mon noeud
768 for (CellLocalId c : current_node.cellIds())
769 my_cells.add(c);
770 }
771 else
772 my_cells.add(dcell.localId());
773 }
774 else {
775 if (ranks_done.find(cell_owner) == ranks_done.end()) {
776 ranks_to_send.add(cell_owner);
777 ranks_done.insert(cell_owner);
778 }
779 }
780 }
781
782 if (is_verbose) {
783 info() << "CELLS TO SEND: node_uid=" << node_uid
784 << " nb_rank=" << ranks_to_send.size()
785 << " nb_cell=" << my_cells.size();
786 info(4) << "CELLS TO SEND: node_uid=" << node_uid
787 << " rank=" << ranks_to_send
788 << " cell=" << my_cells;
789 }
790
791 for (Integer zrank = 0, zn = ranks_to_send.size(); zrank < zn; ++zrank) {
792 Int32 send_rank = ranks_to_send[zrank];
793 SubDomainItemMap::Data* d = cells_to_send.lookupAdd(send_rank);
794 Int32Array& c = d->value();
795 for (Integer zid = 0, zid_size = my_cells.size(); zid < zid_size; ++zid) {
796 // TODO: regarder si maille pas déjà présente et ne pas l'ajouter si ce n'est pas nécessaire.
797 c.add(my_cells[zid]);
798 }
799 }
800 }
801 }
802
803 info() << "GHOST V3 SERIALIZE CELLS";
804 _sendAndReceiveCells(cells_to_send);
805}
806
807/*---------------------------------------------------------------------------*/
808/*---------------------------------------------------------------------------*/
818{
819 IParallelMng* pm = m_parallel_mng;
820 Int32 my_rank = pm->commRank();
821 Int32 nb_rank = pm->commSize();
822 bool is_verbose = m_is_verbose;
823
825 boundary_node_sorter.setNeedIndexAndRank(false);
826
827 {
828 Timer::SimplePrinter sp(traceMng(), "Sorting boundary nodes");
829 boundary_node_sorter.sort(boundary_node_list);
830 }
831
832 if (is_verbose) {
833 ConstArrayView<BoundaryNodeInfo> all_bni = boundary_node_sorter.keys();
834 Integer n = all_bni.size();
835 for (Integer i = 0; i < n; ++i) {
836 const BoundaryNodeInfo& bni = all_bni[i];
837 info() << "NODES_KEY i=" << i
838 << " node=" << bni.node_uid
839 << " cell=" << bni.cell_uid
840 << " rank=" << bni.cell_owner;
841 }
842 }
843
844 // TODO: il n'y a pas besoin d'envoyer toutes les mailles.
845 // pour déterminer le propriétaire d'un noeud, il suffit
846 // que chaque PE envoie sa maille de plus petit UID.
847 // Ensuite, chaque noeud a besoin de savoir la liste
848 // des sous-domaines connectés pour renvoyer l'info. Chaque
849 // sous-domaine en sachant cela saura a qui il doit envoyer
850 // les mailles fantomes.
851
852 {
853 ConstArrayView<BoundaryNodeInfo> all_bni = boundary_node_sorter.keys();
854 Integer n = all_bni.size();
855 // Comme un même noeud peut être présent dans la liste du proc précédent, chaque PE
856 // (sauf le 0) envoie au proc précédent le début sa liste qui contient les même noeuds.
857
858 UniqueArray<BoundaryNodeInfo> end_node_list;
859 Integer begin_own_list_index = 0;
860 if (n != 0 && my_rank != 0) {
861 if (BoundaryNodeBitonicSortTraits::isValid(all_bni[0])) {
862 Int64 node_uid = all_bni[0].node_uid;
863 for (Integer i = 0; i < n; ++i) {
864 if (all_bni[i].node_uid != node_uid) {
865 begin_own_list_index = i;
866 break;
867 }
868 else
869 end_node_list.add(all_bni[i]);
870 }
871 }
872 }
873 info() << "BEGIN_OWN_LIST_INDEX=" << begin_own_list_index << " end_node_list_size=" << end_node_list.size();
874 if (is_verbose) {
875 for (Integer k = 0, kn = end_node_list.size(); k < kn; ++k)
876 info() << " SEND node_uid=" << end_node_list[k].node_uid
877 << " cell_uid=" << end_node_list[k].cell_uid;
878 }
879
880 UniqueArray<BoundaryNodeInfo> end_node_list_recv;
881
883 Integer recv_message_size = 0;
884 Integer send_message_size = BoundaryNodeBitonicSortTraits::messageSize(end_node_list);
885
886 // Envoie et réceptionne d'abord les tailles.
887 if (my_rank != (nb_rank - 1)) {
888 requests.add(pm->recv(IntegerArrayView(1, &recv_message_size), my_rank + 1, false));
889 }
890 if (my_rank != 0) {
891 requests.add(pm->send(IntegerConstArrayView(1, &send_message_size), my_rank - 1, false));
892 }
893 info() << "Send size=" << send_message_size << " Recv size=" << recv_message_size;
894 pm->waitAllRequests(requests);
895 requests.clear();
896
897 if (recv_message_size != 0) {
898 Int32 nb_element = BoundaryNodeInfo::nbElement(recv_message_size);
899 end_node_list_recv.resize(nb_element);
900 requests.add(BoundaryNodeBitonicSortTraits::recv(pm, my_rank + 1, end_node_list_recv));
901 }
902 if (send_message_size != 0)
903 requests.add(BoundaryNodeBitonicSortTraits::send(pm, my_rank - 1, end_node_list));
904
905 pm->waitAllRequests(requests);
906
907 boundary_node_list.clear();
908 boundary_node_list.addRange(all_bni.subConstView(begin_own_list_index, n - begin_own_list_index));
909 boundary_node_list.addRange(end_node_list_recv);
910 }
911}
912
913/*---------------------------------------------------------------------------*/
914/*---------------------------------------------------------------------------*/
915
916void GhostLayerBuilder2::
917_sendAndReceiveCells(SubDomainItemMap& cells_to_send)
918{
919 auto exchanger{ ParallelMngUtils::createExchangerRef(m_parallel_mng) };
920
921 const bool is_verbose = m_is_verbose;
922
923 // Envoie et réceptionne les mailles fantômes
924 for (SubDomainItemMap::Enumerator i_map(cells_to_send); ++i_map;) {
925 Int32 sd = i_map.data()->key();
926 Int32Array& items = i_map.data()->value();
927
928 // Comme la liste par sous-domaine peut contenir plusieurs
929 // fois la même maille, on trie la liste et on supprime les
930 // doublons
931 std::sort(std::begin(items), std::end(items));
932 auto new_end = std::unique(std::begin(items), std::end(items));
933 items.resize(CheckedConvert::toInteger(new_end - std::begin(items)));
934 if (is_verbose)
935 info(4) << "CELLS TO SEND SD=" << sd << " Items=" << items;
936 else
937 info(4) << "CELLS TO SEND SD=" << sd << " nb=" << items.size();
938 exchanger->addSender(sd);
939 }
940 exchanger->initializeCommunicationsMessages();
941 for (Integer i = 0, ns = exchanger->nbSender(); i < ns; ++i) {
942 ISerializeMessage* sm = exchanger->messageToSend(i);
943 Int32 rank = sm->destination().value();
944 ISerializer* s = sm->serializer();
945 Int32ConstArrayView items_to_send = cells_to_send[rank];
946 m_mesh->serializeCells(s, items_to_send);
947 }
948 exchanger->processExchange();
949 info(4) << "END EXCHANGE CELLS";
950 for (Integer i = 0, ns = exchanger->nbReceiver(); i < ns; ++i) {
951 ISerializeMessage* sm = exchanger->messageToReceive(i);
952 ISerializer* s = sm->serializer();
953 m_mesh->addCells(s);
954 }
955 m_mesh_builder->printStats();
956}
957
958/*---------------------------------------------------------------------------*/
959/*---------------------------------------------------------------------------*/
969{
970 IParallelMng* pm = m_mesh->parallelMng();
971 Int32 my_rank = pm->commRank();
972 ItemInternalMap& faces_map = m_mesh->facesMap();
973
974 const int shared_and_boundary_flags = ItemFlags::II_Shared | ItemFlags::II_SubDomainBoundary;
975
976 // Parcours les faces et marque les nœuds, arêtes et faces frontières
977 faces_map.eachItem([&](Face face) {
978 bool is_sub_domain_boundary_face = false;
979 if (face.itemBase().flags() & ItemFlags::II_Boundary) {
980 is_sub_domain_boundary_face = true;
981 }
982 else {
983 if (face.nbCell() == 2 && (face.cell(0).owner() != my_rank || face.cell(1).owner() != my_rank))
984 is_sub_domain_boundary_face = true;
985 }
986 if (is_sub_domain_boundary_face) {
987 face.mutableItemBase().addFlags(shared_and_boundary_flags);
988 for (Item inode : face.nodes())
989 inode.mutableItemBase().addFlags(shared_and_boundary_flags);
990 for (Item iedge : face.edges())
991 iedge.mutableItemBase().addFlags(shared_and_boundary_flags);
992 }
993 });
994 _markBoundaryNodesFromEdges(node_layer);
995}
996
997/*---------------------------------------------------------------------------*/
998/*---------------------------------------------------------------------------*/
999
1000void GhostLayerBuilder2::
1001_markBoundaryNodesFromEdges(ArrayView<Int32> node_layer)
1002{
1003 const bool is_non_manifold = m_mesh->meshKind().isNonManifold();
1004 if (!is_non_manifold)
1005 return;
1006
1007 const int shared_and_boundary_flags = ItemFlags::II_Shared | ItemFlags::II_SubDomainBoundary;
1008
1009 info() << "Mark boundary nodes from edges for non-manifold mesh";
1010 // Parcours l'ensemble des arêtes.
1011 // Si une arête est connectée à une seule maille de dimension 2
1012 // dont on est le propriétaire, alors il s'agit d'une arête de bord
1013 // et on marque les noeuds correspondants.
1014 IParallelMng* pm = m_mesh->parallelMng();
1015 Int32 my_rank = pm->commRank();
1016 ItemInternalMap& edges_map = m_mesh->edgesMap();
1017 edges_map.eachItem([&](Edge edge) {
1018 Int32 nb_cell = edge.nbCell();
1019 Int32 nb_dim2_cell = 0;
1020 Int32 nb_own_dim2_cell = 0;
1021 for (Cell cell : edge.cells()) {
1022 Int32 dim = cell.typeInfo()->dimension();
1023 if (dim == 2) {
1024 ++nb_dim2_cell;
1025 if (cell.owner() == my_rank)
1026 ++nb_own_dim2_cell;
1027 }
1028 }
1029 if (nb_dim2_cell == nb_cell && nb_own_dim2_cell == 1) {
1030 edge.mutableItemBase().addFlags(shared_and_boundary_flags);
1031 for (Item inode : edge.nodes()) {
1032 inode.mutableItemBase().addFlags(shared_and_boundary_flags);
1033 node_layer[inode.localId()] = 1;
1034 }
1035 }
1036 });
1037}
1038
1039/*---------------------------------------------------------------------------*/
1040/*---------------------------------------------------------------------------*/
1041// Cette fonction gère les versions 3 et 4 de calcul des entités fantômes.
1042extern "C++" void
1043_buildGhostLayerNewVersion(DynamicMesh* mesh, bool is_allocate, Int32 version)
1044{
1045 GhostLayerBuilder2 glb(mesh->m_mesh_builder, is_allocate, version);
1046 glb.addGhostLayers();
1047}
1048
1049/*---------------------------------------------------------------------------*/
1050/*---------------------------------------------------------------------------*/
1051
1052} // End namespace Arcane::mesh
1053
1054/*---------------------------------------------------------------------------*/
1055/*---------------------------------------------------------------------------*/
#define ARCANE_FATAL(...)
Macro envoyant une exception FatalErrorException.
Integer size() const
Nombre d'éléments du vecteur.
Vue modifiable d'un tableau d'un type T.
constexpr const_pointer data() const noexcept
Pointeur sur le début de la vue.
Tableau d'items de types quelconques.
void clear()
Supprime les éléments du tableau.
void resize(Int64 s)
Change le nombre d'éléments du tableau à s.
void addRange(ConstReferenceType val, Int64 n)
Ajoute n élément de valeur val à la fin du tableau.
void add(ConstReferenceType val)
Ajoute l'élément val à la fin du tableau.
Maille d'un maillage.
Definition Item.h:1214
Vue constante d'un tableau de type T.
constexpr const_pointer data() const noexcept
Pointeur sur la mémoire allouée.
constexpr Integer size() const noexcept
Nombre d'éléments du tableau.
constexpr ConstArrayView< T > subConstView(Integer abegin, Integer asize) const noexcept
Sous-vue (constante) à partir de l'élément abegin et contenant asize éléments.
Classe template pour convertir un type.
Arête d'une maille.
Definition Item.h:826
CellConnectedListViewType cells() const
Liste des mailles de l'arête.
Definition Item.h:916
Int32 nbCell() const
Nombre de mailles connectées à l'arête.
Definition Item.h:910
Face d'une maille.
Definition Item.h:964
Cell cell(Int32 i) const
i-ème maille de la face
Definition Item.h:1665
Int32 nbCell() const
Nombre de mailles de la face (1 ou 2).
Definition Item.h:1042
EdgeConnectedListViewType edges() const
Liste des arêtes de la face.
Definition Item.h:1159
Table de hachage pour tableaux associatifs.
virtual Int32 maxLocalId() const =0
Interface du gestionnaire de parallélisme pour un sous-domaine.
virtual Int32 commRank() const =0
Rang de cette instance dans le communicateur.
virtual void recv(ArrayView< char > values, Int32 rank)=0
virtual Int32 commSize() const =0
Nombre d'instance dans le communicateur.
virtual void waitAllRequests(ArrayView< Request > rvalues)=0
Bloque en attendant que les requêtes rvalues soient terminées.
virtual bool isParallel() const =0
Retourne true si l'exécution est parallèle.
MutableItemBase toMutable()
Interface modifiable de cette entité
Int32 flags() const
Flags de l'entité
@ II_Shared
L'entité est partagée par un autre sous-domaine.
Definition ItemFlags.h:58
@ II_SubDomainBoundary
L'entité est à la frontière de deux sous-domaines.
Definition ItemFlags.h:59
@ II_Boundary
L'entité est sur la frontière.
Definition ItemFlags.h:50
Structure interne d'une entité de maillage.
Classe utilitaire pour imprimer les infos sur une entité.
Definition ItemPrinter.h:34
Int16 dimension() const
Dimension de l'élément (<0 si inconnu).
NodeConnectedListViewType nodes() const
Liste des noeuds de l'entité
Definition Item.h:794
NodeLocalIdView nodeIds() const
Liste des noeuds de l'entité
Definition Item.h:797
Classe de base d'un élément de maillage.
Definition Item.h:83
const ItemTypeInfo * typeInfo() const
Infos sur le type de l'entité.
Definition Item.h:392
impl::MutableItemBase mutableItemBase() const
Partie interne modifiable de l'entité.
Definition Item.h:380
constexpr Int32 localId() const
Identifiant local de l'entité dans le sous-domaine du processeur.
Definition Item.h:219
Int32 owner() const
Numéro du sous-domaine propriétaire de l'entité
Definition Item.h:238
ItemUniqueId uniqueId() const
Identifiant unique sur tous les domaines.
Definition Item.h:225
constexpr bool isOwn() const
true si l'entité est appartient au sous-domaine
Definition Item.h:253
impl::ItemBase itemBase() const
Partie interne de l'entité.
Definition Item.h:369
bool isNonManifold() const
Vrai si la structure du maillage est eMeshCellDimensionKind::NonManifold.
Definition MeshKind.h:118
Requête d'un message.
Definition Request.h:77
void setOwner(Integer suid, Int32 current_sub_domain)
Positionne le numéro du sous-domaine propriétaire de l'entité.
void addFlags(Int32 added_flags)
Ajoute les flags added_flags à ceux de l'entité
Noeud d'un maillage.
Definition Item.h:582
Algorithme de tri bi-tonique parallèle.
ConstArrayView< KeyType > keys() const override
Après un tri, retourne la liste des éléments de ce rang.
void sort(ConstArrayView< KeyType > keys) override
Trie en parallèle les éléments de keys sur tous les rangs.
Vecteur 1D de données avec sémantique par référence.
Affiche le temps passé entre l'appel au constructeur et le destructeur.
Definition Timer.h:156
TraceAccessor(ITraceMng *m)
Construit un accesseur via le gestionnaire de trace m.
TraceMessage info() const
Flot pour un message d'information.
TraceMessage warning() const
Flot pour un message d'avertissement.
ITraceMng * traceMng() const
Gestionnaire de trace.
Vecteur 1D de données avec sémantique par valeur (style STL).
Construction d'un maillage de manière incrémentale.
Implémentation d'un maillage.
Definition DynamicMesh.h:98
IItemFamily * nodeFamily() override
Retourne la famille des noeuds.
IParallelMng * parallelMng() override
Gestionnaire de parallèlisme.
void serializeCells(ISerializer *buffer, Int32ConstArrayView cells_local_id) override
const MeshKind meshKind() const override
Caractéristiques du maillage.
Fonctor pour trier les BoundaryNodeInfo via le tri bitonic.
Structure contenant les informations des noeuds frontières.
Construction des couches fantômes.
void _sortBoundaryNodeList(Array< BoundaryNodeInfo > &boundary_node_list)
Trie parallèle de la liste des infos sur les noeuds frontières.
void _markBoundaryItems(ArrayView< Int32 > node_layer)
Marque les entitées au bord du sous-domaine.
GhostLayerBuilder2(DynamicMeshIncrementalBuilder *mesh_builder, bool is_allocate, Int32 version)
Construit une instance pour le maillage mesh.
void addGhostLayers()
Ajoute les couches de mailles fantomes.
void _markBoundaryNodes(ArrayView< Int32 > node_layer)
Détermine les noeuds frontières.
Tableau associatif de ItemInternal.
impl::ItemBase findItem(Int64 uid) const
Retourne l'entité de numéro unique uid.
void eachItem(const Lambda &lambda)
Fonction template pour itérer sur les entités de l'instance.
impl::ItemBase tryFind(Int64 key) const
Retourne l'entité associée à key si trouvé ou l'entité nulle sinon.
Ref< IParallelExchanger > createExchangerRef(IParallelMng *pm)
Retourne une interface pour transférer des messages entre rangs.
ArrayView< Int64 > Int64ArrayView
Equivalent C d'un tableau à une dimension d'entiers 64 bits.
Definition UtilsTypes.h:449
std::int64_t Int64
Type entier signé sur 64 bits.
Int32 Integer
Type représentant un entier.
ConstArrayView< Int32 > Int32ConstArrayView
Equivalent C d'un tableau à une dimension d'entiers 32 bits.
Definition UtilsTypes.h:480
ArrayView< Integer > IntegerArrayView
Equivalent C d'un tableau à une dimension d'entiers.
Definition UtilsTypes.h:455
ConstArrayView< Int64 > Int64ConstArrayView
Equivalent C d'un tableau à une dimension d'entiers 64 bits.
Definition UtilsTypes.h:478
UniqueArray< Int32 > Int32UniqueArray
Tableau dynamique à une dimension d'entiers 32 bits.
Definition UtilsTypes.h:339
Array< Int32 > Int32Array
Tableau dynamique à une dimension d'entiers 32 bits.
Definition UtilsTypes.h:121
UniqueArray< Integer > IntegerUniqueArray
Tableau dynamique à une dimension d'entiers.
Definition UtilsTypes.h:345
ConstArrayView< Integer > IntegerConstArrayView
Equivalent C d'un tableau à une dimension d'entiers.
Definition UtilsTypes.h:484
std::int32_t Int32
Type entier signé sur 32 bits.