Arcane  4.2.3.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#include "arcane/core/internal/IMeshInternal.h"
32
33#include "arcane/mesh/DynamicMesh.h"
34#include "arcane/mesh/DynamicMeshIncrementalBuilder.h"
35#include "arcane/utils/ScopedPtr.h"
36
37#include <algorithm>
38#include <set>
39
40/*---------------------------------------------------------------------------*/
41/*---------------------------------------------------------------------------*/
42
43namespace Arcane::mesh
44{
45
46/*---------------------------------------------------------------------------*/
47/*---------------------------------------------------------------------------*/
52: public TraceAccessor
53{
54 class BoundaryNodeInfo;
57
58 public:
59
60 using ItemInternalMap = DynamicMeshKindInfos::ItemInternalMap;
61 using SubDomainItemMap = HashTableMapT<Int32, SharedArray<Int32>>;
62
63 public:
64
66 GhostLayerBuilder2(IMesh* mesh, bool is_allocate, Int32 version);
67
68 public:
69
70 void addGhostLayers();
71
72 private:
73
74 IMesh* m_mesh = nullptr;
75 IMeshInternal* m_mesh_internal = nullptr;
76 IParallelMng* m_parallel_mng = nullptr;
77 bool m_is_verbose = false;
78 bool m_is_allocate = false;
79 Int32 m_version = -1;
80 bool m_use_optimized_node_layer = true;
81 bool m_use_only_minimal_cell_uid = true;
82
83 private:
84
85 void _printItem(ItemInternal* ii, std::ostream& o);
86 void _markBoundaryItems(ArrayView<Int32> node_layer);
87 void _sendAndReceiveCells(SubDomainItemMap& cells_to_send);
88 void _sortBoundaryNodeList(Array<BoundaryNodeInfo>& boundary_node_list);
89 void _addGhostLayer(Integer current_layer, Int32ConstArrayView node_layer);
90 void _markBoundaryNodes(ArrayView<Int32> node_layer);
91 void _markBoundaryNodesFromEdges(ArrayView<Int32> node_layer);
92};
93
94/*---------------------------------------------------------------------------*/
95/*---------------------------------------------------------------------------*/
96
98GhostLayerBuilder2(IMesh* mesh, bool is_allocate, Int32 version)
100, m_mesh(mesh)
101, m_mesh_internal(mesh->_internalApi())
102, m_parallel_mng(m_mesh->parallelMng())
103, m_is_allocate(is_allocate)
104, m_version(version)
105{
106 if (auto v = Convert::Type<Int32>::tryParseFromEnvironment("ARCANE_GHOSTLAYER_USE_OPTIMIZED_LAYER", true)) {
107 Int32 vv = v.value();
108 m_use_optimized_node_layer = (vv == 1 || vv == 3);
109 m_use_only_minimal_cell_uid = (v == 2 || vv == 3);
110 }
111 if (auto v = Convert::Type<Int32>::tryParseFromEnvironment("ARCANE_GHOSTLAYER_VERBOSE", true)) {
112 m_is_verbose = (v.value() != 0);
113 }
114}
115
116/*---------------------------------------------------------------------------*/
117/*---------------------------------------------------------------------------*/
118
119void GhostLayerBuilder2::
120_printItem(ItemInternal* ii, std::ostream& o)
121{
122 o << ItemPrinter(ii);
123}
124
125/*---------------------------------------------------------------------------*/
126/*---------------------------------------------------------------------------*/
127
128/*---------------------------------------------------------------------------*/
129/*---------------------------------------------------------------------------*/
139{
140 public:
141
142 using BasicType = Int64;
143 static constexpr Int64 nbBasicTypeSize() { return 3; }
144
145 public:
146
148 {
149 Int32 message_size = messageSize(values);
150 const BoundaryNodeInfo* fsi_base = values.data();
151 auto* ptr = reinterpret_cast<const Int64*>(fsi_base);
152 return ConstArrayView<BasicType>(message_size, ptr);
153 }
154
155 static ArrayView<BasicType> asBasicBuffer(ArrayView<BoundaryNodeInfo> values)
156 {
157 Int32 message_size = messageSize(values);
158 BoundaryNodeInfo* fsi_base = values.data();
159 auto* ptr = reinterpret_cast<Int64*>(fsi_base);
160 return ArrayView<BasicType>(message_size, ptr);
161 }
162
163 static Int32 messageSize(ConstArrayView<BoundaryNodeInfo> values)
164 {
165 static_assert((sizeof(Int64) * nbBasicTypeSize()) == sizeof(BoundaryNodeInfo));
166 Int64 message_size_i64 = values.size() * nbBasicTypeSize();
167 Int32 message_size = CheckedConvert::toInteger(message_size_i64);
168 return message_size;
169 }
170
171 static Int32 nbElement(Int32 message_size)
172 {
173 if ((message_size % nbBasicTypeSize()) != 0)
174 ARCANE_FATAL("Message size '{0}' is not a multiple of basic size '{1}'", message_size, nbBasicTypeSize());
175 Int32 nb_element = message_size / nbBasicTypeSize();
176 return nb_element;
177 }
178
179 public:
180
182 {
183 size_t operator()(const BoundaryNodeInfo& a) const
184 {
185 size_t h1 = std::hash<Int64>{}(a.node_uid);
186 size_t h2 = std::hash<Int64>{}(a.cell_uid);
187 size_t h3 = std::hash<Int32>{}(a.cell_owner);
188 return h1 ^ h2 ^ h3;
189 }
190 };
191 friend bool operator==(const BoundaryNodeInfo& a, const BoundaryNodeInfo& b)
192 {
193 return (a.node_uid == b.node_uid && a.cell_uid == b.cell_uid && a.cell_owner == b.cell_owner);
194 }
195
196 public:
197
198 Int64 node_uid = NULL_ITEM_UNIQUE_ID;
199 Int64 cell_uid = NULL_ITEM_UNIQUE_ID;
200 Int32 cell_owner = -1;
201 Int32 padding = 0;
202};
203
204/*---------------------------------------------------------------------------*/
205/*---------------------------------------------------------------------------*/
210{
211 public:
212
213 static bool compareLess(const BoundaryNodeInfo& k1, const BoundaryNodeInfo& k2)
214 {
215 Int64 k1_node_uid = k1.node_uid;
216 Int64 k2_node_uid = k2.node_uid;
217 if (k1_node_uid < k2_node_uid)
218 return true;
219 if (k1_node_uid > k2_node_uid)
220 return false;
221
222 Int64 k1_cell_uid = k1.cell_uid;
223 Int64 k2_cell_uid = k2.cell_uid;
224 if (k1_cell_uid < k2_cell_uid)
225 return true;
226 if (k1_cell_uid > k2_cell_uid)
227 return false;
228
229 return (k1.cell_owner < k2.cell_owner);
230 }
231
233 {
234 auto buf_view = BoundaryNodeInfo::asBasicBuffer(values);
235 return pm->send(buf_view, rank, false);
236 }
237
239 {
240 auto buf_view = BoundaryNodeInfo::asBasicBuffer(values);
241 return pm->recv(buf_view, rank, false);
242 }
243
244 static Integer messageSize(ConstArrayView<BoundaryNodeInfo> values)
245 {
246 return BoundaryNodeInfo::messageSize(values);
247 }
248
249 static BoundaryNodeInfo maxValue()
250 {
252 bni.node_uid = INT64_MAX;
253 bni.cell_uid = INT64_MAX;
254 bni.cell_owner = -1;
255 return bni;
256 }
257
258 static bool isValid(const BoundaryNodeInfo& bni)
259 {
260 return bni.node_uid != INT64_MAX;
261 }
262};
263
264/*---------------------------------------------------------------------------*/
265/*---------------------------------------------------------------------------*/
266
268{
269 public:
270
271 Integer m_index;
272 Integer m_nb_cell;
273};
274
275/*---------------------------------------------------------------------------*/
276/*---------------------------------------------------------------------------*/
277
302{
303 IParallelMng* pm = m_parallel_mng;
304 if (!pm->isParallel())
305 return;
306 Integer nb_ghost_layer = m_mesh->ghostLayerMng()->nbGhostLayer();
307 info() << "** GHOST LAYER BUILDER V" << m_version << " with sort (nb_ghost_layer=" << nb_ghost_layer << ")";
308
309 // Couche fantôme à laquelle appartient le noeud.
310 UniqueArray<Integer> node_layer(m_mesh->nodeFamily()->maxLocalId(), -1);
311
312 // Marque les noeuds frontières
313 // On le fait toujours même si on ne veut pas de couche de mailles fantômes
314 _markBoundaryItems(node_layer);
315
316 if (nb_ghost_layer == 0)
317 return;
318 const Int32 my_rank = pm->commRank();
319
320 const bool is_non_manifold = m_mesh->meshKind().isNonManifold();
321 if (is_non_manifold && (m_version != 3))
322 ARCANE_FATAL("Only version 3 of ghostlayer builder is supported for non manifold meshes");
323
324 ItemInternalMap& cells_map = m_mesh_internal->cellsMap();
325 ItemInternalMap& nodes_map = m_mesh_internal->nodesMap();
326
327 Integer boundary_nodes_uid_count = 0;
328
329 // Vérifie qu'il n'y a pas de mailles fantômes avec la version 3.
330 // Si c'est le cas, affiche un avertissement et indique de passer à la version 4.
331 if (m_version == 3) {
332 Integer nb_ghost = 0;
333 cells_map.eachItem([&](Item cell) {
334 if (!cell.isOwn())
335 ++nb_ghost;
336 });
337 if (nb_ghost != 0)
338 warning() << "Invalid call to addGhostLayers() with version 3 because mesh "
339 << " already has '" << nb_ghost << "' ghost cells. The computed ghost cells"
340 << " may be wrong. Use version 4 of ghost layer builder if you want to handle this case";
341 }
342
343 // Couche fantôme à laquelle appartient la maille.
344 UniqueArray<Integer> cell_layer(m_mesh->cellFamily()->maxLocalId(), -1);
345
346 if (m_version >= 4) {
347 _markBoundaryNodes(node_layer);
348 nodes_map.eachItem([&](Item node) {
349 if (node_layer[node.localId()] == 1)
350 ++boundary_nodes_uid_count;
351 });
352 }
353 else {
354 // Parcours les nœuds et calcule le nombre de nœuds frontières
355 // et marque la première couche
356 nodes_map.eachItem([&](Item node) {
357 Int32 f = node.itemBase().flags();
358 if (f & ItemFlags::II_Shared) {
359 node_layer[node.localId()] = 1;
360 ++boundary_nodes_uid_count;
361 }
362 });
363 }
364
365 info() << "NB BOUNDARY NODE=" << boundary_nodes_uid_count;
366
367 for (Integer current_layer = 1; current_layer <= nb_ghost_layer; ++current_layer) {
368 //Integer current_layer = 1;
369 info() << "Processing layer " << current_layer;
370 cells_map.eachItem([&](Cell cell) {
371 // Ne traite pas les mailles qui ne m'appartiennent pas
372 if (m_version >= 4 && cell.owner() != my_rank)
373 return;
374 //Int64 cell_uid = cell->uniqueId();
375 Int32 cell_lid = cell.localId();
376 if (cell_layer[cell_lid] != (-1))
377 return;
378 bool is_current_layer = false;
379 for (Int32 inode_local_id : cell.nodeIds()) {
380 Integer layer = node_layer[inode_local_id];
381 //info() << "NODE_LAYER lid=" << i_node->localId() << " layer=" << layer;
382 if (layer == current_layer) {
383 is_current_layer = true;
384 break;
385 }
386 }
387 if (is_current_layer) {
388 cell_layer[cell_lid] = current_layer;
389 //info() << "Current layer celluid=" << cell_uid;
390 // Si non marqué, initialise à la couche courante + 1.
391 for (Int32 inode_local_id : cell.nodeIds()) {
392 Integer layer = node_layer[inode_local_id];
393 if (layer == (-1)) {
394 //info() << "Marque node uid=" << i_node->uniqueId();
395 node_layer[inode_local_id] = current_layer + 1;
396 }
397 }
398 }
399 });
400 }
401
402 // Marque les nœuds pour lesquels on n'a pas encore assigné la couche fantôme.
403 // Pour eux, on indique qu'on est sur la couche 'nb_ghost_layer+1'.
404 // Le but est de ne jamais transférer ces noeuds.
405 // NOTE: Ce mécanisme a été ajouté en juillet 2024 pour la version 3.14.
406 // S'il fonctionne bien on pourra ne conserver que cette méthode.
407 if (m_use_optimized_node_layer) {
408 Integer nb_no_layer = 0;
409 nodes_map.eachItem([&](Node node) {
410 Int32 lid = node.localId();
411 Int32 layer = node_layer[lid];
412 if (layer <= 0) {
413 node_layer[lid] = nb_ghost_layer + 1;
414 ++nb_no_layer;
415 }
416 });
417 info() << "Mark remaining nodes nb=" << nb_no_layer;
418 }
419
420 for (Integer i = 1; i <= nb_ghost_layer; ++i)
421 _addGhostLayer(i, node_layer);
422}
423
424/*---------------------------------------------------------------------------*/
425/*---------------------------------------------------------------------------*/
440{
441 IParallelMng* pm = m_mesh->parallelMng();
442 const Int32 my_rank = pm->commRank();
443 ItemInternalMap& faces_map = m_mesh_internal->facesMap();
444 // TODO: regarder s'il est correct de modifier ItemFlags::II_SubDomainBoundary
445 const int shared_and_boundary_flags = ItemFlags::II_Shared | ItemFlags::II_SubDomainBoundary;
446 // Parcours les faces et marque les nœuds, arêtes et faces frontières
447 faces_map.eachItem([&](Face face) {
448 Int32 nb_own = 0;
449 for (Integer i = 0, n = face.nbCell(); i < n; ++i)
450 if (face.cell(i).owner() == my_rank)
451 ++nb_own;
452 if (nb_own == 1) {
453 face.mutableItemBase().addFlags(shared_and_boundary_flags);
454 //++nb_sub_domain_boundary_face;
455 for (Item inode : face.nodes()) {
456 inode.mutableItemBase().addFlags(shared_and_boundary_flags);
457 node_layer[inode.localId()] = 1;
458 }
459 for (Item iedge : face.edges())
460 iedge.mutableItemBase().addFlags(shared_and_boundary_flags);
461 }
462 });
463 _markBoundaryNodesFromEdges(node_layer);
464}
465
466/*---------------------------------------------------------------------------*/
467/*---------------------------------------------------------------------------*/
468
469void GhostLayerBuilder2::
470_addGhostLayer(Integer current_layer, Int32ConstArrayView node_layer)
471{
472 info() << "Processing ghost layer " << current_layer
473 << " node_layer_size=" << node_layer.size();
474
475 SharedArray<BoundaryNodeInfo> boundary_node_list;
476 //boundary_node_list.reserve(boundary_nodes_uid_count);
477
478 IParallelMng* pm = m_parallel_mng;
479 Int32 my_rank = pm->commRank();
480 Int32 nb_rank = pm->commSize();
481
482 bool is_verbose = m_is_verbose;
483
484 ItemInternalMap& cells_map = m_mesh_internal->cellsMap();
485 ItemInternalMap& nodes_map = m_mesh_internal->nodesMap();
486
487 Int64 nb_added_for_different_rank = 0;
488 Int64 nb_added_for_in_layer = 0;
489
490 const Int32 max_local_id = m_mesh->nodeFamily()->maxLocalId();
491
492 // Tableaux contenant pour chaque nœud le uid de la plus petite maille connectée
493 // et le rang associé. Si le uid est A_NULL_UNIQUE_ID il ne faut pas ajouter ce nœud.
494 UniqueArray<Int64> node_cell_uids(max_local_id, NULL_ITEM_UNIQUE_ID);
495
496 const bool do_only_minimal_uid = m_use_only_minimal_cell_uid;
497 // On doit envoyer tous les nœuds dont le numéro de couche est différent de (-1).
498 // NOTE: pour la couche au dessus de 1, il ne faut envoyer qu'une seule valeur.
499 cells_map.eachItem([&](Cell cell) {
500 // Ne traite pas les mailles qui ne m'appartiennent pas
501 // FIXME: (juillet 2026) désactiver temporairement cette vérification car elle ne fonctionne pas
502 // lorsque nous avons 2 ou plus de couches fantômes.
503 //if (m_version >= 4 && cell.owner() != my_rank)
504 //return;
505 Int64 cell_uid = cell.uniqueId();
506 for (Node node : cell.nodes()) {
507 Int32 node_lid = node.localId();
508 bool do_it = false;
509 if (cell.owner() != my_rank) {
510 do_it = true;
511 ++nb_added_for_different_rank;
512 }
513 else {
514 Integer layer = node_layer[node_lid];
515 do_it = layer <= current_layer;
516 if (do_it)
517 ++nb_added_for_in_layer;
518 }
519 if (do_it) {
520 Int32 node_lid = node.localId();
521 if (do_only_minimal_uid) {
522 Int64 current_uid = node_cell_uids[node_lid];
523 if ((current_uid == NULL_ITEM_UNIQUE_ID) || cell_uid < current_uid) {
524 node_cell_uids[node_lid] = cell_uid;
525 if (is_verbose)
526 info() << "AddNode node_uid=" << node.uniqueId() << " cell=" << cell_uid;
527 }
528 else if (is_verbose)
529 info() << "AddNode node_uid=" << node.uniqueId() << " cell=" << cell_uid << " not done current=" << current_uid;
530 }
531 else {
532 Int64 node_uid = node.uniqueId();
534 nci.node_uid = node_uid;
535 nci.cell_uid = cell_uid;
536 nci.cell_owner = my_rank;
537 boundary_node_list.add(nci);
538 if (is_verbose)
539 info() << "AddNode node_uid=" << node.uniqueId() << " cell=" << cell_uid;
540 }
541 }
542 }
543 });
544
545 if (do_only_minimal_uid) {
546 nodes_map.eachItem([&](Node node) {
547 Int32 lid = node.localId();
548 Int64 cell_uid = node_cell_uids[lid];
549 if (cell_uid != NULL_ITEM_UNIQUE_ID) {
550 Int64 node_uid = node.uniqueId();
552 nci.node_uid = node_uid;
553 nci.cell_uid = cell_uid;
554 nci.cell_owner = my_rank;
555 boundary_node_list.add(nci);
556 }
557 });
558 }
559
560 info() << "NB BOUNDARY NODE LIST=" << boundary_node_list.size()
561 << " nb_added_for_different_rank=" << nb_added_for_different_rank
562 << " nb_added_for_in_layer=" << nb_added_for_in_layer
563 << " do_only_minimal=" << do_only_minimal_uid;
564
565 _sortBoundaryNodeList(boundary_node_list);
566 SharedArray<BoundaryNodeInfo> all_boundary_node_info = boundary_node_list;
567
568 UniqueArray<BoundaryNodeToSendInfo> node_list_to_send;
569 {
570 ConstArrayView<BoundaryNodeInfo> all_bni = all_boundary_node_info;
571 Integer bi_n = all_bni.size();
572 for (Integer i = 0; i < bi_n; ++i) {
573 const BoundaryNodeInfo& bni = all_bni[i];
574 // Recherche tous les éléments de all_bni qui ont le même noeud.
575 // Cela représente toutes les mailles connectées à ce noeud.
576 Int64 node_uid = bni.node_uid;
577 Integer last_i = i;
578 for (; last_i < bi_n; ++last_i)
579 if (all_bni[last_i].node_uid != node_uid)
580 break;
581 Integer nb_same_node = (last_i - i);
582 if (is_verbose)
583 info() << "NB_SAME_NODE uid=" << node_uid << " n=" << nb_same_node << " last_i=" << last_i;
584 // Maintenant, regarde si les mailles connectées à ce noeud ont le même propriétaire.
585 // Si c'est le cas, il s'agit d'un vrai noeud frontière et il n'y a donc rien à faire.
586 // Sinon, il faudra envoyer la liste des mailles à tous les PE dont les rangs apparaissent dans cette liste
587 Int32 owner = bni.cell_owner;
588 bool has_ghost = false;
589 for (Integer z = 0; z < nb_same_node; ++z)
590 if (all_bni[i + z].cell_owner != owner) {
591 has_ghost = true;
592 break;
593 }
594 if (has_ghost) {
596 si.m_index = i;
597 si.m_nb_cell = nb_same_node;
598 node_list_to_send.add(si);
599 if (is_verbose)
600 info() << "Add ghost uid=" << node_uid << " index=" << i << " nb_same_node=" << nb_same_node;
601 }
602 i = last_i - 1;
603 }
604 }
605
606 IntegerUniqueArray nb_info_to_send(nb_rank, 0);
607 {
608 ConstArrayView<BoundaryNodeInfo> all_bni = all_boundary_node_info;
609 Integer nb_node_to_send = node_list_to_send.size();
610 std::set<Int32> ranks_done;
611 for (Integer i = 0; i < nb_node_to_send; ++i) {
612 Integer index = node_list_to_send[i].m_index;
613 Integer nb_cell = node_list_to_send[i].m_nb_cell;
614
615 ranks_done.clear();
616
617 for (Integer kz = 0; kz < nb_cell; ++kz) {
618 Int32 krank = all_bni[index + kz].cell_owner;
619 if (ranks_done.find(krank) == ranks_done.end()) {
620 ranks_done.insert(krank);
621 // Pour chacun, il faudra envoyer
622 // - le nombre de mailles (1*Int64)
623 // - le uid du noeud (1*Int64)
624 // - le uid et le rank de chaque maille (2*Int64*nb_cell)
625 //TODO: il est possible de stocker les rangs sur Int32
626 nb_info_to_send[krank] += (nb_cell * 2) + 2;
627 }
628 }
629 }
630 }
631
632 if (is_verbose) {
633 for (Integer i = 0; i < nb_rank; ++i) {
634 Integer nb_to_send = nb_info_to_send[i];
635 if (nb_to_send != 0)
636 info() << "NB_TO_SEND rank=" << i << " n=" << nb_to_send;
637 }
638 }
639
640 Integer total_nb_to_send = 0;
641 IntegerUniqueArray nb_info_to_send_indexes(nb_rank, 0);
642 for (Integer i = 0; i < nb_rank; ++i) {
643 nb_info_to_send_indexes[i] = total_nb_to_send;
644 total_nb_to_send += nb_info_to_send[i];
645 }
646 info() << "TOTAL_NB_TO_SEND=" << total_nb_to_send;
647
648 UniqueArray<Int64> resend_infos(total_nb_to_send);
649 {
650 ConstArrayView<BoundaryNodeInfo> all_bni = all_boundary_node_info;
651 Integer nb_node_to_send = node_list_to_send.size();
652 std::set<Int32> ranks_done;
653 for (Integer i = 0; i < nb_node_to_send; ++i) {
654 Integer node_index = node_list_to_send[i].m_index;
655 Integer nb_cell = node_list_to_send[i].m_nb_cell;
656 Int64 node_uid = all_bni[node_index].node_uid;
657
658 ranks_done.clear();
659
660 for (Integer kz = 0; kz < nb_cell; ++kz) {
661 Int32 krank = all_bni[node_index + kz].cell_owner;
662 if (ranks_done.find(krank) == ranks_done.end()) {
663 ranks_done.insert(krank);
664 Integer send_index = nb_info_to_send_indexes[krank];
665 resend_infos[send_index] = node_uid;
666 ++send_index;
667 resend_infos[send_index] = nb_cell;
668 ++send_index;
669 for (Integer zz = 0; zz < nb_cell; ++zz) {
670 resend_infos[send_index] = all_bni[node_index + zz].cell_uid;
671 ++send_index;
672 resend_infos[send_index] = all_bni[node_index + zz].cell_owner;
673 ++send_index;
674 }
675 nb_info_to_send_indexes[krank] = send_index;
676 }
677 }
678 }
679 }
680
681 IntegerUniqueArray nb_info_to_recv(nb_rank, 0);
682 {
683 Timer::SimplePrinter sp(traceMng(), "Sending size with AllToAll");
684 pm->allToAll(nb_info_to_send, nb_info_to_recv, 1);
685 }
686
687 if (is_verbose)
688 for (Integer i = 0; i < nb_rank; ++i)
689 info() << "NB_TO_RECV: I=" << i << " n=" << nb_info_to_recv[i];
690
691 Integer total_nb_to_recv = 0;
692 for (Integer i = 0; i < nb_rank; ++i)
693 total_nb_to_recv += nb_info_to_recv[i];
694
695 // Il y a de fortes chances que cela ne marche pas si le tableau est trop grand,
696 // il faut proceder avec des tableaux qui ne depassent pas 2Go a cause des
697 // Int32 de MPI.
698 // TODO: Faire le AllToAll en plusieurs fois si besoin.
699 // TOOD: Fusionner ce code avec celui de FaceUniqueIdBuilder2.
700 UniqueArray<Int64> recv_infos;
701 {
702 Int32 vsize = sizeof(Int64) / sizeof(Int64);
703 Int32UniqueArray send_counts(nb_rank);
704 Int32UniqueArray send_indexes(nb_rank);
705 Int32UniqueArray recv_counts(nb_rank);
706 Int32UniqueArray recv_indexes(nb_rank);
707 Int32 total_send = 0;
708 Int32 total_recv = 0;
709 for (Integer i = 0; i < nb_rank; ++i) {
710 send_counts[i] = (Int32)(nb_info_to_send[i] * vsize);
711 recv_counts[i] = (Int32)(nb_info_to_recv[i] * vsize);
712 send_indexes[i] = total_send;
713 recv_indexes[i] = total_recv;
714 total_send += send_counts[i];
715 total_recv += recv_counts[i];
716 }
717 recv_infos.resize(total_nb_to_recv);
718
719 Int64ConstArrayView send_buf(total_nb_to_send * vsize, (Int64*)resend_infos.data());
720 Int64ArrayView recv_buf(total_nb_to_recv * vsize, (Int64*)recv_infos.data());
721
722 info() << "BUF_SIZES: send=" << send_buf.size() << " recv=" << recv_buf.size();
723 {
724 Timer::SimplePrinter sp(traceMng(), "Send values with AllToAll");
725 pm->allToAllVariable(send_buf, send_counts, send_indexes, recv_buf, recv_counts, recv_indexes);
726 }
727 }
728
729 SubDomainItemMap cells_to_send(50, true);
730
731 // TODO: il n'y a a priori pas besoin d'avoir les mailles ici mais
732 // seulement la liste des procs a qui il faut envoyer. Ensuite,
733 // si le proc connait a qui il doit envoyer, il peut envoyer les mailles
734 // à ce moment la. Cela permet d'envoyer moins d'infos dans le AllToAll précédent
735
736 {
737 Integer index = 0;
738 UniqueArray<Int32> my_cells;
739 SharedArray<Int32> ranks_to_send;
740 std::set<Int32> ranks_done;
741 while (index < total_nb_to_recv) {
742 Int64 node_uid = recv_infos[index];
743 ++index;
744 Int64 nb_cell = recv_infos[index];
745 ++index;
746 Node current_node(nodes_map.findItem(node_uid));
747 if (is_verbose)
748 info() << "NODE uid=" << node_uid << " nb_cell=" << nb_cell << " idx=" << (index - 2);
749 my_cells.clear();
750 ranks_to_send.clear();
751 ranks_done.clear();
752 for (Integer kk = 0; kk < nb_cell; ++kk) {
753 Int64 cell_uid = recv_infos[index];
754 ++index;
755 Int32 cell_owner = CheckedConvert::toInt32(recv_infos[index]);
756 ++index;
757 if (kk == 0 && current_layer == 1 && m_is_allocate)
758 // Je suis la maille de plus petit uid et donc je
759 // positionne le propriétaire du noeud.
760 // TODO: ne pas faire cela ici, mais le faire dans une routine à part.
761 nodes_map.findItem(node_uid).toMutable().setOwner(cell_owner, my_rank);
762 if (is_verbose)
763 info() << " CELL=" << cell_uid << " owner=" << cell_owner;
764 if (cell_owner == my_rank) {
765 impl::ItemBase dcell = cells_map.tryFind(cell_uid);
766 if (dcell.null())
767 ARCANE_FATAL("Internal error: cell uid={0} is not in our mesh", cell_uid);
768 if (do_only_minimal_uid) {
769 // Ajoute toutes les mailles autour de mon noeud
770 for (CellLocalId c : current_node.cellIds())
771 my_cells.add(c);
772 }
773 else
774 my_cells.add(dcell.localId());
775 }
776 else {
777 if (ranks_done.find(cell_owner) == ranks_done.end()) {
778 ranks_to_send.add(cell_owner);
779 ranks_done.insert(cell_owner);
780 }
781 }
782 }
783
784 if (is_verbose) {
785 info() << "CELLS TO SEND: node_uid=" << node_uid
786 << " nb_rank=" << ranks_to_send.size()
787 << " nb_cell=" << my_cells.size();
788 info(4) << "CELLS TO SEND: node_uid=" << node_uid
789 << " rank=" << ranks_to_send
790 << " cell=" << my_cells;
791 }
792
793 for (Integer zrank = 0, zn = ranks_to_send.size(); zrank < zn; ++zrank) {
794 Int32 send_rank = ranks_to_send[zrank];
795 SubDomainItemMap::Data* d = cells_to_send.lookupAdd(send_rank);
796 Int32Array& c = d->value();
797 for (Integer zid = 0, zid_size = my_cells.size(); zid < zid_size; ++zid) {
798 // TODO: regarder si maille pas déjà présente et ne pas l'ajouter si ce n'est pas nécessaire.
799 c.add(my_cells[zid]);
800 }
801 }
802 }
803 }
804
805 info() << "GHOST V3 SERIALIZE CELLS";
806 _sendAndReceiveCells(cells_to_send);
807}
808
809/*---------------------------------------------------------------------------*/
810/*---------------------------------------------------------------------------*/
820{
821 IParallelMng* pm = m_parallel_mng;
822 Int32 my_rank = pm->commRank();
823 Int32 nb_rank = pm->commSize();
824 bool is_verbose = m_is_verbose;
825
827 boundary_node_sorter.setNeedIndexAndRank(false);
828
829 {
830 Timer::SimplePrinter sp(traceMng(), "Sorting boundary nodes");
831 boundary_node_sorter.sort(boundary_node_list);
832 }
833
834 if (is_verbose) {
835 ConstArrayView<BoundaryNodeInfo> all_bni = boundary_node_sorter.keys();
836 Integer n = all_bni.size();
837 for (Integer i = 0; i < n; ++i) {
838 const BoundaryNodeInfo& bni = all_bni[i];
839 info() << "NODES_KEY i=" << i
840 << " node=" << bni.node_uid
841 << " cell=" << bni.cell_uid
842 << " rank=" << bni.cell_owner;
843 }
844 }
845
846 // TODO: il n'y a pas besoin d'envoyer toutes les mailles.
847 // pour déterminer le propriétaire d'un noeud, il suffit
848 // que chaque PE envoie sa maille de plus petit UID.
849 // Ensuite, chaque noeud a besoin de savoir la liste
850 // des sous-domaines connectés pour renvoyer l'info. Chaque
851 // sous-domaine en sachant cela saura a qui il doit envoyer
852 // les mailles fantomes.
853
854 {
855 ConstArrayView<BoundaryNodeInfo> all_bni = boundary_node_sorter.keys();
856 Integer n = all_bni.size();
857 // Comme un même noeud peut être présent dans la liste du proc précédent, chaque PE
858 // (sauf le 0) envoie au proc précédent le début sa liste qui contient les même noeuds.
859
860 UniqueArray<BoundaryNodeInfo> end_node_list;
861 Integer begin_own_list_index = 0;
862 if (n != 0 && my_rank != 0) {
863 if (BoundaryNodeBitonicSortTraits::isValid(all_bni[0])) {
864 Int64 node_uid = all_bni[0].node_uid;
865 for (Integer i = 0; i < n; ++i) {
866 if (all_bni[i].node_uid != node_uid) {
867 begin_own_list_index = i;
868 break;
869 }
870 else
871 end_node_list.add(all_bni[i]);
872 }
873 }
874 }
875 info() << "BEGIN_OWN_LIST_INDEX=" << begin_own_list_index << " end_node_list_size=" << end_node_list.size();
876 if (is_verbose) {
877 for (Integer k = 0, kn = end_node_list.size(); k < kn; ++k)
878 info() << " SEND node_uid=" << end_node_list[k].node_uid
879 << " cell_uid=" << end_node_list[k].cell_uid;
880 }
881
882 UniqueArray<BoundaryNodeInfo> end_node_list_recv;
883
885 Integer recv_message_size = 0;
886 Integer send_message_size = BoundaryNodeBitonicSortTraits::messageSize(end_node_list);
887
888 // Envoie et réceptionne d'abord les tailles.
889 if (my_rank != (nb_rank - 1)) {
890 requests.add(pm->recv(IntegerArrayView(1, &recv_message_size), my_rank + 1, false));
891 }
892 if (my_rank != 0) {
893 requests.add(pm->send(IntegerConstArrayView(1, &send_message_size), my_rank - 1, false));
894 }
895 info() << "Send size=" << send_message_size << " Recv size=" << recv_message_size;
896 pm->waitAllRequests(requests);
897 requests.clear();
898
899 if (recv_message_size != 0) {
900 Int32 nb_element = BoundaryNodeInfo::nbElement(recv_message_size);
901 end_node_list_recv.resize(nb_element);
902 requests.add(BoundaryNodeBitonicSortTraits::recv(pm, my_rank + 1, end_node_list_recv));
903 }
904 if (send_message_size != 0)
905 requests.add(BoundaryNodeBitonicSortTraits::send(pm, my_rank - 1, end_node_list));
906
907 pm->waitAllRequests(requests);
908
909 boundary_node_list.clear();
910 boundary_node_list.addRange(all_bni.subConstView(begin_own_list_index, n - begin_own_list_index));
911 boundary_node_list.addRange(end_node_list_recv);
912 }
913}
914
915/*---------------------------------------------------------------------------*/
916/*---------------------------------------------------------------------------*/
917
918void GhostLayerBuilder2::
919_sendAndReceiveCells(SubDomainItemMap& cells_to_send)
920{
921 auto exchanger{ ParallelMngUtils::createExchangerRef(m_parallel_mng) };
922
923 const bool is_verbose = m_is_verbose;
924
925 // Envoie et réceptionne les mailles fantômes
926 for (SubDomainItemMap::Enumerator i_map(cells_to_send); ++i_map;) {
927 Int32 sd = i_map.data()->key();
928 Int32Array& items = i_map.data()->value();
929
930 // Comme la liste par sous-domaine peut contenir plusieurs
931 // fois la même maille, on trie la liste et on supprime les
932 // doublons
933 std::sort(std::begin(items), std::end(items));
934 auto new_end = std::unique(std::begin(items), std::end(items));
935 items.resize(CheckedConvert::toInteger(new_end - std::begin(items)));
936 if (is_verbose)
937 info(4) << "CELLS TO SEND SD=" << sd << " Items=" << items;
938 else
939 info(4) << "CELLS TO SEND SD=" << sd << " nb=" << items.size();
940 exchanger->addSender(sd);
941 }
942 exchanger->initializeCommunicationsMessages();
943 for (Integer i = 0, ns = exchanger->nbSender(); i < ns; ++i) {
944 ISerializeMessage* sm = exchanger->messageToSend(i);
945 Int32 rank = sm->destination().value();
946 ISerializer* s = sm->serializer();
947 Int32ConstArrayView items_to_send = cells_to_send[rank];
948 ScopedPtrT<IItemFamilySerializer> cell_serializer(m_mesh->cellFamily()->policyMng()->createSerializer());
949 s->setMode(ISerializer::ModeReserve);
950 cell_serializer->serializeItems(s, items_to_send);
951 s->allocateBuffer();
952 s->setMode(ISerializer::ModePut);
953 cell_serializer->serializeItems(s, items_to_send);
954 }
955 exchanger->processExchange();
956 info(4) << "END EXCHANGE CELLS";
957 for (Integer i = 0, ns = exchanger->nbReceiver(); i < ns; ++i) {
958 ISerializeMessage* sm = exchanger->messageToReceive(i);
959 ISerializer* s = sm->serializer();
960 s->setMode(ISerializer::ModeGet);
961 ScopedPtrT<IItemFamilySerializer> cell_serializer(m_mesh->cellFamily()->policyMng()->createSerializer());
962 cell_serializer->deserializeItems(s, nullptr);
963 }
964 m_mesh_internal->printStats(TraceMessage::DEFAULT_LEVEL);
965}
966
967/*---------------------------------------------------------------------------*/
968/*---------------------------------------------------------------------------*/
978{
979 IParallelMng* pm = m_mesh->parallelMng();
980 Int32 my_rank = pm->commRank();
981 ItemInternalMap& faces_map = m_mesh_internal->facesMap();
982
983 const int shared_and_boundary_flags = ItemFlags::II_Shared | ItemFlags::II_SubDomainBoundary;
984
985 // Parcours les faces et marque les nœuds, arêtes et faces frontières
986 faces_map.eachItem([&](Face face) {
987 bool is_sub_domain_boundary_face = false;
988 if (face.itemBase().flags() & ItemFlags::II_Boundary) {
989 is_sub_domain_boundary_face = true;
990 }
991 else {
992 if (face.nbCell() == 2 && (face.cell(0).owner() != my_rank || face.cell(1).owner() != my_rank))
993 is_sub_domain_boundary_face = true;
994 }
995 if (is_sub_domain_boundary_face) {
996 face.mutableItemBase().addFlags(shared_and_boundary_flags);
997 for (Item inode : face.nodes())
998 inode.mutableItemBase().addFlags(shared_and_boundary_flags);
999 for (Item iedge : face.edges())
1000 iedge.mutableItemBase().addFlags(shared_and_boundary_flags);
1001 }
1002 });
1003 _markBoundaryNodesFromEdges(node_layer);
1004}
1005
1006/*---------------------------------------------------------------------------*/
1007/*---------------------------------------------------------------------------*/
1008
1009void GhostLayerBuilder2::
1010_markBoundaryNodesFromEdges(ArrayView<Int32> node_layer)
1011{
1012 const bool is_non_manifold = m_mesh->meshKind().isNonManifold();
1013 if (!is_non_manifold)
1014 return;
1015
1016 const int shared_and_boundary_flags = ItemFlags::II_Shared | ItemFlags::II_SubDomainBoundary;
1017
1018 info() << "Mark boundary nodes from edges for non-manifold mesh";
1019 // Parcours l'ensemble des arêtes.
1020 // Si une arête est connectée à une seule maille de dimension 2
1021 // dont on est le propriétaire, alors il s'agit d'une arête de bord
1022 // et on marque les noeuds correspondants.
1023 IParallelMng* pm = m_mesh->parallelMng();
1024 Int32 my_rank = pm->commRank();
1025 ItemInternalMap& edges_map = m_mesh_internal->edgesMap();
1026 edges_map.eachItem([&](Edge edge) {
1027 Int32 nb_cell = edge.nbCell();
1028 Int32 nb_dim2_cell = 0;
1029 Int32 nb_own_dim2_cell = 0;
1030 for (Cell cell : edge.cells()) {
1031 Int32 dim = cell.typeInfo()->dimension();
1032 if (dim == 2) {
1033 ++nb_dim2_cell;
1034 if (cell.owner() == my_rank)
1035 ++nb_own_dim2_cell;
1036 }
1037 }
1038 if (nb_dim2_cell == nb_cell && nb_own_dim2_cell == 1) {
1039 edge.mutableItemBase().addFlags(shared_and_boundary_flags);
1040 for (Item inode : edge.nodes()) {
1041 inode.mutableItemBase().addFlags(shared_and_boundary_flags);
1042 node_layer[inode.localId()] = 1;
1043 }
1044 }
1045 });
1046}
1047
1048/*---------------------------------------------------------------------------*/
1049/*---------------------------------------------------------------------------*/
1050// Cette fonction gère les versions 3 et 4 de calcul des entités fantômes.
1051extern "C++" void
1052_buildGhostLayerNewVersion(IMesh* mesh, bool is_allocate, Int32 version)
1053{
1054 GhostLayerBuilder2 glb(mesh, is_allocate, version);
1055 glb.addGhostLayers();
1056}
1057
1058/*---------------------------------------------------------------------------*/
1059/*---------------------------------------------------------------------------*/
1060
1061} // End namespace Arcane::mesh
1062
1063/*---------------------------------------------------------------------------*/
1064/*---------------------------------------------------------------------------*/
#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 IItemFamilySerializer * createSerializer(bool with_flags=false)=0
Créé une instance pour la sérialisation des entités. L'instance retournée doit être détruite par l'op...
virtual IItemFamilyPolicyMng * policyMng()=0
Interface des comportements/politiques associées à cette famille.
virtual Int32 maxLocalId() const =0
virtual IItemFamily * nodeFamily()=0
Retourne la famille des noeuds.
virtual IItemFamily * cellFamily()=0
Retourne la famille des mailles.
Partie interne de IMesh.
virtual mesh::ItemInternalMap & nodesMap()=0
virtual IParallelMng * parallelMng()=0
Gestionnaire de parallèlisme.
virtual const MeshKind meshKind() const =0
Caractéristiques du maillage.
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.
@ ModePut
Le sérialiseur attend des reserve().
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).
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.
void addGhostLayers()
Ajoute les couches de mailles fantomes.
GhostLayerBuilder2(IMesh *mesh, bool is_allocate, Int32 version)
Construit une instance pour le maillage mesh.
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.