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