Arcane  4.1.12.0
Developer documentation
Loading...
Searching...
No Matches
FaceUniqueIdBuilder.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/* FaceUniqueIdBuilder.cc (C) 2000-2025 */
9/* */
10/* Construction of unique face identifiers. */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arcane/utils/PlatformUtils.h"
15#include "arcane/utils/ScopedPtr.h"
16#include "arcane/utils/ITraceMng.h"
17#include "arcane/utils/OStringStream.h"
18#include "arcane/utils/CheckedConvert.h"
19
20#include "arcane/core/IMeshUniqueIdMng.h"
21#include "arcane/core/IParallelExchanger.h"
22#include "arcane/core/IParallelMng.h"
23#include "arcane/core/ISerializeMessage.h"
24#include "arcane/core/ISerializer.h"
25#include "arcane/core/ParallelMngUtils.h"
26
27#include "arcane/mesh/DynamicMesh.h"
28#include "arcane/mesh/OneMeshItemAdder.h"
29#include "arcane/mesh/GhostLayerBuilder.h"
30#include "arcane/mesh/FaceUniqueIdBuilder.h"
31#include "arcane/mesh/ItemTools.h"
32#include "arcane/mesh/ItemsOwnerBuilder.h"
33
34#include <unordered_set>
35
36/*---------------------------------------------------------------------------*/
37/*---------------------------------------------------------------------------*/
38
39namespace Arcane::mesh
40{
41
42/*---------------------------------------------------------------------------*/
43/*---------------------------------------------------------------------------*/
44
45extern "C++" void
46_computeFaceUniqueIdVersion3(DynamicMesh* mesh);
47extern "C++" void
48_computeFaceUniqueIdVersion5(DynamicMesh* mesh);
49extern "C++" void
50arcaneComputeCartesianFaceUniqueId(DynamicMesh* mesh);
51
52/*---------------------------------------------------------------------------*/
53/*---------------------------------------------------------------------------*/
54
57: TraceAccessor(mesh_builder->mesh()->traceMng())
58, m_mesh(mesh_builder->mesh())
59, m_mesh_builder(mesh_builder)
60{
61}
62
63/*---------------------------------------------------------------------------*/
64/*---------------------------------------------------------------------------*/
65
66void FaceUniqueIdBuilder::
67computeFacesUniqueIds()
68{
69 IParallelMng* pm = m_mesh->parallelMng();
70 Real begin_time = platform::getRealTime();
71 Integer face_version = m_mesh->meshUniqueIdMng()->faceBuilderVersion();
72 bool is_parallel = pm->isParallel();
73 info() << "Using version=" << face_version << " to compute faces unique ids"
74 << " mesh=" << m_mesh->name() << " is_parallel=" << is_parallel;
75
76 if (face_version > 5 || face_version < 0)
77 ARCANE_FATAL("Invalid value '{0}' for compute face unique ids versions: v>=0 && v<=6", face_version);
78
79 if (face_version == 5)
80 _computeFaceUniqueIdVersion5(m_mesh);
81 else if (face_version == 4)
82 arcaneComputeCartesianFaceUniqueId(m_mesh);
83 else if (face_version == 3)
84 _computeFaceUniqueIdVersion3(m_mesh);
85 else if (face_version == 0) {
86 info() << "No face renumbering";
87 }
88 else {
89 // Version 1 or 2
90 if (is_parallel) {
91 if (face_version == 2) {
92 //NOT YET BY DEFAULT
93 info() << "Use new mesh init in FaceUniqueIdBuilder";
95 }
96 else {
97 // Default version.
99 }
100 }
101 else {
103 }
104 }
105
106 Real end_time = platform::getRealTime();
107 Real diff = (Real)(end_time - begin_time);
108 info() << "TIME to compute face unique ids=" << diff;
109
110 if (arcaneIsCheck())
112
113 ItemInternalMap& faces_map = m_mesh->facesMap();
114
115 // We must re-index #m_faces_map because the uniqueId() of the
116 // faces have been modified
117 if (face_version != 0)
118 m_mesh->faceFamily()->notifyItemsUniqueIdChanged();
119
120 bool is_verbose = m_mesh_builder->isVerbose();
121 if (is_verbose) {
122 info() << "NEW FACES_MAP after re-indexing";
123 faces_map.eachItem([&](Item face) {
124 info() << "Face uid=" << face.uniqueId() << " lid=" << face.localId();
125 });
126 }
127 // With version 0 or 5, the owners are not positioned
128 // We must do it now
129 if (face_version == 0 || face_version == 5) {
130 ItemsOwnerBuilder owner_builder(m_mesh);
131 owner_builder.computeFacesOwner();
132 }
133}
134
135/*---------------------------------------------------------------------------*/
136/*---------------------------------------------------------------------------*/
142{
143 info() << "Check no duplicate face uniqueId";
144 ItemInternalMap& faces_map = m_mesh->facesMap();
145 std::unordered_set<Int64> checked_faces_map;
146 faces_map.eachItem([&](Item face) {
147 ItemUniqueId uid = face.uniqueId();
148 auto p = checked_faces_map.find(uid);
149 if (p != checked_faces_map.end()) {
150 pwarning() << "Duplicate Face UniqueId=" << uid;
151 ARCANE_FATAL("Duplicate Face uniqueId={0}", uid);
152 }
153 checked_faces_map.insert(uid);
154 });
155}
156
157/*---------------------------------------------------------------------------*/
158/*---------------------------------------------------------------------------*/
166class T_CellFaceInfo
167{
168 public:
169
170 T_CellFaceInfo(Int64 uid, Integer nb_back_face, Integer nb_true_boundary_face)
171 : m_unique_id(uid)
172 , m_nb_back_face(nb_back_face)
173 , m_nb_true_boundary_face(nb_true_boundary_face)
174 {
175 }
176
177 T_CellFaceInfo()
178 : m_unique_id(NULL_ITEM_ID)
179 , m_nb_back_face(0)
180 , m_nb_true_boundary_face(0)
181 {
182 }
183
184 public:
185
186 bool operator<(const T_CellFaceInfo& ci) const
187 {
188 return m_unique_id < ci.m_unique_id;
189 }
190
191 public:
192
193 Int64 m_unique_id;
194 Int64 m_nb_back_face;
195 Int64 m_nb_true_boundary_face;
196};
197
198/*---------------------------------------------------------------------------*/
199/*---------------------------------------------------------------------------*/
217{
218 IParallelMng* pm = m_mesh->parallelMng();
219 Integer my_rank = pm->commRank();
220 Integer nb_rank = pm->commSize();
221
222 Integer nb_local_face = m_mesh_builder->oneMeshItemAdder()->nbFace();
223 Integer nb_local_cell = m_mesh_builder->oneMeshItemAdder()->nbCell();
224 bool is_verbose = m_mesh_builder->isVerbose();
225
226 UniqueArray<Int64> faces_opposite_cell_uid(nb_local_face);
227 faces_opposite_cell_uid.fill(NULL_ITEM_ID);
228 UniqueArray<Integer> faces_opposite_cell_index(nb_local_face);
229 UniqueArray<Integer> faces_opposite_cell_owner(nb_local_face);
230
231 // For verification, ensures that all elements of this array
232 // are valid, which means that all faces have been
233 // renumbered
234 UniqueArray<Int64> faces_new_uid(nb_local_face);
235 faces_new_uid.fill(NULL_ITEM_ID);
236
237 Integer nb_recv_sub_domain_boundary_face = 0;
238
239 Int64UniqueArray faces_infos;
240 faces_infos.reserve(10000);
241 ItemInternalMap& cells_map = m_mesh->cellsMap();
242 ItemInternalMap& faces_map = m_mesh->facesMap();
243 ItemInternalMap& nodes_map = m_mesh->nodesMap();
244
245 // NOTE: this array is not useful on all cells. It
246 // is enough that it contains the cells that we need, that is to say
247 // ours + those connected to one of our faces. A hash table
248 // would be more appropriate.
249 HashTableMapT<Int64, Int64> cells_first_face_uid(m_mesh_builder->oneMeshItemAdder()->nbCell() * 2, true);
250
251 // Gather data from other processors into recv_cells;
252 // To prevent the arrays from being too large, we proceed in several
253 // steps.
254 // Each sub-domain builds its list of boundary faces, with for each face:
255 // - its type
256 // - the list of its nodes,
257 // - the unique number of its cell
258 // - the owner of its cell
259 // - its index in its cell
260 // This list will then be sent to all sub-domains.
261 {
262 ItemTypeMng* itm = m_mesh->itemTypeMng();
263
264 UniqueArray<Int32> faces_local_id;
265
266 faces_map.eachItem([&](Face face) {
267 bool boundary_val = face.isSubDomainBoundary();
268 if (boundary_val)
269 faces_local_id.add(face.localId());
270 });
271
272 Integer nb_sub_domain_boundary_face = faces_local_id.size();
273 Int64 global_nb_boundary_face = pm->reduce(Parallel::ReduceSum, (Int64)nb_sub_domain_boundary_face);
274 debug() << "NB BOUNDARY FACE=" << nb_sub_domain_boundary_face
275 << " NB_FACE=" << nb_local_face
276 << " GLOBAL_NB_BOUNDARY_FACE=" << global_nb_boundary_face;
277
278 Int64UniqueArray faces_infos2;
279 Int64UniqueArray recv_faces_infos;
280
281 // Calculate the size of a send block
282 // The memory required for a face is equal to nb_node + 4.
283 // If we assume we have quadrangles in general, this
284 // makes 8 Int64 per face, or 64 bytes per face.
285 // The memory required to send everything is therefore 64 * global_nb_boundary_face.
286 // step_size * nb_proc * 64 bytes.
287 // The default step_size is calculated so that the required memory
288 // is on the order of 100 Mo for each message.
289 Int64 step_size = 1500000;
290 Integer nb_phase = CheckedConvert::toInteger((global_nb_boundary_face / step_size) + 1);
291 FaceLocalIdToFaceConverter faces(m_mesh->faceFamily());
292 for (Integer i_phase = 0; i_phase < nb_phase; ++i_phase) {
293 Integer nb_face_to_send = nb_sub_domain_boundary_face / nb_phase;
294 Integer first_face_to_send = nb_face_to_send * i_phase;
295 Integer last_face_to_send = first_face_to_send + nb_face_to_send;
296 if (i_phase + 1 == nb_phase)
297 last_face_to_send = nb_sub_domain_boundary_face;
298 Integer real_nb_face_to_send = last_face_to_send - first_face_to_send;
299
300 faces_infos2.clear();
301 for (Integer i_face = first_face_to_send; i_face < first_face_to_send + real_nb_face_to_send; ++i_face) {
302 Face face(faces[faces_local_id[i_face]]);
304 bool has_back_cell = face.itemBase().flags() & ItemFlags::II_HasBackCell;
305 faces_infos2.add(face.type());
306 for (Node node : face.nodes())
307 faces_infos2.add(node.uniqueId().asInt64());
308
309 //info() << " ADD FACE lid=" << face->localId();
310
311 Cell cell = face.cell(0);
312 faces_infos2.add(cell.uniqueId().asInt64());
313 faces_infos2.add(cell.owner());
314
315 Integer face_index_in_cell = 0;
316 if (has_back_cell) {
317 for (Face current_face_in_cell : cell.faces()) {
318 if (current_face_in_cell == face)
319 break;
320 if (current_face_in_cell.backCell() == cell)
321 ++face_index_in_cell;
322 }
323 }
324 else
325 face_index_in_cell = NULL_ITEM_ID;
326 faces_infos2.add(face_index_in_cell);
327 }
328 faces_infos2.add(IT_NullType); // To indicate that the list ends
329
330 pm->allGatherVariable(faces_infos2, recv_faces_infos);
331
332 info() << "Number of face bytes received: " << recv_faces_infos.size()
333 << " phase=" << i_phase << "/" << nb_phase
334 << " first_face=" << first_face_to_send
335 << " last_face=" << last_face_to_send
336 << " nb=" << real_nb_face_to_send;
337
338 Integer recv_faces_infos_index = 0;
339
340 for (Integer i_sub_domain = 0; i_sub_domain < nb_rank; ++i_sub_domain) {
341 bool is_end = false;
342 // We must not read the info that comes from me
343 if (i_sub_domain == my_rank) {
344 recv_faces_infos_index += faces_infos2.size();
345 continue;
346 }
347 // Deserializes the info for each subdomain
348 while (!is_end) {
349 Integer face_type = CheckedConvert::toInteger(recv_faces_infos[recv_faces_infos_index]);
350 ++recv_faces_infos_index;
351 if (face_type == IT_NullType) {
352 is_end = true;
353 break;
354 }
355 ItemTypeInfo* itt = itm->typeFromId(face_type);
356 Integer face_nb_node = itt->nbLocalNode();
357 ConstArrayView<Int64> faces_nodes_uid(face_nb_node, &recv_faces_infos[recv_faces_infos_index]);
358
359 recv_faces_infos_index += face_nb_node;
360 Int64 cell_uid = recv_faces_infos[recv_faces_infos_index];
361 ++recv_faces_infos_index;
362 Integer cell_owner = CheckedConvert::toInteger(recv_faces_infos[recv_faces_infos_index]);
363 ++recv_faces_infos_index;
364 Integer cell_face_index = CheckedConvert::toInteger(recv_faces_infos[recv_faces_infos_index]);
365 ++recv_faces_infos_index;
366
367 Node node = nodes_map.tryFind(faces_nodes_uid[0]);
368 // If the face does not exist in my subdomain, it is not of interest to me
369 if (node.null())
370 continue;
371 Face face = ItemTools::findFaceInNode2(node, face_type, faces_nodes_uid);
372 if (face.null())
373 continue;
374 ++nb_recv_sub_domain_boundary_face;
375 faces_opposite_cell_uid[face.localId()] = cell_uid;
376 faces_opposite_cell_index[face.localId()] = cell_face_index;
377 faces_opposite_cell_owner[face.localId()] = cell_owner;
378 cells_first_face_uid.add(cell_uid, -1);
379 }
380 }
381 }
382 info() << "Number of faces on the subdomain interface: "
383 << nb_sub_domain_boundary_face << ' ' << nb_recv_sub_domain_boundary_face;
384 }
385
386 // Finds the max uniqueId of the cells across all subdomains.
387 Int64 max_cell_uid = 0;
388 Int32 max_cell_local_id = 0;
389 cells_map.eachItem([&](Item cell) {
390 Int64 cell_uid = cell.uniqueId().asInt64();
391 Int32 cell_local_id = cell.localId();
392 if (cell_uid > max_cell_uid)
393 max_cell_uid = cell_uid;
394 if (cell_local_id > max_cell_local_id)
395 max_cell_local_id = cell_local_id;
396 });
397 Int64 global_max_cell_uid = pm->reduce(Parallel::ReduceMax, max_cell_uid);
398 debug() << "GLOBAL MAX CELL UID=" << global_max_cell_uid;
399
400 UniqueArray<T_CellFaceInfo> my_cells_faces_info;
401 my_cells_faces_info.reserve(nb_local_cell);
402 IntegerUniqueArray my_cells_nb_back_face(max_cell_local_id + 1);
403 my_cells_nb_back_face.fill(0);
404
405 cells_map.eachItem([&](Cell cell) {
406 Int64 cell_uid = cell.uniqueId().asInt64();
407 Int32 cell_local_id = cell.localId();
408 Integer nb_back_face = 0;
409 Integer nb_true_boundary_face = 0;
410 for (Face face : cell.faces()) {
411 Int64 opposite_cell_uid = faces_opposite_cell_uid[face.localId()];
412 if (face.backCell() == cell)
413 ++nb_back_face;
414 else if (face.nbCell() == 1 && opposite_cell_uid == NULL_ITEM_ID) {
415 ++nb_true_boundary_face;
416 }
417 }
418 my_cells_nb_back_face[cell_local_id] = nb_back_face;
419 my_cells_faces_info.add(T_CellFaceInfo(cell_uid, nb_back_face, nb_true_boundary_face));
420 });
421 std::sort(std::begin(my_cells_faces_info), std::end(my_cells_faces_info));
422
423 {
424 Integer nb_phase = 16;
425 Integer first_cell_index_to_send = 0;
426 Int64 current_face_uid = 0;
427
428 for (Integer i_phase = 0; i_phase < nb_phase; ++i_phase) {
429 Integer nb_uid_to_send = CheckedConvert::toInteger(global_max_cell_uid / nb_phase);
430 Integer first_uid_to_send = nb_uid_to_send * i_phase;
431 Int64 last_uid_to_send = first_uid_to_send + nb_uid_to_send;
432 if (i_phase + 1 == nb_phase)
433 last_uid_to_send = global_max_cell_uid;
434
435 //Integer last_cell_index_to_send = first_cell_index_to_send;
436 Integer nb_cell_to_send = 0;
437 for (Integer zz = first_cell_index_to_send, zs = my_cells_faces_info.size(); zz < zs; ++zz) {
438 if (my_cells_faces_info[zz].m_unique_id <= last_uid_to_send)
439 ++nb_cell_to_send;
440 else
441 break;
442 }
443 debug() << "FIRST TO SEND=" << first_cell_index_to_send
444 << " NB=" << nb_cell_to_send
445 << " first_uid=" << first_uid_to_send
446 << " last_uid=" << last_uid_to_send;
447
448 T_CellFaceInfo* begin_cell_array = my_cells_faces_info.data() + first_cell_index_to_send;
449 Int64* begin_array = reinterpret_cast<Int64*>(begin_cell_array);
450 Integer begin_size = nb_cell_to_send * 3;
451
452 Int64ConstArrayView cells_faces_infos(begin_size, begin_array);
453
454 Int64UniqueArray recv_cells_faces_infos;
455 pm->allGatherVariable(cells_faces_infos, recv_cells_faces_infos);
456 first_cell_index_to_send += nb_cell_to_send;
457
458 info() << "Infos faces (received) nb_int64=" << recv_cells_faces_infos.size();
459 Integer recv_nb_cell = recv_cells_faces_infos.size() / 3;
460
461 // NOTE: since we know the possible min and max uid, we can optimize by
462 // creating an array dimensioned with this min and max as bounds and
463 // filling the elements of this array directly. This way, we
464 UniqueArray<T_CellFaceInfo> global_cells_faces_info(recv_nb_cell);
465 for (Integer i = 0; i < recv_nb_cell; ++i) {
466 Int64 cell_uid = recv_cells_faces_infos[i * 3];
467 global_cells_faces_info[i].m_unique_id = cell_uid;
468 global_cells_faces_info[i].m_nb_back_face = recv_cells_faces_infos[(i * 3) + 1];
469 global_cells_faces_info[i].m_nb_true_boundary_face = recv_cells_faces_infos[(i * 3) + 2];
470 }
471 info() << "Sorting the faces nb=" << global_cells_faces_info.size();
472 std::sort(std::begin(global_cells_faces_info), std::end(global_cells_faces_info));
473
474 for (Integer i = 0; i < recv_nb_cell; ++i) {
475 Int64 cell_uid = global_cells_faces_info[i].m_unique_id;
476 if (cells_map.hasKey(cell_uid) || cells_first_face_uid.hasKey(cell_uid))
477 cells_first_face_uid.add(cell_uid, current_face_uid);
478 current_face_uid += global_cells_faces_info[i].m_nb_back_face + global_cells_faces_info[i].m_nb_true_boundary_face;
479 }
480 }
481 }
482
483 cells_map.eachItem([&](Cell cell) {
484 Int64 cell_uid = cell.uniqueId();
485 Int32 cell_local_id = cell.localId();
486 Integer num_local_face = 0;
487 Integer num_true_boundary_face = 0;
488 for (Face face : cell.faces()) {
489 Int64 opposite_cell_uid = faces_opposite_cell_uid[face.localId()];
490 Int64 face_new_uid = NULL_ITEM_UNIQUE_ID;
491 if (face.backCell() == cell) {
492 if (!cells_first_face_uid.hasKey(cell_uid))
493 fatal() << "NO KEY 0 for cell_uid=" << cell_uid;
494 face_new_uid = cells_first_face_uid[cell_uid] + num_local_face;
495 face.mutableItemBase().setOwner(my_rank, my_rank);
496 ++num_local_face;
497 }
498 else if (face.nbCell() == 1) {
499 if (opposite_cell_uid == NULL_ITEM_UNIQUE_ID) {
500 // This is a boundary face of the initial domain
501 if (!cells_first_face_uid.hasKey(cell_uid))
502 fatal() << "NO KEY 1 for cell_uid=" << cell_uid;
503 face_new_uid = cells_first_face_uid[cell_uid] + my_cells_nb_back_face[cell_local_id] + num_true_boundary_face;
504 ++num_true_boundary_face;
505 face.mutableItemBase().setOwner(my_rank, my_rank);
506 }
507 else {
508 if (!cells_first_face_uid.hasKey(opposite_cell_uid))
509 fatal() << "NO KEY 1 for cell_uid=" << cell_uid << " opoosite=" << opposite_cell_uid;
510 face_new_uid = cells_first_face_uid[opposite_cell_uid] + faces_opposite_cell_index[face.localId()];
511 face.mutableItemBase().setOwner(faces_opposite_cell_owner[face.localId()], my_rank);
512 }
513 }
514 if (face_new_uid != NULL_ITEM_UNIQUE_ID) {
515 faces_new_uid[face.localId()] = face_new_uid;
516 face.mutableItemBase().setUniqueId(face_new_uid);
517 }
518 }
519 });
520
521 // Verifies that all faces have been re-indexed
522 {
523 Integer nb_error = 0;
524 for (Integer i = 0, is = nb_local_face; i < is; ++i) {
525 if (faces_new_uid[i] == NULL_ITEM_UNIQUE_ID) {
526 ++nb_error;
527 if (nb_error < 10)
528 error() << "The face lid=" << i << " has not been re-indexed.";
529 }
530 }
531 if (nb_error != 0)
532 ARCANE_FATAL("Some ({0}) faces have not been reindexed", nb_error);
533 }
534
535 if (is_verbose) {
536 OStringStream ostr;
537 cells_map.eachItem([&](Cell cell) {
538 Int64 cell_uid = cell.uniqueId().asInt64();
539 Integer face_index = 0;
540 for (Face face : cell.faces()) {
541 Int64 opposite_cell_uid = faces_opposite_cell_uid[face.localId()];
542 bool shared = false;
543 bool true_boundary = false;
544 bool internal_other = false;
545 if (face.backCell() == cell) {
546 }
547 else if (face.nbCell() == 1) {
548 if (opposite_cell_uid == NULL_ITEM_ID)
549 true_boundary = true;
550 else
551 shared = true;
552 }
553 else {
554 internal_other = true;
555 opposite_cell_uid = face.backCell().uniqueId().asInt64();
556 }
557 ostr() << "NEW UNIQUE ID FOR FACE"
558 << " lid=" << face.localId()
559 << " cell=" << cell_uid
560 << " face=" << face.uniqueId()
561 << " nbcell=" << face.nbCell()
562 << " cellindex=" << face_index << " (";
563 for (Node node : face.nodes())
564 ostr() << ' ' << node.uniqueId();
565 ostr() << ")";
566 if (internal_other)
567 ostr() << " internal-other";
568 if (true_boundary)
569 ostr() << " true-boundary";
570 if (opposite_cell_uid != NULL_ITEM_ID) {
571 ostr() << " opposite " << opposite_cell_uid;
572 }
573 if (shared)
574 ostr() << " (shared)";
575 ostr() << "\n";
576 ++face_index;
577 }
578 });
579 info() << ostr.str();
580 String file_name("faces_uid.");
581 file_name = file_name + my_rank;
582 std::ofstream ofile(file_name.localstr());
583 ofile << ostr.str();
584 }
585}
586
587/*---------------------------------------------------------------------------*/
588/*---------------------------------------------------------------------------*/
594_exchangeData(IParallelExchanger* exchanger, BoundaryInfosMap& boundary_infos_to_send)
595{
596 for (BoundaryInfosMapEnumerator i_map(boundary_infos_to_send); ++i_map;) {
597 Int32 sd = i_map.data()->key();
598 exchanger->addSender(sd);
599 }
601 Integer nb_sender = exchanger->nbSender();
602 Integer nb_receiver = exchanger->nbReceiver();
603 info() << "NB_SEND=" << nb_sender << " NB_RECV=" << nb_receiver;
604 Integer total = nb_sender + nb_receiver;
605 Integer global_total = exchanger->parallelMng()->reduce(Parallel::ReduceSum, total);
606 info() << "GLOBAL_NB_MESSAGE=" << global_total;
607
608 {
609 for (Integer i = 0, ns = exchanger->nbSender(); i < ns; ++i) {
610 ISerializeMessage* sm = exchanger->messageToSend(i);
611 Int32 rank = sm->destination().value();
612 ISerializer* s = sm->serializer();
613 Int64ConstArrayView infos = boundary_infos_to_send[rank];
614 Integer nb_info = infos.size();
615 s->setMode(ISerializer::ModeReserve);
616 s->reserveInt64(1); // For the number of elements
617 s->reserveSpan(eBasicDataType::Int64, nb_info); // For the elements
618 s->allocateBuffer();
620 s->putInt64(nb_info);
621 s->putSpan(infos);
622 }
623 }
624 exchanger->processExchange();
625 debug() << "END EXCHANGE";
626}
627
628template <typename DataType>
630{
631 public:
632 private:
633
634 class MyInfo
635 {
636 public:
637
638 MyInfo(const DataType& d, Integer n)
639 : data(d)
640 , next_index(n)
641 {}
642
643 public:
644
645 DataType data;
646 Integer next_index;
647 };
648
649 public:
650
651 ItemInfoMultiList()
652 : m_last_index(5000, true)
653 {}
654
655 public:
656
657 void add(Int64 node_uid, const DataType& data)
658 {
659 Integer current_index = m_values.size();
660
661 bool is_add = false;
662 HashTableMapT<Int64, Int32>::Data* d = m_last_index.lookupAdd(node_uid, -1, is_add);
663
664 m_values.add(MyInfo(data, d->value()));
665 d->value() = current_index;
666 }
667
668 public:
669
670 UniqueArray<MyInfo> m_values;
671 HashTableMapT<Int64, Int32> m_last_index;
672};
673
674/*---------------------------------------------------------------------------*/
675/*---------------------------------------------------------------------------*/
685{
686 IParallelMng* pm = m_mesh->parallelMng();
687 Integer my_rank = pm->commRank();
688 Integer nb_rank = pm->commSize();
689
690 Integer nb_local_face = m_mesh_builder->oneMeshItemAdder()->nbFace();
691 //Integer nb_local_cell = m_mesh_builder->nbCell();
692 //bool is_verbose = m_mesh_builder->isVerbose();
693
694 Int64UniqueArray faces_opposite_cell_uid(nb_local_face);
695 faces_opposite_cell_uid.fill(NULL_ITEM_ID);
696 IntegerUniqueArray faces_opposite_cell_index(nb_local_face);
697 IntegerUniqueArray faces_opposite_cell_owner(nb_local_face);
698
699 // For verification, ensures that all elements of this array
700 // are valid, which means that all faces have been renumbered.
701 Int64UniqueArray faces_new_uid(nb_local_face);
702 faces_new_uid.fill(NULL_ITEM_ID);
703
704 Int64UniqueArray faces_infos;
705 faces_infos.reserve(10000);
706 ItemInternalMap& faces_map = m_mesh->facesMap();
707 ItemInternalMap& nodes_map = m_mesh->nodesMap();
708
709 // NOTE: this array is not useful on all cells. It
710 // is enough that it contains the cells we need, that is,
711 // ours + those connected to one of our faces.
712 HashTableMapT<Int32, Int32> cell_first_face_uid(m_mesh_builder->oneMeshItemAdder()->nbCell() * 2, true);
713
714 // Collects data from other processors into recv_cells;
715 // To prevent the arrays from being too large, we proceed in several
716 // steps.
717 // Each subdomain builds its list of boundary faces, for each face:
718 // - its type
719 // - the list of its nodes,
720 // - the unique ID of its cell
721 // - the owner of its cell
722 // - its index in its cell
723 // This list will then be sent to all subdomains.
724 ItemTypeMng* itm = m_mesh->itemTypeMng();
725
726 // Determines the max unique id of the nodes
727 Int64 my_max_node_uid = NULL_ITEM_UNIQUE_ID;
728 nodes_map.eachItem([&](Item node) {
729 Int64 node_uid = node.uniqueId();
730 if (node_uid > my_max_node_uid)
731 my_max_node_uid = node_uid;
732 });
733 Int64 global_max_node_uid = pm->reduce(Parallel::ReduceMax, my_max_node_uid);
734 debug() << "NODE_UID_INFO: MY_MAX_UID=" << my_max_node_uid
735 << " GLOBAL=" << global_max_node_uid;
736
737 //TODO: choose a good value to initialize the table
738 BoundaryInfosMap boundary_infos_to_send(nb_rank, true);
739 NodeUidToSubDomain uid_to_subdomain_converter(global_max_node_uid, nb_rank);
740 info() << "NB_CORE modulo=" << uid_to_subdomain_converter.modulo();
741 HashTableMapT<Int64, SharedArray<Int64>> nodes_info(100000, true);
742 IItemFamily* node_family = m_mesh->nodeFamily();
743 UniqueArray<bool> is_boundary_nodes(node_family->maxLocalId(), false);
744
745 // Marks all boundary nodes because these are the ones that need to be sent
746 faces_map.eachItem([&](Face face) {
747 Integer face_nb_cell = face.nbCell();
748 if (face_nb_cell == 1) {
749 for (Node node : face.nodes())
750 is_boundary_nodes[node.localId()] = true;
751 }
752 });
753
754 // Determines the list of boundary faces
755 faces_map.eachItem([&](Face face) {
756 Node first_node = face.node(0);
757 Int64 first_node_uid = first_node.uniqueId();
759 Int32 dest_rank = -1;
760 if (!is_boundary_nodes[first_node.localId()]) {
761 v = nodes_info.lookupAdd(first_node_uid)->value();
762 }
763 else {
764 dest_rank = uid_to_subdomain_converter.uidToRank(first_node_uid);
765 v = boundary_infos_to_send.lookupAdd(dest_rank)->value();
766 }
767 v.add(first_node_uid); // 0
768 v.add(my_rank); // 1
769 v.add(face.uniqueId()); // 2
770 v.add(face.type()); // 3
771 Cell back_cell = face.backCell();
772 Cell front_cell = face.frontCell();
773 if (back_cell.null()) // 4 : only used for debug
774 v.add(NULL_ITEM_UNIQUE_ID);
775 else
776 v.add(back_cell.uniqueId());
777 if (front_cell.null()) // 5 : only used for debug
778 v.add(NULL_ITEM_UNIQUE_ID);
779 else
780 v.add(front_cell.uniqueId());
781 for (Integer z = 0, zs = face.nbNode(); z < zs; ++z)
782 v.add(face.node(z).uniqueId());
783 });
784
785 // Positions the list of sends
787 _exchangeData(exchanger.get(), boundary_infos_to_send);
788
789 {
790 Integer nb_receiver = exchanger->nbReceiver();
791 debug() << "NB RECEIVER=" << nb_receiver;
792 Int64UniqueArray received_infos;
793 for (Integer i = 0; i < nb_receiver; ++i) {
794 ISerializeMessage* sm = exchanger->messageToReceive(i);
795 //Int32 orig_rank = sm->destSubDomain();
796 ISerializer* s = sm->serializer();
798 Int64 nb_info = s->getInt64();
799 //info() << "RECEIVE NB_INFO=" << nb_info << " from=" << orig_rank;
800 received_infos.resize(nb_info);
801 s->getSpan(received_infos);
802 //if ((nb_info % 3)!=0)
803 //fatal() << "info size can not be divided by 3";
804 Integer z = 0;
805 while (z < nb_info) {
806 Int64 node_uid = received_infos[z + 0];
807 Int32 face_type = (Int32)received_infos[z + 3];
808 ItemTypeInfo* itt = itm->typeFromId(face_type);
809 Integer face_nb_node = itt->nbLocalNode();
810 Int64Array& a = nodes_info.lookupAdd(node_uid)->value();
811 a.addRange(Int64ConstArrayView(6 + face_nb_node, &received_infos[z]));
812 z += 6;
813 z += face_nb_node;
814 }
815 }
816 Integer my_max_face_node = 0;
817 for (HashTableMapT<Int64, SharedArray<Int64>>::Enumerator inode(nodes_info); ++inode;) {
818 Int64ConstArrayView a = *inode;
819 Integer nb_info = a.size();
820 Integer z = 0;
821 Integer node_nb_face = 0;
822 while (z < nb_info) {
823 ++node_nb_face;
824 Int32 face_type = (Int32)a[z + 3];
825 ItemTypeInfo* itt = itm->typeFromId(face_type);
826 Integer face_nb_node = itt->nbLocalNode();
827 z += 6;
828 z += face_nb_node;
829 }
830 my_max_face_node = math::max(node_nb_face, my_max_face_node);
831 }
832 Integer global_max_face_node = pm->reduce(Parallel::ReduceMax, my_max_face_node);
833 debug() << "GLOBAL MAX FACE NODE=" << global_max_face_node;
834 // OK, maintenant donne comme uid de la face (node_uid * global_max_face_node + index)
835 IntegerUniqueArray indexes;
836 boundary_infos_to_send = BoundaryInfosMap(nb_rank, true);
837
838 for (HashTableMapT<Int64, SharedArray<Int64>>::Enumerator inode(nodes_info); ++inode;) {
839 Int64ConstArrayView a = *inode;
840 Integer nb_info = a.size();
841 Integer z = 0;
842 Integer node_nb_face = 0;
843 indexes.clear();
844 while (z < nb_info) {
845 Int64 node_uid = a[z + 0];
846 Int32 sender_rank = (Int32)a[z + 1];
847 Int64 face_uid = a[z + 2];
848 Int32 face_type = (Int32)a[z + 3];
849 ItemTypeInfo* itt = itm->typeFromId(face_type);
850 Integer face_nb_node = itt->nbLocalNode();
851
852 // Checks if the face is already in the list:
853 Integer face_index = node_nb_face;
854 Int32 face_new_owner = sender_rank;
855 for (Integer y = 0; y < node_nb_face; ++y) {
856 if (memcmp(&a[indexes[y] + 6], &a[z + 6], sizeof(Int64) * face_nb_node) == 0) {
857 face_index = y;
858 face_new_owner = (Int32)a[indexes[y] + 1];
859 }
860 }
861 Int64 face_new_uid = (node_uid * global_max_face_node) + face_index;
862 Int64Array& v = boundary_infos_to_send.lookupAdd(sender_rank)->value();
863 // Indicates to the owner of this face its new uid
864 v.add(face_uid);
865 v.add(face_new_uid);
866 v.add(face_new_owner);
867 indexes.add(z);
868 z += 6;
869 z += face_nb_node;
870 ++node_nb_face;
871 }
872 my_max_face_node = math::max(node_nb_face, my_max_face_node);
873 }
874 }
876
877 _exchangeData(exchanger.get(), boundary_infos_to_send);
878 {
879 Integer nb_receiver = exchanger->nbReceiver();
880 debug() << "NB RECEIVER=" << nb_receiver;
881 Int64UniqueArray received_infos;
882 for (Integer i = 0; i < nb_receiver; ++i) {
883 ISerializeMessage* sm = exchanger->messageToReceive(i);
884 ISerializer* s = sm->serializer();
886 Int64 nb_info = s->getInt64();
887 received_infos.resize(nb_info);
888 s->getSpan(received_infos);
889 if ((nb_info % 3) != 0)
890 ARCANE_FATAL("info size can not be divided by 3 v={0}", nb_info);
891 ;
892 Int64 nb_item = nb_info / 3;
893 for (Int64 z = 0; z < nb_item; ++z) {
894 Int64 old_uid = received_infos[(z * 3)];
895 Int64 new_uid = received_infos[(z * 3) + 1];
896 Int32 new_owner = (Int32)received_infos[(z * 3) + 2];
897 impl::MutableItemBase face(faces_map.tryFind(old_uid));
898 if (face.null())
899 ARCANE_FATAL("Can not find own face uid={0}", old_uid);
900 face.setUniqueId(new_uid);
901 face.setOwner(new_owner, my_rank);
902 }
903 }
904 }
905
906 traceMng()->flush();
907 pm->barrier();
908 debug() << "END OF TEST NEW FACE COMPUTE";
909 return;
910}
911
912/*---------------------------------------------------------------------------*/
913/*---------------------------------------------------------------------------*/
921{
922 bool is_verbose = m_mesh_builder->isVerbose();
923
924 ItemInternalMap& cells_map = m_mesh->cellsMap();
925
926 // In sequential mode, the uniqueIds() of the cells cannot exceed the
927 // size of Integers even in 32 bits.
928 Int32 max_uid = 0;
929 cells_map.eachItem([&](Item cell) {
930 Int32 cell_uid = cell.uniqueId().asInt32();
931 if (cell_uid > max_uid)
932 max_uid = cell_uid;
933 });
934 info() << "Max uid=" << max_uid;
935 Integer nb_computed = max_uid + 1;
936 Int32UniqueArray cell_first_face_uid(nb_computed, 0);
937 Int32UniqueArray cell_nb_num_back_face(nb_computed, 0);
938 Int32UniqueArray cell_true_boundary_face(nb_computed, 0);
939
940 cells_map.eachItem([&](Cell cell) {
941 Int32 cell_uid = cell.uniqueId().asInt32();
942 Integer nb_num_back_face = 0;
943 Integer nb_true_boundary_face = 0;
944 for (Face face : cell.faces()) {
945 if (face.backCell() == cell)
946 ++nb_num_back_face;
947 else if (face.nbCell() == 1) {
948 ++nb_true_boundary_face;
949 }
950 }
951 cell_nb_num_back_face[cell_uid] = nb_num_back_face;
952 cell_true_boundary_face[cell_uid] = nb_true_boundary_face;
953 });
954
955 Integer current_face_uid = 0;
956 for (Integer i = 0; i < nb_computed; ++i) {
957 cell_first_face_uid[i] = current_face_uid;
958 current_face_uid += cell_nb_num_back_face[i] + cell_true_boundary_face[i];
959 }
960
961 if (is_verbose) {
962 cells_map.eachItem([&](Item cell) {
963 Int32 i = cell.uniqueId().asInt32();
964 info() << "Recv: Cell FaceInfo celluid=" << i
965 << " firstfaceuid=" << cell_first_face_uid[i]
966 << " nbback=" << cell_nb_num_back_face[i]
967 << " nbbound=" << cell_true_boundary_face[i];
968 });
969 }
970
971 cells_map.eachItem([&](Cell cell) {
972 Int32 cell_uid = cell.uniqueId().asInt32();
973 Integer nb_num_back_face = 0;
974 Integer nb_true_boundary_face = 0;
975 for (Face face : cell.faces()) {
976 Int64 face_new_uid = NULL_ITEM_UNIQUE_ID;
977 if (face.backCell() == cell) {
978 face_new_uid = cell_first_face_uid[cell_uid] + nb_num_back_face;
979 ++nb_num_back_face;
980 }
981 else if (face.nbCell() == 1) {
982 face_new_uid = cell_first_face_uid[cell_uid] + cell_nb_num_back_face[cell_uid] + nb_true_boundary_face;
983 ++nb_true_boundary_face;
984 }
985 if (face_new_uid != NULL_ITEM_UNIQUE_ID) {
986 face.mutableItemBase().setUniqueId(face_new_uid);
987 }
988 }
989 });
990
991 if (is_verbose) {
992 OStringStream ostr;
993 cells_map.eachItem([&](Cell cell) {
994 Integer face_index = 0;
995 for (Face face : cell.faces()) {
996 Int64 opposite_cell_uid = NULL_ITEM_UNIQUE_ID;
997 bool true_boundary = false;
998 bool internal_other = false;
999 if (face.backCell() == cell) {
1000 }
1001 else if (face.nbCell() == 1) {
1002 true_boundary = true;
1003 }
1004 else {
1005 internal_other = true;
1006 opposite_cell_uid = face.backCell().uniqueId().asInt64();
1007 }
1008 ostr() << "NEW LOCAL ID FOR CELLFACE cell_uid=" << cell.uniqueId() << ' '
1009 << face_index << " uid=" << face.uniqueId() << " (";
1010 for (Node node : face.nodes())
1011 ostr() << ' ' << node.uniqueId();
1012 ostr() << ")";
1013 if (internal_other)
1014 ostr() << " internal-other";
1015 if (true_boundary)
1016 ostr() << " true-boundary";
1017 if (opposite_cell_uid != NULL_ITEM_ID)
1018 ostr() << " opposite " << opposite_cell_uid;
1019 ostr() << '\n';
1020 ++face_index;
1021 }
1022 });
1023 info() << ostr.str();
1024 }
1025}
1026
1027/*---------------------------------------------------------------------------*/
1028/*---------------------------------------------------------------------------*/
1029
1030} // End namespace Arcane::mesh
1031
1032/*---------------------------------------------------------------------------*/
1033/*---------------------------------------------------------------------------*/
#define ARCANE_FATAL(...)
Macro throwing a FatalErrorException.
Integer size() const
Number of elements in the vector.
void fill(ConstReferenceType value)
Fills the array with the value value.
void resize(Int64 s)
Changes the number of elements in the array to s.
void clear()
Removes the elements from the array.
void add(ConstReferenceType val)
Adds element val to the end of the array.
const T * data() const
Access to the root of the array without any protection.
void reserve(Int64 new_capacity)
Reserves memory for new_capacity elements.
Cell of a mesh.
Definition Item.h:1300
FaceConnectedListViewType faces() const
List of faces of the cell.
Definition Item.h:1403
Constant view of an array of type T.
constexpr Integer size() const noexcept
Number of elements in the array.
Class to convert a FaceLocalId to a face.
Face of a cell.
Definition Item.h:1032
Cell frontCell() const
Cell in front of the face (null cell if none).
Definition Item.h:1780
Cell cell(Int32 i) const
i-th cell of the face
Definition Item.h:1793
Int32 nbCell() const
Number of cells of the face (1 or 2).
Definition Item.h:1129
bool isSubDomainBoundary() const
Indicates if the face is on the subdomain boundary (i.e nbCell()==1).
Definition Item.h:1148
Cell backCell() const
Cell behind the face (null cell if none).
Definition Item.h:1774
Hash table for associative arrays.
Data * lookupAdd(KeyTypeConstRef id, const ValueType &value, bool &is_add)
Searches for or adds the value corresponding to key id.
bool add(KeyTypeConstRef id, const ValueType &value)
Adds the value value corresponding to key id.
bool hasKey(KeyTypeConstRef id)
true if a value with key id is present
Interface of an entity family.
Definition IItemFamily.h:83
virtual Int32 maxLocalId() const =0
virtual Integer faceBuilderVersion() const =0
Face numbering version.
Information exchange between processors.
virtual void addSender(Int32 rank)=0
Adds a processor to send to.
virtual Integer nbSender() const =0
Number of processors to which we send.
virtual Integer nbReceiver() const =0
Number of processors from which we will receive messages.
virtual bool initializeCommunicationsMessages()=0
Calculates communications.
virtual ISerializeMessage * messageToSend(Integer i)=0
Message intended for the i-th processor.
virtual void processExchange()=0
Performs the exchange using the default options of ParallelExchangerOptions.
Interface of the parallelism manager for a subdomain.
virtual Int32 commRank() const =0
Rank of this instance in the communicator.
virtual void allGatherVariable(ConstArrayView< char > send_buf, Array< char > &recv_buf)=0
Performs an all-gather operation across all processors.
virtual Int32 commSize() const =0
Number of instances in the communicator.
virtual bool isParallel() const =0
Returns true if the execution is parallel.
virtual char reduce(eReduceType rt, char v)=0
Performs a reduction of type rt on the real v and returns the value.
virtual void barrier()=0
Performs a barrier.
virtual Int64 getInt64()=0
Retrieve a size.
virtual void allocateBuffer()=0
Allocates the serializer memory.
virtual void putSpan(Span< const Real > values)
Add the array values.
virtual void getSpan(Span< Real > values)
Retrieve the array values.
virtual void reserveSpan(eBasicDataType dt, Int64 n)=0
Reserves memory for n values of dt.
virtual void setMode(eMode new_mode)=0
Sets the current mode.
virtual void putInt64(Int64 value)=0
Add the integer value.
virtual void flush()=0
Flushes all streams.
Int32 flags() const
Flags of the entity.
bool null() const
True if the entity is the null entity.
@ II_Shared
The entity is shared by another subdomain.
Definition ItemFlags.h:59
@ II_HasBackCell
The entity has a back cell.
Definition ItemFlags.h:53
@ II_SubDomainBoundary
The entity is at the boundary of two subdomains.
Definition ItemFlags.h:60
Info on a mesh entity type.
Integer nbLocalNode() const
Number of nodes of the entity.
Mesh entity type manager.
Definition ItemTypeMng.h:66
ItemTypeInfo * typeFromId(Integer id) const
Type corresponding to the number id.
Unique identifier of an entity.
Node node(Int32 i) const
i-th node of the entity
Definition Item.h:840
NodeConnectedListViewType nodes() const
List of nodes of the entity.
Definition Item.h:843
Int32 nbNode() const
Number of nodes of the entity.
Definition Item.h:837
Base class for a mesh element.
Definition Item.h:84
impl::MutableItemBase mutableItemBase() const
Mutable internal part of the entity.
Definition Item.h:394
constexpr Int32 localId() const
Local identifier of the entity in the processor subdomain.
Definition Item.h:233
Int32 owner() const
Owner subdomain number of the entity.
Definition Item.h:252
ItemUniqueId uniqueId() const
Unique identifier across all domains.
Definition Item.h:239
constexpr bool null() const
true if the entity is null (i.e. not connected to the mesh)
Definition Item.h:230
impl::ItemBase itemBase() const
Internal part of the entity.
Definition Item.h:383
Int16 type() const
Entity type.
Definition Item.h:255
virtual MessageRank destination() const =0
Destination rank (if isSend() is true) or sender.
virtual ISerializer * serializer()=0
Serializer.
Int32 value() const
Rank value.
Definition MessageRank.h:76
void setOwner(Integer suid, Int32 current_sub_domain)
Sets the sub-domain number of the entity owner.
void addFlags(Int32 added_flags)
Adds the flags added_flags to those of the entity.
Node of a mesh.
Definition Item.h:598
Output stream linked to a String.
InstanceType * get() const
Associated instance or nullptr if none.
Reference to an instance.
1D vector of data with reference semantics.
const char * localstr() const
Returns the conversion of the instance into UTF-8 encoding.
Definition String.cc:229
TraceAccessor(ITraceMng *m)
Constructs an accessor via the trace manager m.
TraceMessageDbg debug(Trace::eDebugLevel=Trace::Medium) const
Flow for a debug message.
TraceMessage fatal() const
Flow for a fatal error message.
TraceMessage info() const
Flow for an information message.
TraceMessage error() const
Flow for an error message.
ITraceMng * traceMng() const
Trace manager.
TraceMessage pwarning() const
1D data vector with value semantics (STL style).
Implementation of a mesh.
Definition DynamicMesh.h:98
IParallelMng * parallelMng() override
Parallelism manager.
String name() const override
Mesh name.
IMeshUniqueIdMng * meshUniqueIdMng() const override
Unique ID numbering manager.
void _computeFacesUniqueIdsParallelV2()
Calculates the unique IDs for each face in parallel V2.
void _computeFacesUniqueIdsSequential()
Calculates the unique IDs for each face sequentially.
FaceUniqueIdBuilder(DynamicMeshIncrementalBuilder *mesh_builder)
Constructs an instance for the mesh.
void _exchangeData(IParallelExchanger *exchanger, BoundaryInfosMap &boundary_infos_to_send)
void _checkNoDuplicate()
Checks that there are no duplicate uniqueIds.
void _computeFacesUniqueIdsParallelV1()
Calculates the unique numbers for each face in parallel.
Associative array of ItemInternal.
void eachItem(const Lambda &lambda)
Template function to iterate over the instance's entities.
bool hasKey(Int64 key)
true if a value with the key id is present
impl::ItemBase tryFind(Int64 key) const
Returns the entity associated with key if found, or the null entity otherwise.
static Face findFaceInNode2(Node node, Integer face_type_id, Int64ConstArrayView face_nodes_uid)
Definition ItemTools.cc:44
Helper class for parallel determination of face unique_ids.
T max(const T &a, const T &b, const T &c)
Returns the maximum of three elements.
Definition MathUtils.h:407
@ ReduceMax
Maximum of values.
Ref< IParallelExchanger > createExchangerRef(IParallelMng *pm)
Returns an interface to transfer messages between ranks.
Real getRealTime()
Real time used in seconds.
Array< Int64 > Int64Array
Dynamic one-dimensional array of 64-bit integers.
Definition UtilsTypes.h:125
bool arcaneIsCheck()
True if running in check mode.
Definition Misc.cc:66
UniqueArray< Int64 > Int64UniqueArray
Dynamic 1D array of 64-bit integers.
Definition UtilsTypes.h:339
std::int64_t Int64
Signed integer type of 64 bits.
Int32 Integer
Type representing an integer.
ConstArrayView< Int64 > Int64ConstArrayView
C equivalent of a 1D array of 64-bit integers.
Definition UtilsTypes.h:480
UniqueArray< Int32 > Int32UniqueArray
Dynamic 1D array of 32-bit integers.
Definition UtilsTypes.h:341
double Real
Type representing a real number.
UniqueArray< Integer > IntegerUniqueArray
Dynamic 1D array of integers.
Definition UtilsTypes.h:347
std::int32_t Int32
Signed integer type of 32 bits.