Arcane  4.2.3.0
Documentation développeur
Chargement...
Recherche...
Aucune correspondance
MeshCutService.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/* MeshCutService.cc (C) 2000-2026 */
9/* */
10/* Service permettant la création d'un maillage avec une coupe d'un autre */
11/* maillage. */
12/*---------------------------------------------------------------------------*/
13/*---------------------------------------------------------------------------*/
14
15#include "arcane/utils/StringBuilder.h"
16
17#include "arcane/core/IGhostLayerMng.h"
18#include "arcane/core/IItemFamily.h"
19#include "arcane/core/IMesh.h"
20#include "arcane/core/IMeshFactoryMng.h"
21#include "arcane/core/IMeshMng.h"
22#include "arcane/core/IMeshModifier.h"
23#include "arcane/core/IPrimaryMesh.h"
24#include "arcane/core/MeshBuildInfo.h"
25#include "arcane/core/IMeshSection.h"
26#include "arcane/core/IVariableMng.h"
27#include "arcane/core/VariableMetaData.h"
28
29#include "arcane/core/internal/IVariableInternal.h"
30#include "arcane/core/internal/IVariableMngInternal.h"
31
32#include "arcane/std/MeshCut_axl.h"
33
34/*---------------------------------------------------------------------------*/
35/*---------------------------------------------------------------------------*/
36
37namespace Arcane
38{
39
40/*---------------------------------------------------------------------------*/
41/*---------------------------------------------------------------------------*/
42
43namespace
44{
52struct NodeOnEdge
53{
54 NodeOnEdge(const Node first_node, const Node second_node)
55 {
56 const Int64 a = first_node.uniqueId();
57 const Int64 b = second_node.uniqueId();
58 if (a < b) {
59 m_node0 = first_node;
60 m_node1 = second_node;
61 m_uid_node0 = a;
62 m_uid_node1 = b;
63 }
64 else {
65 m_node0 = second_node;
66 m_node1 = first_node;
67 m_uid_node0 = b;
68 m_uid_node1 = a;
69 }
70 }
71
72 NodeOnEdge(Int64 first_node, Int64 second_node)
73 {
74 if (first_node < second_node) {
75 m_uid_node0 = first_node;
76 m_uid_node1 = second_node;
77 }
78 else {
79 m_uid_node0 = second_node;
80 m_uid_node1 = first_node;
81 }
82 }
83
84 NodeOnEdge() = default;
85
86 bool operator<(const NodeOnEdge& other) const
87 {
88 if (m_uid_node0 != other.m_uid_node0) {
89 return m_uid_node0 < other.m_uid_node0;
90 }
91 return m_uid_node1 < other.m_uid_node1;
92 }
93
94 bool operator==(const NodeOnEdge& other) const
95 {
96 return m_uid_node0 == other.m_uid_node0 && m_uid_node1 == other.m_uid_node1;
97 }
98
99 Node m_node0;
100 Node m_node1;
101
102 Int64 m_uid_node0 = -1;
103 Int64 m_uid_node1 = -1;
104
105 Int64 m_uid_new_node = -1;
106 Int32 m_owner_new_node = -1;
107};
108
109/*---------------------------------------------------------------------------*/
110/*---------------------------------------------------------------------------*/
111
115struct FaceLite
116{
117 FaceLite(const Ref<NodeOnEdge>& node0, const Ref<NodeOnEdge>& node1)
118 : m_node0(node0->m_uid_node0 < node1->m_uid_node0 ? node0 : node1)
119 , m_node1(node0->m_uid_node0 < node1->m_uid_node0 ? node1 : node0)
120 {
121 const Int64 node00 = node0->m_uid_node0;
122 const Int64 node01 = node0->m_uid_node1;
123 const Int64 node10 = node1->m_uid_node0;
124 const Int64 node11 = node1->m_uid_node1;
125
126 if (node00 < node10) {
127 m_node0 = node0;
128 m_node1 = node1;
129 }
130 else if (node00 > node10) {
131 m_node0 = node1;
132 m_node1 = node0;
133 }
134 else {
135 if (node01 == node11) {
136 ARCANE_FATAL("Impossible (normalement...)");
137 }
138 if (node01 < node11) {
139 m_node0 = node0;
140 m_node1 = node1;
141 }
142 else {
143 m_node0 = node1;
144 m_node1 = node0;
145 }
146 }
147 }
148
149 bool operator<(const FaceLite& other) const
150 {
151 return m_node0->operator<(*(other.m_node0.get())) || (m_node0->operator==(*(other.m_node0.get())) && m_node1->operator<(*(other.m_node1.get())));
152 }
153
154 bool operator==(const FaceLite& other) const
155 {
156 return m_node0->operator==(*(other.m_node0.get())) && m_node1->operator==(*(other.m_node1.get()));
157 }
158
159 Ref<NodeOnEdge> m_node0;
160 Ref<NodeOnEdge> m_node1;
161
162 Int64 m_uid_new_face = -1;
163 Int32 m_owner_new_face = -1;
164};
165
166/*---------------------------------------------------------------------------*/
167/*---------------------------------------------------------------------------*/
168
172struct NodeIntersection
173{
174 NodeIntersection(const Node first_node, const Node second_node, const Real3& intersection_pos)
175 : m_new_node(makeRef(new NodeOnEdge(first_node, second_node)))
176 , m_intersection_pos(intersection_pos)
177 {}
178
179 NodeIntersection(Int64 first_node, Int64 second_node, const Real3& intersection_pos)
180 : m_new_node(makeRef(new NodeOnEdge(first_node, second_node)))
181 , m_intersection_pos(intersection_pos)
182 {}
183
184 NodeIntersection() = default;
185
186 bool operator<(const NodeIntersection& other) const
187 {
188 return m_new_node->operator<(*(other.m_new_node.get()));
189 }
190
191 bool operator==(const NodeIntersection& other) const
192 {
193 return m_new_node->operator==(*(other.m_new_node.get()));
194 }
195
196 Ref<NodeOnEdge> m_new_node;
197 Real3 m_intersection_pos{ -1 };
198};
199
200/*---------------------------------------------------------------------------*/
201/*---------------------------------------------------------------------------*/
202
203struct UnknownNodeOrFace
204{
205 UnknownNodeOrFace(Int64 node0_uid, Int64 node1_uid, Int32 who) : m_node0_uid(node0_uid), m_node1_uid(node1_uid), m_who(who){}
206 bool null() const {return m_who == -1;}
207 bool operator==(const UnknownNodeOrFace& other) const{return m_node0_uid == other.m_node0_uid && m_node1_uid == other.m_node1_uid;}
208 Int64 m_node0_uid;
209 Int64 m_node1_uid;
210 Int32 m_who;
211};
212
213/*---------------------------------------------------------------------------*/
214/*---------------------------------------------------------------------------*/
215
216/*---------------------------------------------------------------------------*/
217/*---------------------------------------------------------------------------*/
218
219template <class T>
220struct VariableGroup
221{
222 UniqueArray<ArrayView<T>> dim1;
223 UniqueArray<Array2View<T>> dim2;
224};
225
226template <class T>
227struct VariableGroupType
228{
229 bool isUnknownUsed() { return !(unknown.dim1.empty() && unknown.dim2.empty()); }
230 bool isCellsUsed() { return !(cells.dim1.empty() && cells.dim2.empty()); }
231 bool isFacesUsed() { return !(faces.dim1.empty() && faces.dim2.empty()); }
232 bool isNodesUsed() { return !(nodes.dim1.empty() && nodes.dim2.empty()); }
233
234 VariableGroup<T> unknown;
235 VariableGroup<T> cells;
236 VariableGroup<T> faces;
237 VariableGroup<T> nodes;
238};
239
240template <class T>
241struct VariableOriClone
242{
243 VariableGroupType<T> ori;
244 VariableGroupType<T> clone;
245};
246} // namespace
247
248/*---------------------------------------------------------------------------*/
249/*---------------------------------------------------------------------------*/
250
251/*---------------------------------------------------------------------------*/
252/*---------------------------------------------------------------------------*/
253
261class MeshCutService
262: public ArcaneMeshCutObject
263{
264 public:
265
266 explicit MeshCutService(const ServiceBuildInfo& sbi)
268 , m_creation_type(sbi.creationType())
269 {}
270
271 public:
272
273 void addPlane(const Real3& p0, const Real3& normal) override;
274
276
277 void setServiceMeshUniqueId(Int32 unique_id) override;
278
279 VariableCollection variables() override;
280
281 void updateSection() override;
282
284 {
285 return m_cloned_mesh->handle();
286 }
287
288 private:
289
290 void _createMesh();
291 void _createVariables();
292 void _createNodesAndCells(Int32 plan_pos, Int32& sd_nb_node, UniqueArray<NodeIntersection>& new_nodes, Int32 ajust_node_pos, Int32& sd_nb_cell, UniqueArray<Int64>& new_cells, Int32& sd_nb_face, UniqueArray<FaceLite>& new_faces, UniqueArray<Cell>& ori_cells);
293 void _makeUniqueCellUID(Int32 sd_nb_cell, UniqueArray<Int64>& new_cells, UniqueArray<NodeIntersection>& new_nodes);
294
295 void _fillNodeUID(Int32& sd_nb_node, UniqueArray<NodeIntersection>& new_nodes);
296 Int32 _makeUniqueNodeUID(Int32 sd_nb_node, UniqueArray<NodeIntersection>& new_nodes);
297 void _fillFaceUID(Int32& sd_nb_face, UniqueArray<FaceLite>& new_faces);
298 Int32 _makeUniqueFaceUID(Int32 sd_nb_face, UniqueArray<FaceLite>& new_faces);
299 void _compute();
300
301 std::optional<Int64> _find(Span<FaceLite> new_faces, Int64 node_uid0, Int64 node_uid1);
302
303 void _addFaces(UniqueArray<FaceLite>& new_faces) const;
304 void _addCells(UniqueArray<Int64>& new_cells);
305 void _setCoordNodesAndOwner(UniqueArray<NodeIntersection>& new_nodes);
306 void _setFacesOwner(UniqueArray<FaceLite>& new_faces);
307
308 void _updateVariables(UniqueArray<Cell>& ori_cells);
309
310 template <class T>
311 void _updateVariablesT(UniqueArray<Cell>& ori_cells, Int32 type, T);
312
313 template <class T>
314 void _updateArrayVariable(UniqueArray<Cell>& ori_cells, T, VariableOriClone<T>& voc);
315
316 private:
317
318 eServiceType m_creation_type;
319 VariableCollection m_variables_ori;
320 VariableCollection m_variables_cloned;
321 IPrimaryMesh* m_cloned_mesh = nullptr;
323 Int32 m_mesh_uid = -1;
324};
325
326/*---------------------------------------------------------------------------*/
327/*---------------------------------------------------------------------------*/
328
329ARCANE_REGISTER_SERVICE_MESHCUT(MeshCut, MeshCutService);
330
331/*---------------------------------------------------------------------------*/
332/*---------------------------------------------------------------------------*/
333
334/*---------------------------------------------------------------------------*/
335/*---------------------------------------------------------------------------*/
336
338addPlane(const Real3& p0, const Real3& normal)
339{
340 m_plans.add({ p0, math::normalizeReal3(normal) });
341}
342
343/*---------------------------------------------------------------------------*/
344/*---------------------------------------------------------------------------*/
345
348{
349 m_variables_ori = variables;
350}
351
352/*---------------------------------------------------------------------------*/
353/*---------------------------------------------------------------------------*/
354
357{
358 m_mesh_uid = unique_id;
359}
360
361/*---------------------------------------------------------------------------*/
362/*---------------------------------------------------------------------------*/
363
365variables()
366{
367 return m_variables_cloned;
368}
369
370/*---------------------------------------------------------------------------*/
371/*---------------------------------------------------------------------------*/
372
375{
376 if (mesh()->ghostLayerMng()->nbGhostLayer() < 1) {
377 ARCANE_FATAL("A ghost layer is required for this service");
378 }
379 _createMesh();
380 _compute();
381}
382
383/*---------------------------------------------------------------------------*/
384/*---------------------------------------------------------------------------*/
385
386void MeshCutService::
387_createMesh()
388{
389 if (m_cloned_mesh != nullptr) {
390 m_cloned_mesh->modifier()->clearItems();
391 }
392
393 if (mesh()->dimension() != 3) {
394 ARCANE_FATAL("Only 3D meshes are supported");
395 }
396
397 IMeshMng* mm = subDomain()->meshMng();
398
399 if (m_mesh_uid == -1) {
400 if (m_creation_type == ST_CaseOption) {
401 m_mesh_uid = options()->getUniqueIdServiceMesh();
402 }
403 else {
404 m_mesh_uid = 0;
405 }
406 }
407
408 String service_mesh_name = mesh()->name() + "_MeshCut" + m_mesh_uid;
409
410 MeshHandle* mesh_handle = mm->findMeshHandle(service_mesh_name, false);
411
412 if (mesh_handle == nullptr) {
413 IParallelMng* pm = subDomain()->parallelMng();
414 MeshBuildInfo mbi(service_mesh_name);
415 // auto mesh_kind = mbi.meshKind();
416 // mesh_kind.setMeshDimensionKind(eMeshCellDimensionKind::NonManifold);
417 // mbi.addMeshKind(mesh_kind);
418 mbi.addParallelMng(makeRef(pm));
419 m_cloned_mesh = mm->meshFactoryMng()->createMesh(mbi);
420 m_cloned_mesh->modifier()->setDynamic(true);
421 m_cloned_mesh->setDimension(2);
422 m_cloned_mesh->endAllocate();
423 }
424 else {
425 m_cloned_mesh = mesh_handle->mesh()->toPrimaryMesh();
426 m_cloned_mesh->modifier()->clearItems();
427 }
428 _createVariables();
429}
430
431/*---------------------------------------------------------------------------*/
432/*---------------------------------------------------------------------------*/
433
434void MeshCutService::
435_createVariables()
436{
437 IVariableMng* variable_mng = m_cloned_mesh->variableMng();
438 for (VariableCollection::Enumerator i(m_variables_ori); ++i;) {
439 IVariable* var = *i;
440 Ref vmd(var->createMetaDataRef());
441 const String& mesh_name = vmd->meshName();
442 if (mesh_name.null()) {
443 ARCANE_FATAL("Only variables with support are supported.");
444 }
445 if (vmd->isPartial()) {
446 ARCANE_FATAL("Partial variables are not supported.");
447 }
448 const String& full_type = vmd->fullType();
449 const String& base_name = vmd->baseName();
450 Integer property = vmd->property();
451 const String& family_name = vmd->itemFamilyName();
452
453 // info() << "Clone variable : " << vmd->fullName();
454
455 VariableBuildInfo vbi(m_cloned_mesh, base_name, family_name, property);
456 VariableRef* variable_ref = variable_mng->_internalApi()->createVariableFromType(full_type, vbi);
457
458 // info() << "Cloned variable : " << variable_ref->variable()->fullName();
459 m_variables_cloned.add(variable_ref->variable());
460 }
461}
462
463/*---------------------------------------------------------------------------*/
464/*---------------------------------------------------------------------------*/
465
466void MeshCutService::
467_createNodesAndCells(Int32 plan_pos, Int32& sd_nb_node, UniqueArray<NodeIntersection>& new_nodes, Int32 ajust_node_pos, Int32& sd_nb_cell, UniqueArray<Int64>& new_cells, Int32& sd_nb_face, UniqueArray<FaceLite>& new_faces, UniqueArray<Cell>& ori_cells)
468{
469 auto [p0, normal] = m_plans[plan_pos];
470
471 const Int32 mesh_dim = mesh()->dimension();
472
473 VariableNodeReal3& node_coord = mesh()->nodesCoordinates();
474 VariableNodeReal node_dist(VariableBuildInfo(mesh(), "NodeDist"));
475
476 UniqueArray<Int32> face_already_computed;
477
478 // Calcul de la distance signée de chaque noeud par rapport au plan de coupe.
479 ENUMERATE_ (Node, inode, allNodes()) {
480 Real d = math::dot({ node_coord[inode] - p0 }, normal);
481 node_dist[inode] = math::isNearlyZeroWithEpsilon(d, 1e-10) ? 0 : d;
482 }
483
484 // Tableau qui contiendra tous les noeuds d'une future maille.
485 // Une maille 3D, si elle est coupée par le plan, donnera forcément une
486 // maille 2D. Les noeuds de cette maille seront stockés dans ce tableau,
487 // ainsi que, pour chaque noeud, sa position et l'arête dont il est issu
488 // (pour éviter les doublons).
489 UniqueArray<NodeIntersection> point_coords_tmp;
490
491 ENUMERATE_ (Cell, icell, ownCells()) {
492 Cell cell = *icell;
493
494 bool has_face_on_plane = false;
495 {
496 bool cell_useful = true;
497 Int32 nb_node_on_plane = 0;
498
499 // On regarde si la maille traverse le plan ou si des noeuds sont sur le
500 // plan.
501 {
502 bool has_neg = false;
503 bool has_pos = false;
504 for (Node node : cell.nodes()) {
505 Real d = node_dist[node];
506 if (d < 0)
507 has_neg = true;
508 else if (d > 0)
509 has_pos = true;
510 else {
511 nb_node_on_plane++;
512 }
513 }
514
515 // Tous les noeuds sont du même coté du plan.
516 if (!(has_neg && has_pos))
517 cell_useful = false;
518 }
519
520 // Si le nombre de noeuds sur le plan correspond au nombre de noeuds
521 // minimum d'une face, il s'agit peut-être d'une face confondue au plan.
522 // Dans ce cas, il est nécessaire de faire un traitement spécial pour
523 // éviter un doublon de mailles dans le maillage final.
524 if (nb_node_on_plane >= mesh_dim) {
525 for (Face face : cell.faces()) {
526 // Pour éviter que deux processus créent la même maille.
527 if (!face.isOwn())
528 continue;
529 if (face.nbNode() != nb_node_on_plane)
530 continue;
531
532 bool has_egal = true;
533 for (Node node : face.nodes()) {
534 if (node_dist[node] != 0) {
535 has_egal = false;
536 break;
537 }
538 }
539
540 // On a trouvé la face confondue.
541 if (has_egal) {
542
543 // Si elle a déjà été traitée, on passe pour éviter un doublon.
544 if (face_already_computed.contains(face.localId())) {
545 cell_useful = false;
546 break;
547 }
548
549 // On sait que la face est confondue au plan et que la future
550 // maille créée grâce à cette face n'existe pas encore.
551 // On peut donc directement ajouter les noeuds de la face dans la
552 // liste des noeuds de la future maille.
553 face_already_computed.add(face.localId());
554 for (Node node : face.nodes()) {
555 auto ni = NodeIntersection{ node, node, node_coord[node] };
556 point_coords_tmp.add(ni);
557 }
558 has_face_on_plane = true;
559 cell_useful = true;
560 break;
561 }
562 }
563 }
564
565 if (!cell_useful)
566 continue;
567 }
568
569 if (!has_face_on_plane) {
570 // Tableaux définissant les arêtes pour chaque type d'élément.
571 // Chaque arête = [n0, n1] indices noeuds locaux de maille.
572 //
573 static constexpr Integer edges_tetraedron4[6][2] = {
574 { 0, 1 }, { 0, 2 }, { 0, 3 }, { 1, 2 }, { 1, 3 }, { 2, 3 }
575 };
576 static constexpr Integer edges_pyramid5[8][2] = {
577 { 0, 1 }, { 1, 2 }, { 2, 3 }, { 3, 0 }, { 0, 4 }, { 1, 4 }, { 2, 4 }, { 3, 4 }
578 };
579 static constexpr Integer edges_pentaedron6[9][2] = {
580 { 0, 1 }, { 1, 2 }, { 2, 0 }, { 0, 3 }, { 1, 4 }, { 2, 5 }, { 3, 4 }, { 4, 5 }, { 5, 3 }
581 };
582 static constexpr Integer edges_hexaedron8[12][2] = {
583 { 0, 1 }, { 1, 2 }, { 2, 3 }, { 3, 0 }, { 0, 4 }, { 1, 5 }, { 2, 6 }, { 3, 7 }, { 4, 5 }, { 5, 6 }, { 6, 7 }, { 7, 4 }
584 };
585
586 Integer nb_edges = 0;
587 const Integer(*edge_def)[2] = nullptr;
588
589 auto type = cell.itemTypeId();
590
591 if (type == ITI_Tetraedron4) {
592 nb_edges = 6;
593 edge_def = edges_tetraedron4;
594 }
595 else if (type == ITI_Pyramid5) {
596 nb_edges = 8;
597 edge_def = edges_pyramid5;
598 }
599 else if (type == ITI_Pentaedron6) {
600 nb_edges = 9;
601 edge_def = edges_pentaedron6;
602 }
603 else if (type == ITI_Hexaedron8) {
604 nb_edges = 12;
605 edge_def = edges_hexaedron8;
606 }
607
608 else {
609 ARCANE_FATAL("Cell type not supported -- type: {0}", type);
610 }
611
612 // Itère sur toutes les arêtes de la maille.
613 for (Integer i = 0; i < nb_edges; ++i) {
614 Node node0 = cell.node(edge_def[i][0]);
615 Node node1 = cell.node(edge_def[i][1]);
616
617 bool need_compute_intersection = true;
618
619 // Si le noeud 0 est sur le plan.
620 if (node_dist[node0] == 0) {
621 const Real3 p = node_coord[node0];
622
623 auto ni = NodeIntersection{ node0, node0, p };
624
625 if (!point_coords_tmp.contains(ni)) {
626 point_coords_tmp.add(ni);
627 }
628 need_compute_intersection = false;
629 }
630
631 // Si le noeud 1 est sur le plan.
632 if (node_dist[node1] == 0) {
633 const Real3 p = node_coord[node1];
634
635 auto ni = NodeIntersection{ node1, node1, p };
636
637 if (!point_coords_tmp.contains(ni)) {
638 point_coords_tmp.add(ni);
639 }
640 need_compute_intersection = false;
641 }
642
643 // Si l'arrête passe à travers le plan.
644 if (need_compute_intersection && node_dist[node0] * node_dist[node1] < 0) {
645
646 // Paramètre d'interpolation t dans [0,1] pour le point d'intersection
647 // le long de l'arête de node0 à node1.
648 Real t = std::abs(node_dist[node0]) / (std::abs(node_dist[node0]) + std::abs(node_dist[node1]));
649
650 // Calcul du point d'intersection par interpolation linéaire.
651 Real3 p;
652 p.x = node_coord[node0].x + t * (node_coord[node1].x - node_coord[node0].x);
653 p.y = node_coord[node0].y + t * (node_coord[node1].y - node_coord[node0].y);
654 p.z = node_coord[node0].z + t * (node_coord[node1].z - node_coord[node0].z);
655
656 auto ni = NodeIntersection{ node0, node1, p };
657
658 if (!point_coords_tmp.contains(ni)) {
659 point_coords_tmp.add(ni);
660 }
661 }
662 }
663 }
664
665 // Si le nombre de futurs noeuds est suffisant pour en faire une maille.
666 if (point_coords_tmp.size() >= mesh_dim) {
667
668 // Type de maille.
669 if (point_coords_tmp.size() > 6)
670 ARCANE_FATAL("Pas implem : {0}", point_coords_tmp.size());
671
672 new_cells.add(point_coords_tmp.size());
673 new_cells.add(sd_nb_cell);
674 ori_cells.add(cell);
675
676 {
677 // Calcul du barycentre de tous les points d'intersection.
678 Real3 bary{ 0 };
679 for (const auto& node : point_coords_tmp) {
680 bary += node.m_intersection_pos;
681 }
682 bary /= point_coords_tmp.size();
683
684 // On choisit un vecteur de référence arbitraire non parallèle au normal.
685 // Si normal.x est grand (proche de l'axe X), utiliser l'axe Y ; sinon utiliser l'axe X.
686 const Real3 arbitrary = (std::abs(normal.x) > 0.9) ? Real3{ 0.0, 1.0, 0.0 } : Real3{ 1.0, 0.0, 0.0 };
687 // u et v forment une base orthonormale du plan de coupe.
688 // Ils sont perpendiculaires au normal et l'un à l'autre.
689 const Real3 u = math::normalizedCrossProduct3(arbitrary, normal);
690 const Real3 v = math::normalizedCrossProduct3(normal, u);
691
692 // On trie les points d'intersection par angle polaire autour du barycentre.
693 // Cela garantit que le polygone résultant est correctement ordonné (sens inverse des aiguilles d'une montre).
694 UniqueArray<Int64> indices;
695 indices.reserve(point_coords_tmp.size());
696 for (Int64 i = 0; i < point_coords_tmp.size(); ++i) {
697 indices.add(i);
698 }
699
700 std::sort(indices.begin(), indices.end(),
701 [&](const Int64 ia, const Int64 ib) {
702 const Real3& pa = point_coords_tmp[ia].m_intersection_pos;
703 const Real3& pb = point_coords_tmp[ib].m_intersection_pos;
704
705 // Vecteurs allant du barycentre vers chaque point.
706 const Real3 va{ pa - bary };
707 const Real3 vb{ pb - bary };
708
709 // Projeter sur la base 2D du plan (u, v).
710 const Real a_x = math::dot(va, u);
711 const Real a_y = math::dot(va, v);
712
713 const Real b_x = math::dot(vb, u);
714 const Real b_y = math::dot(vb, v);
715
716 // Comparer les angles en utilisant atan2.
717 const Real angle_a = std::atan2(a_y, a_x);
718 const Real angle_b = std::atan2(b_y, b_x);
719
720 return angle_a < angle_b;
721 });
722
723 // On crée la nouvelle maille.
724 // On en profite pour modifier le tableau "indices" :
725 // - avant la boucle, ce tableau contient les indices de positions de
726 // noeuds dans le tableau point_coords_tmp.
727 // - après la boucle, ce tableau contiendra les indices de positions
728 // de ces mêmes noeuds mais dans le tableau "global" new_nodes.
729 for (Int64& idx : indices) {
730 // On regarde si le noeud est déjà présent dans le tableau "global".
731 std::optional<Int64> pos = new_nodes.span().findFirst(point_coords_tmp[idx]);
732 if (pos) {
733 // Attention : on ajoute la position du noeud dans le tableau
734 // "global", pas le UID de celui-ci ! En effet, on ne le connait
735 // pas encore, ce sera ajusté dans la méthode
736 // "_makeUniqueCellUID()".
737 new_cells.add(static_cast<Int32>(pos.value()) + ajust_node_pos);
738 idx = pos.value();
739 }
740 // S'il n'est pas présent, on le crée.
741 else {
742 NodeIntersection& elem = point_coords_tmp[idx];
743
744 // Si les deux noeuds ont le même proprio, on sait que le nouveau noeud aura le même proprio.
745 if (elem.m_new_node->m_node0.owner() == elem.m_new_node->m_node1.owner()) {
746 elem.m_new_node->m_owner_new_node = elem.m_new_node->m_node0.owner();
747 // Si les deux noeuds sont à nous, on est sûr d'être le
748 // propriétaire du nouveau noeud. C'est donc à nous de donner le
749 // UniqueID (unique pour ce processus, la correction sera faite
750 // plus tard, quand on saura le nombre de noeuds de chaque
751 // processus).
752 if (elem.m_new_node->m_node0.isOwn()) {
753 elem.m_new_node->m_uid_new_node = sd_nb_node++;
754 }
755 else {
756 // -2 = nécessite d'être ajouté plus tard.
757 elem.m_new_node->m_uid_new_node = -2;
758 }
759 }
760 new_cells.add(new_nodes.size() + ajust_node_pos);
761 idx = new_nodes.size();
762 new_nodes.add(elem);
763 }
764 }
765
766 // Maintenant, on ajoute les faces.
767 Int64 idxm1 = indices[indices.size() - 1];
768 for (Int64 idx : indices) {
769 FaceLite fl(new_nodes[idx].m_new_node, new_nodes[idxm1].m_new_node);
770 if (!new_faces.contains(fl)) {
771 // info() << "Add face"
772 // << " -- N00 " << fl.m_node0->m_uid_node0
773 // << " -- N01 " << fl.m_node0->m_uid_node1
774 // << " -- N10 " << fl.m_node1->m_uid_node0
775 // << " -- N11 " << fl.m_node1->m_uid_node1;
776
777 // Même principe qu'avec les noeuds juste au-dessus.
778 if (new_nodes[idx].m_new_node->m_owner_new_node == new_nodes[idxm1].m_new_node->m_owner_new_node) {
779 fl.m_owner_new_face = new_nodes[idx].m_new_node->m_owner_new_node;
780 if (fl.m_owner_new_face == subDomain()->subDomainId()) {
781 fl.m_uid_new_face = sd_nb_face++;
782 }
783 else {
784 fl.m_uid_new_face = -2;
785 }
786 }
787 new_faces.add(fl);
788 }
789 idxm1 = idx;
790 }
791 }
792 sd_nb_cell++;
793 }
794 point_coords_tmp.clear();
795 }
796}
797
798/*---------------------------------------------------------------------------*/
799/*---------------------------------------------------------------------------*/
800
801void MeshCutService::
802_makeUniqueCellUID(Int32 sd_nb_cell, UniqueArray<Int64>& new_cells, UniqueArray<NodeIntersection>& new_nodes)
803{
804 IParallelMng* pm = subDomain()->parallelMng();
805
806 Int32 decal = sd_nb_cell;
807 pm->scan(MessagePassing::ReduceSum, ArrayView{ 1, &decal });
808
809 // Int32 nb_cells_global = decal;
810 // pm->broadcast(ArrayView{ 1, &nb_cells_global }, pm->commSize() - 1);
811
812 decal -= sd_nb_cell;
813
814 // info() << "[" << pm->commRank() << "] Scan result (rectified) : " << decal;
815
816 // En plus de décaler les uids des mailles pour les rendre unique sur tout le
817 // domaine, on ajoute les vrais UID des noeuds à la place des positions des
818 // noeuds dans le tableau "new_nodes".
819 Int32 pos0 = 0;
820 while (pos0 < new_cells.size()) {
821 Int64 type = new_cells[pos0++];
822 new_cells[pos0++] += decal;
823
824 for (Int32 i = 0; i < type; ++i) {
825 Int64& pos_to_uid = new_cells[pos0++];
826 pos_to_uid = new_nodes[pos_to_uid].m_new_node->m_uid_new_node;
827 }
828 }
829 // return nb_cells_global;
830}
831
832/*---------------------------------------------------------------------------*/
833/*---------------------------------------------------------------------------*/
834
835void MeshCutService::
836_fillNodeUID(Int32& sd_nb_node, UniqueArray<NodeIntersection>& new_nodes)
837{
838
839 // À partir d'ici, nous avons un tableau de noeuds. Les noeuds dont on est
840 // sûr qu'ils nous appartiennent ont déjà un UID/Owner.
841 //
842 // Il y a deux autres types de noeuds :
843 // - le noeud dont on pense connaitre le proprio à qui demander le UID,
844 // - le noeud dont on ne connait pas encore le proprio ni le UID.
845
846 IParallelMng* pm = subDomain()->parallelMng();
847 Int32 my_proc = pm->commRank();
848 UniqueArray<UniqueArray<Ref<NodeOnEdge>>> requested_nodes(subDomain()->nbSubDomain());
849
850 //
851 //
852 // Première étape : détermination des proprios potentiels et création des
853 // requêtes.
854 //
855 // Détermination des proprios :
856 // Si un noeud n'a pas encore de proprio, on choisit le proprio parmi les
857 // mailles des noeuds (N0 et N1) de l'arête dont est issu notre noeud (NN).
858 //
859 // N0 NN N1
860 // *-------*-------*
861 //
862 // NN = elem.m_new_node
863 // N0 = elem.m_new_node->m_node0
864 // N1 = elem.m_new_node->m_node1
865 //
866 // Il peut y avoir des cas où le proprio choisi ne possède pas le noeud.
867 // Ce cas sera traité plus tard dans la méthode.
868 //
869 // À partir de là, on a un proprio pour tous les noeuds.
870 //
871 // Création des requêtes :
872 // On a un message pour chaque processus.
873 // Ce message est composé de pairs de UID : les UID des noeuds de l'arête
874 // dont est issu notre noeud (seul moyen d'identifier notre noeud sur tous
875 // les processus (si l'on ne souhaite pas utiliser sa position)).
876 //
877 // Exemple :
878 // request_uid[][] :
879 // P0 : --Nous--
880 // P1 : [NN09_N0_UID, NN09_N1_UID, NN01_N0_UID, NN01_N1_UID, NN00_N0_UID, NN00_N1_UID]
881 // P2 : [NN14_N0_UID, NN14_N1_UID, NN15_N0_UID, NN15_N1_UID]
882 // P3 : []
883 //
884 // request_uid[][] :
885 // P0 : []
886 // P1 : [NN09_N0_UID, NN09_N1_UID]
887 // P2 : --Nous--
888 // P3 : []
889 //
890 // request_uid[][] :
891 // P0 : []
892 // P1 : [NN00_N0_UID, NN00_N1_UID]
893 // P2 : []
894 // P3 : --Nous--
895 //
896 // On stocke aussi, dans un autre tableau, les NodeOnEdge correspondant
897 // aux paires de UID, afin de les retrouver facilement lorsque l'on aura
898 // reçu les réponses des processus.
899 //
900 // Exemple :
901 // requested_nodes[][] :
902 // P0 : --Nous--
903 // P1 : [NN09, NN01, NN00]
904 // P2 : [NN14, NN15]
905 // P3 : []
906 //
907 //
908 {
909 UniqueArray<UniqueArray<Int64>> request_uid(subDomain()->nbSubDomain());
910
911 debug() << "[Node][" << my_proc << "] Step 1";
912
913 for (auto& elem : new_nodes) {
914 // Si le uid est déjà mis, pas besoin de le rechercher...
915 if (elem.m_new_node->m_uid_new_node >= 0) {
916 continue;
917 }
918
919 // Si le proprio n'est pas définit, on doit faire une recherche.
920 if (elem.m_new_node->m_owner_new_node < 0) {
921
922 Node node0 = elem.m_new_node->m_node0;
923 Node node1 = elem.m_new_node->m_node1;
924
925 // Le propriétaire du noeud est le propriétaire de la maille ayant le plus
926 // petit UID, parmi les mailles en commun entre les deux noeuds d'origine.
927
928 // TODO Ajouter traitement particulier pour le cas où node0 == node1
929 Int64 min_uid = INT64_MAX;
930 Int32 owner_min = -1;
931 for (Cell cell0 : node0.cells()) {
932 for (Cell cell1 : node1.cells()) {
933 if (cell0 == cell1) {
934 if (cell0.uniqueId() < min_uid) {
935 min_uid = cell0.uniqueId();
936 owner_min = cell0.owner();
937 }
938 }
939 }
940 }
941
942 // S'il l'on est le proprio, on peut définir le uid du noeud.
943 if (owner_min == subDomain()->subDomainId()) {
944 elem.m_new_node->m_owner_new_node = subDomain()->subDomainId();
945 elem.m_new_node->m_uid_new_node = sd_nb_node++;
946
947 // debug() << "[Node][" << my_proc << " -> " << elem.m_new_node->m_owner_new_node << "] Set UID"
948 // << " -- UID0 : " << elem.m_new_node->m_uid_node0
949 // << " -- UID1 : " << elem.m_new_node->m_uid_node1
950 // << " -- New UID : " << elem.m_new_node->m_uid_new_node;
951 }
952
953 // Sinon, on doit aller demander le uid au proprio.
954 else {
955 elem.m_new_node->m_owner_new_node = owner_min;
956 elem.m_new_node->m_uid_new_node = -2;
957
958 // Une requête est composée uniquement des deux UID des noeuds de
959 // l'arête dont est issue le nouveau noeud. C'est le seul moyen
960 // d'identifier ce noeud pour l'instant (si on exclut l'identification
961 // par sa position).
962 request_uid[elem.m_new_node->m_owner_new_node].add(elem.m_new_node->m_uid_node0);
963 request_uid[elem.m_new_node->m_owner_new_node].add(elem.m_new_node->m_uid_node1);
964
965 requested_nodes[elem.m_new_node->m_owner_new_node].add(elem.m_new_node);
966
967 // debug() << "[Node][" << my_proc << " -> " << elem.m_new_node->m_owner_new_node << "] Ask1"
968 // << " -- UID0 : " << elem.m_new_node->m_uid_node0
969 // << " -- UID1 : " << elem.m_new_node->m_uid_node1;
970 }
971 }
972
973 // Si le proprio est déjà défini, on doit lui demander le uid du noeud.
974 else {
975 request_uid[elem.m_new_node->m_owner_new_node].add(elem.m_new_node->m_uid_node0);
976 request_uid[elem.m_new_node->m_owner_new_node].add(elem.m_new_node->m_uid_node1);
977
978 requested_nodes[elem.m_new_node->m_owner_new_node].add(elem.m_new_node);
979
980 // debug() << "[Node][" << my_proc << " -> " << elem.m_new_node->m_owner_new_node << "] Ask2"
981 // << " -- UID0 : " << elem.m_new_node->m_uid_node0
982 // << " -- UID1 : " << elem.m_new_node->m_uid_node1;
983 }
984 }
985
986 // On envoie les requêtes.
987 {
988 UniqueArray<Parallel::Request> requests(subDomain()->nbSubDomain() * 2);
989
990 debug() << "[Node][" << my_proc << "] Step 1.2";
991
992 for (Int32 sr = 0; sr < subDomain()->nbSubDomain(); ++sr) {
993 if (sr == subDomain()->subDomainId()) {
994 continue;
995 }
996 Int32 size = request_uid[sr].size();
997 requests[sr * 2] = pm->send(ArrayView{ 1, &size }, sr, false);
998 requests[sr * 2 + 1] = pm->send(request_uid[sr], sr, false);
999 // debug() << "[Node][" << my_proc << " -> " << sr << "] Requests : " << request_uid[sr];
1000 }
1001
1002 pm->waitAllRequests(requests);
1003 }
1004 }
1005
1006 //
1007 //
1008 // Deuxième étape : traitement des requêtes des autres processus et gestion
1009 // des inconnus.
1010 //
1011 // Traitement des requêtes :
1012 //
1013 // On effectue le traitement à la réception (donc processus par processus).
1014 // Pour chaque paire de UID (N0-N1), on la recherche dans notre tableau des
1015 // noeuds.
1016 // On l'a trouve, parfait, on place l'UID du noeud (NN) dans le tableau de
1017 // réponse.
1018 //
1019 // Gestion des inconnus :
1020 //
1021 // Si on ne trouve pas la paire dans notre tableau, on est dans le cas où le
1022 // noeud NN est placé sur un noeud du maillage 3D (donc N0 == N1 et
1023 // position(N0) == position(NN)).
1024 // Lorsque l'on itère sur les mailles de N0, on peut tomber sur des mailles
1025 // qui ne sont pas coupées par le plan, donc qui ne possède pas le noeud.
1026 //
1027 // On n'a pas le noeud dans notre tableau. Par contre, on sait qui demande
1028 // ce noeud ! Grâce aux mailles fantômes, on est sûr que tous les processus
1029 // qui souhaite le UID du noeud NN vont le demander au même processus. Nous
1030 // pouvons donc construire la liste de ces processus demandeurs. Processus
1031 // demandeurs dont on est sûr qu'ils possèdent le noeud NN !
1032 // Nous allons donc leur envoyer cette liste et il se débrouilleront pour
1033 // trouver le bon proprio parmi les processus de cette liste !
1034 //
1035 // Réponses :
1036 //
1037 // Le tableau des réponses doit donc contenir deux parties :
1038 // - les UIDs, s'ils sont trouvés,
1039 // - les processus possédant les noeuds inconnus.
1040 //
1041 // Pour avoir la limite entre ces deux parties, on place la taille de la
1042 // première partie en première position du tableau de réponse.
1043 //
1044 // Si un UID est trouvé, on l'ajoute. S'il n'est pas trouvé, on place "-1".
1045 //
1046 // La seconde partie est structurée ainsi :
1047 // - Pour chaque inconnu (pour chaque "-1") :
1048 // - UID du noeud N0
1049 // - UID du noeud N1
1050 // - Nombre de processus demandeurs (donc possédant forcément le noeud NN),
1051 // - Rangs des processus demandeurs.
1052 //
1053 // TODO : A changer :
1054 // L'ordre de la seconde partie n'a pas d'importance.
1055 // En effet, l'ordre de cette partie est le même pour tous les processus (il
1056 // n'y a pas de tri par processus selon l'ordre des requêtes (qui
1057 // correspondrait à l'ordre des "-1")).
1058 // C'est pour ça qu'il y a la présence des "UID du noeud N0" et "UID du
1059 // noeud N1".
1060 // (trier avant envoi économiserait de la mémoire et réduirait la taille de
1061 // la réponse, donc TODO).
1062 //
1063 //
1064 // (Pour l'exemple, on suit le précédent exemple)
1065 // answers_uid[][] :
1066 // P0 : [3, -1, NN01_UID, -1, NN09_N0_UID, NN09_N1_UID, 2, 0, 2, NN00_N0_UID, NN00_N1_UID, 2, 0, 3]
1067 // P1 : --Nous--
1068 // P2 : [1, -1, NN09_N0_UID, NN09_N1_UID, 2, 0, 2 ]
1069 // P3 : [1, -1, NN00_N0_UID, NN00_N1_UID, 2, 0, 3 ]
1070 //
1071 //
1072 {
1073 UniqueArray<UniqueArray<Int64>> answers_uid(subDomain()->nbSubDomain());
1074 {
1075 UniqueArray<UnknownNodeOrFace> unknown_node;
1076 debug() << "[Node][" << my_proc << "] Step 2";
1077
1078 for (Int32 sr = 0; sr < subDomain()->nbSubDomain(); ++sr) {
1079 if (sr == subDomain()->subDomainId()) {
1080 continue;
1081 }
1082 Int32 size = 0;
1083 pm->recv(ArrayView{ 1, &size }, sr);
1084
1085 UniqueArray<Int64> requested_uid(size);
1086 pm->recv(requested_uid, sr);
1087
1088 // Taille de la première partie.
1089 answers_uid[sr].add(0);
1090
1091 // On traite chaque paire de noeud de la requête.
1092 for (Int32 ipair_uid = 0; ipair_uid < requested_uid.size(); ipair_uid += 2) {
1093
1094 std::optional<Int64> pos = new_nodes.span().findFirst(NodeIntersection{ requested_uid[ipair_uid], requested_uid[ipair_uid + 1], Real3{ 0 } });
1095 if (pos) {
1096 answers_uid[sr].add(new_nodes[pos.value()].m_new_node->m_uid_new_node);
1097 // debug() << "[Node][" << my_proc << " <- " << sr << "] Found"
1098 // << " -- UID0 : " << requested_uid[ipair_uid]
1099 // << " -- UID1 : " << requested_uid[ipair_uid + 1]
1100 // << " -- New UID : " << new_nodes[pos.value()].m_new_node->m_uid_new_node;
1101 }
1102
1103 // Il peut arriver que l'on nous demande un noeud que nous n'avons pas.
1104 // Par exemple, si un plan arrive pile sur une des arêtes de nos mailles.
1105 // Par contre, on sait qui en a besoin, donc qui possède une maille avec
1106 // le noeud en question.
1107 // Pour donner cette information, on va ajouter "-1" puis, à la fin de
1108 // la réponse, on va placer les processus en question.
1109 else {
1110 answers_uid[sr].add(-1);
1111 unknown_node.add({ requested_uid[ipair_uid], requested_uid[ipair_uid + 1], sr });
1112 // debug() << "[Node][" << my_proc << " <- " << sr << "] NOT Found"
1113 // << " -- UID0 : " << requested_uid[ipair_uid]
1114 // << " -- UID1 : " << requested_uid[ipair_uid + 1];
1115 }
1116 }
1117 answers_uid[sr][0] = answers_uid[sr].size() - 1;
1118 }
1119
1120 UniqueArray<Int64> who;
1121 debug() << "[Node][" << my_proc << "] Step 2.2";
1122
1123 // Dès qu'il y a eu une paire de noeuds inconnus dans une requête, il y
1124 // a eu un enregistrement de fait dans le tableau unknown_node.
1125 // Il faut maintenant lister, pour chaque paire, qui l'a aussi demandé
1126 // (rechercher les doublons) et envoyer cette liste à chacun d'entre eux.
1127 for (Int32 i = 0; i < unknown_node.size(); ++i) {
1128 if (unknown_node[i].null())
1129 continue;
1130 who.clear();
1131 who.add(unknown_node[i].m_who);
1132 unknown_node[i].m_who = -1;
1133 for (Int32 j = i + 1; j < unknown_node.size(); ++j) {
1134 if (unknown_node[j].null())
1135 continue;
1136 if (unknown_node[j] == unknown_node[i]) {
1137 who.add(unknown_node[j].m_who);
1138 unknown_node[j].m_who = -1;
1139 }
1140 }
1141 // debug() << "[Node] Additionnal infos -- Node0UID : " << unknown_node[i].m_node0_uid << " -- Node1UID : " << unknown_node[i].m_node1_uid << " -- Who : " << who;
1142 for (auto proc : who) {
1143 answers_uid[proc].add(unknown_node[i].m_node0_uid);
1144 answers_uid[proc].add(unknown_node[i].m_node1_uid);
1145 answers_uid[proc].add(who.size());
1146 answers_uid[proc].addRange(who);
1147 }
1148 }
1149 }
1150
1151 {
1152 UniqueArray<Parallel::Request> requests(subDomain()->nbSubDomain() * 2);
1153
1154 // On envoie les réponses.
1155 debug() << "[Node][" << my_proc << "] Step 2.3";
1156 for (Int32 sr = 0; sr < subDomain()->nbSubDomain(); ++sr) {
1157 if (sr == subDomain()->subDomainId()) {
1158 continue;
1159 }
1160
1161 Int32 size = answers_uid[sr].size();
1162 requests[sr * 2] = pm->send(ArrayView{ 1, &size }, sr, false);
1163 requests[sr * 2 + 1] = pm->send(answers_uid[sr], sr, false);
1164
1165 // debug() << "[Node][" << my_proc << " -> " << sr << "] Send " << answers_uid[sr];
1166 }
1167
1168 pm->waitAllRequests(requests);
1169 }
1170 }
1171
1172
1173 //
1174 //
1175 // Troisième étape : Mise à jour des UIDs des noeuds et recherche
1176 // complémentaire de proprios.
1177 //
1178 // On commence par découper la réponse en deux, pour retrouver les deux
1179 // parties tèl que défini plus haut.
1180 // On itère sur la première partie. L'ordre des UIDs reçus est le même que
1181 // l'ordre des noeuds du tableau "requested_nodes" complété dans la première
1182 // partie. On peut ainsi aisément récupérer l'objet correspondant
1183 // (`node_on_edge` dans le code juste en dessous).
1184 //
1185 // Si nous avons reçu un UID valide, on met à jour le noeud correspondant
1186 // et on passe au suivant.
1187 //
1188 // Si nous avons "-1", c'est que le propriétaire que nous avons défini dans
1189 // la première partie n'est pas le bon. Nous devons donc organiser un nouvel
1190 // envoi.
1191 //
1192 // Recherche complémentaire de proprios :
1193 //
1194 // Comme l'ordre des infos complémentaires de la seconde partie du message
1195 // n'est pas forcément le même que l'ordre de la première partie (voir TODO
1196 // au dessus), on doit d'abord rechercher, dans la seconde partie, la paire
1197 // de UID correspondant au noeud que nous traitons actuellement.
1198 //
1199 // On trouve forcément une correspondance.
1200 //
1201 // On est sûr que l'ordre dans lequel est organisée la seconde partie est le
1202 // même pour tous. On va donc se caler sur cette ordre pour transmettre le
1203 // prochain message.
1204 //
1205 // Lorsqu'une correspondance est trouvée, on modifie le message. On remplace
1206 // UID0 par la position de l'objet `Ref<NodeOnEdge>` dans le tableau
1207 // `requested_nodes` (pour éviter une seconde recherche).
1208 //
1209 // Exemple :
1210 // Rappel tableau requested_nodes[][] (Première étape de la méthode) :
1211 // requested_nodes[][] :
1212 // P0 : --Nous--
1213 // P1 : [NN09, NN01, NN00]
1214 //
1215 // Avant modif :
1216 // answered_uid[] :
1217 // P0 : --Nous--
1218 // P1 : [3, -1, NN01_UID, -1, NN09_N0_UID, NN09_N1_UID, 2, 0, 2, NN00_N0_UID, NN00_N1_UID, 2, 0, 3]
1219 //
1220 // Après modif :
1221 // answered_uid[] :
1222 // P0 : --Nous--
1223 // P1 : [3, -1, NN01_UID, -1, 0, NN09_N1_UID, 2, 0, 2, 2, NN00_N1_UID, 2, 0, 3]
1224 //
1225 // Explication de l'exemple :
1226 // On remplace "NN09_N0_UID" par "0" car le noeud "NN09" se trouve à la
1227 // position 0 du tableau `requested_nodes[1][]` (le premier indice est le
1228 // rang du processus cible (donc 1 ici)).
1229 // On remplace "NN00_N0_UID" par "2" car le noeud "NN00" se trouve à la
1230 // position 2 du tableau `requested_nodes[1][]`.
1231 //
1232 // Cette algorithme est en deux étapes pour préserver simplement l'ordre de
1233 // la seconde partie de la réponse.
1234 //
1235 // Une fois `answered_uid[1]` modifié (donc une fois que l'on a fini
1236 // d'itérer sur la première partie de la réponse), on passe à la recherche
1237 // du propriétaire des noeuds restants.
1238 //
1239 // On itère sur la seconde partie de la réponse. Tous les processus
1240 // itèreront dans le même ordre !
1241 //
1242 // On récupère l'objet `Ref<NodeOnEdge>` à l'aide de sa position dans le
1243 // tableau `requested_nodes`. On reproduit l'algo de recherche de proprio de
1244 // la première étape de la méthode en s'assurant de tomber sur un proprio de
1245 // la liste des proprios potentiels.
1246 //
1247 // Par rapport à la première étape de la méthode, on sait de qui recevoir
1248 // des informations et on sait dans quel ordre les interpréter. Donc inutile
1249 // d'envoyer des messages vides si aucune infos n'est nécessaire.
1250 //
1251 // On va utiliser le tableau "request_uid" pour stocker les messages à
1252 // envoyer mais aussi la taille des messages que l'on devra recevoir.
1253 //
1254 // Pour les messages à envoyer, on stocke uniquement les UIDs qui devront
1255 // être appliqués.
1256 // Pour chaque UID stocké du coté du processus envoyeur, l'objet
1257 // `Ref<NodeOnEdge>` correspondant est stocké dans le tableau
1258 // `requested_nodes2` du coté du processus receveur.
1259 //
1260 // Exemple :
1261 // L'algo a déterminé que
1262 // - "NN09" appartient à "2" (et non à "0") et
1263 // - "NN00" appartient à "0" (et non à "3").
1264 //
1265 // request_uid[][] :
1266 // P0 : --Nous-- [0, 0, 1, 0]
1267 // P1 : []
1268 // P2 : []
1269 // P3 : [NN00_UID]
1270 //
1271 // (à lire : P0 attend un message de taille 1 de la part de P2)
1272 // ( P0 envoie NN00_UID à P3)
1273 //
1274 // request_uid[][] :
1275 // P0 : [NN09_UID]
1276 // P1 : []
1277 // P2 : --Nous-- [0, 0, 0, 0]
1278 // P3 : []
1279 //
1280 // request_uid[][] :
1281 // P0 : []
1282 // P1 : []
1283 // P2 : []
1284 // P3 : --Nous-- [1, 0, 0, 0]
1285 //
1286 //
1287 UniqueArray<UniqueArray<Int64>> request_uid(subDomain()->nbSubDomain());
1288 UniqueArray<UniqueArray<Ref<NodeOnEdge>>> requested_nodes2(subDomain()->nbSubDomain());
1289
1290 // On reçoit et traite les réponses.
1291 debug() << "[Node][" << my_proc << "] Step 3";
1292
1293 // Permet de savoir si les prochaines étapes sont utiles.
1294 bool need_more_comm = false;
1295
1296 for (Int32 sr = 0; sr < subDomain()->nbSubDomain(); ++sr) {
1297 if (sr == subDomain()->subDomainId()) {
1298 continue;
1299 }
1300
1301 Int32 total_size = 0;
1302 pm->recv(ArrayView{ 1, &total_size }, sr);
1303
1304 UniqueArray<Int64> answered_uid(total_size);
1305 pm->recv(answered_uid, sr);
1306
1307 if (total_size == 1) {
1308 continue;
1309 }
1310
1311 Int32 answer_to_request_size = static_cast<Int32>(answered_uid[0]);
1312
1313 Span<Int64> answer_to_request = answered_uid.subView(1, answer_to_request_size);
1314 Span<Int64> additionnal_answer = answered_uid.subView(answer_to_request_size+1, answered_uid.size() - (answer_to_request_size+1));
1315
1316 // debug() << "[Node][" << my_proc << " <- " << sr << "] Decoupe"
1317 // << " -- answer_to_request : " << answer_to_request
1318 // << " -- additionnal_answer : " << additionnal_answer;
1319
1320
1321 for (Int32 answer = 0; answer < answer_to_request.size(); ++answer) {
1322 Ref<NodeOnEdge> node_on_edge = requested_nodes[sr][answer];
1323
1324 if (answer_to_request[answer] != -1) {
1325 node_on_edge->m_uid_new_node = answer_to_request[answer];
1326 // debug() << "[Node][" << my_proc << "] Apply"
1327 // << " -- UID0 : " << node_on_edge->m_uid_node0
1328 // << " -- UID1 : " << node_on_edge->m_uid_node1
1329 // << " -- New UID : " << node_on_edge->m_uid_new_node;
1330 }
1331 else {
1332 Int64 pos = 0;
1333 while (pos < additionnal_answer.size()) {
1334 Int64& uid0 = additionnal_answer[pos++];
1335 Int64 uid1 = additionnal_answer[pos++];
1336 Int64 decal = additionnal_answer[pos++];
1337 if (uid0 == node_on_edge->m_uid_node0 && uid1 == node_on_edge->m_uid_node1) {
1338 uid0 = answer;
1339 break;
1340 }
1341 pos += decal;
1342 }
1343 if (pos >= additionnal_answer.size()) {
1344 ARCANE_FATAL("Unknown node -- UID0 : {0} -- UID1 : {1}", node_on_edge->m_uid_node0, node_on_edge->m_uid_node1);
1345 }
1346 }
1347 }
1348
1349 Int64 pos = 0;
1350 Span<Int64> sub_additionnal_answer;
1351
1352 while (pos < additionnal_answer.size()) {
1353 Int64 pos_node_in_array = additionnal_answer[pos++];
1354 pos++;
1355 Int64 decal = additionnal_answer[pos++];
1356
1357 Ref<NodeOnEdge> node_on_edge = requested_nodes[sr][pos_node_in_array];
1358 sub_additionnal_answer = additionnal_answer.subSpan(pos, decal);
1359
1360 pos += decal;
1361
1362 // debug() << "[Node][" << my_proc << " <- " << sr << "] Sub"
1363 // << " -- sub_additionnal_answer : " << sub_additionnal_answer;
1364
1365 Node node0 = node_on_edge->m_node0;
1366 Node node1 = node_on_edge->m_node1;
1367
1368 // Le propriétaire du noeud est le propriétaire de la maille ayant le plus
1369 // petit UID, parmi les mailles en commun entre les deux noeuds d'origine.
1370
1371 // TODO Ajouter traitement particulier pour le cas où node0 == node1
1372 Int64 min_uid = INT64_MAX;
1373 Int32 owner_min = -1;
1374 for (Cell cell0 : node0.cells()) {
1375 for (Cell cell1 : node1.cells()) {
1376 if (cell0 == cell1) {
1377 if (cell0.uniqueId() < min_uid && sub_additionnal_answer.contains(cell0.owner())) {
1378 min_uid = cell0.uniqueId();
1379 owner_min = cell0.owner();
1380 }
1381 }
1382 }
1383 }
1384
1385 // S'il l'on est le proprio, on peut définir le uid du noeud.
1386 if (owner_min == subDomain()->subDomainId()) {
1387 node_on_edge->m_owner_new_node = subDomain()->subDomainId();
1388 node_on_edge->m_uid_new_node = sd_nb_node++;
1389
1390 // On enregistre le UID à envoyer.
1391 if (sub_additionnal_answer.size() > 1) {
1392 need_more_comm = true;
1393
1394 // debug() << "[Node][" << my_proc << "] Send"
1395 // << " -- UID : " << node_on_edge->m_uid_new_node
1396 // << " -- for Node UID0 : " << node_on_edge->m_uid_node0
1397 // << " -- UID1 : " << node_on_edge->m_uid_node1
1398 // << " -- to : " << sub_additionnal_answer;
1399
1400 for (auto proc : sub_additionnal_answer) {
1401 if (proc == my_proc)
1402 continue;
1403 request_uid[proc].add(node_on_edge->m_uid_new_node);
1404 }
1405 }
1406 }
1407
1408 // Sinon, on doit aller demander le uid au proprio.
1409 else if (owner_min >= 0) {
1410 need_more_comm = true;
1411 node_on_edge->m_owner_new_node = owner_min;
1412 node_on_edge->m_uid_new_node = -2;
1413
1414 if (request_uid[my_proc].empty()) {
1415 request_uid[my_proc].resize(subDomain()->nbSubDomain(), 0);
1416 }
1417
1418 request_uid[my_proc][owner_min]++;
1419
1420 requested_nodes2[owner_min].add(node_on_edge);
1421
1422 // debug() << "[Node][" << my_proc << " -> " << node_on_edge->m_owner_new_node << "] Recv UID for Node"
1423 // << " -- UID0 : " << node_on_edge->m_uid_node0
1424 // << " -- UID1 : " << node_on_edge->m_uid_node1;
1425 }
1426
1427 else {
1428 ARCANE_FATAL("Node owner not found");
1429 }
1430 }
1431 }
1432
1433 // Si nous n'avons pas de UID manquants et que personne n'a besoin de l'un
1434 // de nos UIDs, on passe cette étape.
1435 // Sinon :
1436 if (need_more_comm) {
1437 {
1438 UniqueArray<Parallel::Request> requests(subDomain()->nbSubDomain());
1439
1440 // On envoie les UID complémentaires.
1441 debug() << "[Node][" << my_proc << "] Step 3.2";
1442
1443 for (Int32 sr = 0; sr < subDomain()->nbSubDomain(); ++sr) {
1444 if (sr == subDomain()->subDomainId()) {
1445 // debug() << "[Node][" << my_proc << " -> " << sr << "] My RequestsC : " << request_uid[sr];
1446 continue;
1447 }
1448 if (request_uid[sr].empty()) continue;
1449 requests[sr] = pm->send(request_uid[sr], sr, false);
1450 // debug() << "[Node][" << my_proc << " -> " << sr << "] RequestsC : " << request_uid[sr];
1451 }
1452
1453 pm->waitAllRequests(requests);
1454 }
1455
1456 //
1457 //
1458 // Quatrième étape : Mise à jour des UIDs manquants.
1459 //
1460 // On est sûr d'avoir une réponse complète, donc c'est plus simple.
1461 // On récupère le nombre de messages à recevoir de la part de l'autre
1462 // processus et on traite son message dans l'ordre. Le tableau
1463 // `requested_nodes2[][]` est dans le même ordre ce qui facilite les
1464 // choses.
1465 //
1466 //
1467
1468 if (!request_uid[my_proc].empty()) {
1469 // On reçoit et traite les UID complémentaires.
1470 debug() << "[Node][" << my_proc << "] Step 4";
1471
1472 for (Int32 sr = 0; sr < subDomain()->nbSubDomain(); ++sr) {
1473 if (sr == subDomain()->subDomainId()) {
1474 continue;
1475 }
1476
1477 Int64 size = request_uid[my_proc][sr];
1478 if (size == 0) continue;
1479
1480 // debug() << "[Node][" << my_proc << " -> " << sr << "] Size recv : " << size;
1481
1482
1483 UniqueArray<Int64> requested_uid(size);
1484 pm->recv(requested_uid, sr);
1485 for (Int32 i = 0; i < size; ++i) {
1486 Ref<NodeOnEdge> node_on_edge = requested_nodes2[sr][i];
1487 node_on_edge->m_uid_new_node = requested_uid[i];
1488 // debug() << "[Nœud][" << my_proc << "] Appliquer2"
1489 // << " -- UID0 : " << node_on_edge->m_uid_node0
1490 // << " -- UID1 : " << node_on_edge->m_uid_node1
1491 // << " -- Nouveau UID : " << node_on_edge->m_uid_new_node;
1492 }
1493 }
1494 }
1495 }
1496}
1497
1498/*---------------------------------------------------------------------------*/
1499/*---------------------------------------------------------------------------*/
1500
1501Int32 MeshCutService::
1502_makeUniqueNodeUID(Int32 sd_nb_node, UniqueArray<NodeIntersection>& new_nodes)
1503{
1504 IParallelMng* pm = subDomain()->parallelMng();
1505
1506 UniqueArray<Int32> all_nb_node(pm->commSize());
1507 pm->allGather(ArrayView{ 1, &sd_nb_node }, all_nb_node);
1508
1509 Int32 sum = 0;
1510 for (auto& elem : all_nb_node) {
1511 const Int32 old = elem;
1512 elem = sum;
1513 sum += old;
1514 }
1515
1516 // info() << "[" << pm->commRank() << "] Résultat de la collecte (rectifié) : " << all_nb_node;
1517
1518 for (auto& elem : new_nodes) {
1519 elem.m_new_node->m_uid_new_node += all_nb_node[elem.m_new_node->m_owner_new_node];
1520 }
1521 return sum;
1522}
1523
1524/*---------------------------------------------------------------------------*/
1525/*---------------------------------------------------------------------------*/
1526
1527void MeshCutService::
1528_fillFaceUID(Int32& sd_nb_face, UniqueArray<FaceLite>& new_faces)
1529{
1530 // Cet algo est repris de celui traitant les noeuds.
1531 IParallelMng* pm = subDomain()->parallelMng();
1532 Int32 my_proc = pm->commRank();
1533 UniqueArray<UniqueArray<Int32>> requested_faces(subDomain()->nbSubDomain());
1534
1535 {
1536 Int32 iter = -1;
1537 UniqueArray<UniqueArray<Int64>> request_uid(subDomain()->nbSubDomain());
1538
1539 debug() << "[Face][" << my_proc << "] Étape 1";
1540
1541 for (auto& elem : new_faces) {
1542 iter++;
1543 if (elem.m_uid_new_face >= 0) {
1544 continue;
1545 }
1546 if (elem.m_owner_new_face < 0) {
1547
1548 Node node00 = elem.m_node0->m_node0;
1549 Node node01 = elem.m_node0->m_node1;
1550 Node node10 = elem.m_node1->m_node0;
1551 Node node11 = elem.m_node1->m_node1;
1552
1553 Int64 min_uid = INT64_MAX;
1554 Int32 owner_min = -1;
1555
1556 // TODO AH : C'est quand même TURBO moche
1557 for (Cell cell00 : node00.cells()) {
1558 for (Cell cell01 : node01.cells()) {
1559 if (cell00 == cell01) {
1560 for (Cell cell10 : node10.cells()) {
1561 if (cell01 == cell10) {
1562 for (Cell cell11 : node11.cells()) {
1563 if (cell10 == cell11) {
1564 if (cell11.uniqueId() < min_uid) {
1565 min_uid = cell11.uniqueId();
1566 owner_min = cell11.owner();
1567 }
1568 }
1569 }
1570 }
1571 }
1572 }
1573 }
1574 }
1575
1576 if (owner_min == subDomain()->subDomainId()) {
1577 elem.m_owner_new_face = subDomain()->subDomainId();
1578 elem.m_uid_new_face = sd_nb_face++;
1579
1580 // debug() << "[Face][" << my_proc << " -> " << elem.m_owner_new_face << "] Définir UID"
1581 // << " -- UID0 : " << elem.m_node0->m_uid_new_node
1582 // << " -- UID1 : " << elem.m_node1->m_uid_new_node
1583 // << " -- Nouveau UID : " << elem.m_uid_new_face;
1584 }
1585 else {
1586 elem.m_owner_new_face = owner_min;
1587 elem.m_uid_new_face = -2;
1588 request_uid[elem.m_owner_new_face].add(elem.m_node0->m_uid_new_node);
1589 request_uid[elem.m_owner_new_face].add(elem.m_node1->m_uid_new_node);
1590
1591 requested_faces[elem.m_owner_new_face].add(iter);
1592
1593 // debug() << "[Face][" << my_proc << " -> " << elem.m_owner_new_face << "] Demande1"
1594 // << " -- UID0 : " << elem.m_node0->m_uid_new_node
1595 // << " -- UID1 : " << elem.m_node1->m_uid_new_node;
1596 }
1597 }
1598 else {
1599 request_uid[elem.m_owner_new_face].add(elem.m_node0->m_uid_new_node);
1600 request_uid[elem.m_owner_new_face].add(elem.m_node1->m_uid_new_node);
1601
1602 requested_faces[elem.m_owner_new_face].add(iter);
1603
1604 // debug() << "[Face][" << my_proc << " -> " << elem.m_owner_new_face << "] Demande2"
1605 // << " -- UID0 : " << elem.m_node0->m_uid_new_node
1606 // << " -- UID1 : " << elem.m_node1->m_uid_new_node;
1607 }
1608 }
1609
1610 {
1611 UniqueArray<Parallel::Request> requests(subDomain()->nbSubDomain() * 2);
1612
1613 // On envoie les requêtes.
1614 debug() << "[Face][" << my_proc << "] Étape 1.2";
1615
1616 for (Int32 sr = 0; sr < subDomain()->nbSubDomain(); ++sr) {
1617 if (sr == subDomain()->subDomainId()) {
1618 continue;
1619 }
1620 Int32 size = request_uid[sr].size();
1621 requests[sr * 2] = pm->send(ArrayView{ 1, &size }, sr, false);
1622 requests[sr * 2 + 1] = pm->send(request_uid[sr], sr, false);
1623 // debug() << "[Face][" << my_proc << " -> " << sr << "] Requêtes : " << request_uid[sr];
1624 }
1625
1626 pm->waitAllRequests(requests);
1627 }
1628 }
1629
1630 {
1631 UniqueArray<UniqueArray<Int64>> answers_uid(subDomain()->nbSubDomain());
1632 {
1633 UniqueArray<UnknownNodeOrFace> unknown_face;
1634 debug() << "[Face][" << my_proc << "] Étape 2";
1635
1636 for (Int32 sr = 0; sr < subDomain()->nbSubDomain(); ++sr) {
1637 if (sr == subDomain()->subDomainId()) {
1638 continue;
1639 }
1640 Int32 size = 0;
1641 pm->recv(ArrayView{ 1, &size }, sr);
1642
1643 UniqueArray<Int64> requested_uid(size);
1644 pm->recv(requested_uid, sr);
1645
1646 answers_uid[sr].add(0);
1647
1648 for (Int32 ipair_uid = 0; ipair_uid < requested_uid.size(); ipair_uid += 2) {
1649 std::optional<Int64> pos = _find(new_faces, requested_uid[ipair_uid], requested_uid[ipair_uid + 1]);
1650 if (pos) {
1651 answers_uid[sr].add(new_faces[pos.value()].m_uid_new_face);
1652 // debug() << "[Face][" << my_proc << " <- " << sr << "] Trouvé"
1653 // << " -- UID0 : " << requested_uid[ipair_uid]
1654 // << " -- UID1 : " << requested_uid[ipair_uid + 1]
1655 // << " -- Nouveau UID : " << new_faces[pos.value()].m_uid_new_face;
1656 }
1657
1658 // Il peut arriver que l'on nous demande une face que nous n'avons pas.
1659 // Par exemple, si un plan arrive pile sur une des arêtes de nos mailles.
1660 // Par contre, on sait qui en a besoin, donc qui possède une maille avec
1661 // la face en question.
1662 // Pour donner cette information, on va ajouter "-1" puis, à la fin de
1663 // la réponse, on va placer les processus en question.
1664 else {
1665 answers_uid[sr].add(-1);
1666 unknown_face.add({ requested_uid[ipair_uid], requested_uid[ipair_uid + 1], sr });
1667 // debug() << "[Face][" << my_proc << " <- " << sr << "] NON Trouvé"
1668 // << " -- UID0 : " << requested_uid[ipair_uid]
1669 // << " -- UID1 : " << requested_uid[ipair_uid + 1];
1670 }
1671 }
1672 answers_uid[sr][0] = answers_uid[sr].size() - 1;
1673 }
1674
1675 UniqueArray<Int64> who;
1676 debug() << "[Face][" << my_proc << "] Étape 2.2";
1677
1678 for (Int32 i = 0; i < unknown_face.size(); ++i) {
1679 if (unknown_face[i].null())
1680 continue;
1681 who.clear();
1682 who.add(unknown_face[i].m_who);
1683 unknown_face[i].m_who = -1;
1684 for (Int32 j = i + 1; j < unknown_face.size(); ++j) {
1685 if (unknown_face[j].null())
1686 continue;
1687 if (unknown_face[j] == unknown_face[i]) {
1688 who.add(unknown_face[j].m_who);
1689 unknown_face[j].m_who = -1;
1690 }
1691 }
1692 // debug() << "[Face] Infos additionnelles -- Node0UID : " << unknown_face[i].m_node0_uid << " -- Node1UID : " << unknown_face[i].m_node1_uid << " -- Qui : " << who;
1693 for (auto proc : who) {
1694 answers_uid[proc].add(unknown_face[i].m_node0_uid);
1695 answers_uid[proc].add(unknown_face[i].m_node1_uid);
1696 answers_uid[proc].add(who.size());
1697 answers_uid[proc].addRange(who);
1698 }
1699 }
1700 }
1701
1702 {
1703 UniqueArray<Parallel::Request> requests(subDomain()->nbSubDomain() * 2);
1704
1705 // On envoie les réponses.
1706 debug() << "[Face][" << my_proc << "] Étape 2.3";
1707 for (Int32 sr = 0; sr < subDomain()->nbSubDomain(); ++sr) {
1708 if (sr == subDomain()->subDomainId()) {
1709 continue;
1710 }
1711
1712 Int32 size = answers_uid[sr].size();
1713 requests[sr * 2] = pm->send(ArrayView{ 1, &size }, sr, false);
1714 requests[sr * 2 + 1] = pm->send(answers_uid[sr], sr, false);
1715 }
1716
1717 pm->waitAllRequests(requests);
1718 }
1719 }
1720
1721 UniqueArray<UniqueArray<Int64>> request_uid(subDomain()->nbSubDomain());
1722 UniqueArray<UniqueArray<Int32>> requested_faces2(subDomain()->nbSubDomain());
1723
1724 // On reçoit et traite les réponses.
1725 debug() << "[Face][" << my_proc << "] Étape 3";
1726
1727 bool need_more_comm = false;
1728
1729 for (Int32 sr = 0; sr < subDomain()->nbSubDomain(); ++sr) {
1730 if (sr == subDomain()->subDomainId()) {
1731 continue;
1732 }
1733
1734 Int32 total_size = 0;
1735 pm->recv(ArrayView{ 1, &total_size }, sr);
1736
1737 UniqueArray<Int64> answered_uid(total_size);
1738 pm->recv(answered_uid, sr);
1739
1740 if (total_size == 1) {
1741 continue;
1742 }
1743
1744 Int32 answer_to_request_size = static_cast<Int32>(answered_uid[0]);
1745
1746 Span<Int64> answer_to_request = answered_uid.subView(1, answer_to_request_size);
1747 Span<Int64> additionnal_answer = answered_uid.subView(answer_to_request_size + 1, answered_uid.size() - (answer_to_request_size + 1));
1748
1749 // debug() << "[Face][" << my_proc << " <- " << sr << "] Découpe"
1750 // << " -- answer_to_request : " << answer_to_request
1751 // << " -- additionnal_answer : " << additionnal_answer;
1752
1753 for (Int32 answer = 0; answer < answer_to_request.size(); ++answer) {
1754 FaceLite& face_lite = new_faces[requested_faces[sr][answer]];
1755
1756 if (answer_to_request[answer] != -1) {
1757 face_lite.m_uid_new_face = answer_to_request[answer];
1758 // debug() << "[Face][" << my_proc << "] Appliquer"
1759 // << " -- UID0 : " << face_lite.m_node0->m_uid_new_node
1760 // << " -- UID1 : " << face_lite.m_node1->m_uid_new_node
1761 // << " -- Nouveau UID : " << face_lite.m_uid_new_face;
1762 }
1763 else {
1764 Int64 pos = 0;
1765 while (pos < additionnal_answer.size()) {
1766 Int64& uid0 = additionnal_answer[pos++];
1767 Int64 uid1 = additionnal_answer[pos++];
1768 Int64 decal = additionnal_answer[pos++];
1769 if (uid0 == face_lite.m_node0->m_uid_new_node && uid1 == face_lite.m_node1->m_uid_new_node) {
1770 uid0 = answer;
1771 break;
1772 }
1773 pos += decal;
1774 }
1775 if (pos >= additionnal_answer.size()) {
1776 ARCANE_FATAL("Unknown face -- UID0 : {0} -- UID1 : {1}", face_lite.m_node0->m_uid_new_node, face_lite.m_node1->m_uid_new_node);
1777 }
1778 }
1779 }
1780
1781 Int64 pos = 0;
1782 Span<Int64> sub_additionnal_answer;
1783
1784 while (pos < additionnal_answer.size()) {
1785 Int64 pos_node_in_array = additionnal_answer[pos++];
1786 pos++;
1787 Int64 decal = additionnal_answer[pos++];
1788
1789 FaceLite& face_lite = new_faces[requested_faces[sr][pos_node_in_array]];
1790 sub_additionnal_answer = additionnal_answer.subSpan(pos, decal);
1791
1792 pos += decal;
1793
1794
1795 // debug() << "[Face][" << my_proc << " <- " << sr << "] Sous"
1796 // << " -- sub_additionnal_answer : " << sub_additionnal_answer;
1797
1798 Node node00 = face_lite.m_node0->m_node0;
1799 Node node01 = face_lite.m_node0->m_node1;
1800 Node node10 = face_lite.m_node1->m_node0;
1801 Node node11 = face_lite.m_node1->m_node1;
1802
1803 Int64 min_uid = INT64_MAX;
1804 Int32 owner_min = -1;
1805
1806 // TODO AH : C'est quand même TURBO moche
1807 for (Cell cell00 : node00.cells()) {
1808 for (Cell cell01 : node01.cells()) {
1809 if (cell00 == cell01) {
1810 for (Cell cell10 : node10.cells()) {
1811 if (cell01 == cell10) {
1812 for (Cell cell11 : node11.cells()) {
1813 if (cell10 == cell11) {
1814 if (cell11.uniqueId() < min_uid && sub_additionnal_answer.contains(cell11.owner())) {
1815 min_uid = cell11.uniqueId();
1816 owner_min = cell11.owner();
1817 }
1818 }
1819 }
1820 }
1821 }
1822 }
1823 }
1824 }
1825
1826 // S'il l'on est le proprio, on peut définir le uid du noeud.
1827 if (owner_min == subDomain()->subDomainId()) {
1828 face_lite.m_owner_new_face = subDomain()->subDomainId();
1829 face_lite.m_uid_new_face = sd_nb_face++;
1830
1831 // On enregistre le UID à envoyer.
1832 if (sub_additionnal_answer.size() > 1) {
1833 need_more_comm = true;
1834
1835 // debug() << "[Face][" << my_proc << "] Envoyer"
1836 // << " -- UID : " << face_lite.m_uid_new_face
1837 // << " -- pour UID Nœud0 : " << face_lite.m_node0->m_uid_new_node
1838 // << " -- UID1 : " << face_lite.m_node1->m_uid_new_node
1839 // << " -- à : " << sub_additionnal_answer;
1840
1841 for (auto proc : sub_additionnal_answer) {
1842 if (proc == my_proc)
1843 continue;
1844 request_uid[proc].add(face_lite.m_uid_new_face);
1845 }
1846 }
1847 }
1848
1849 // Sinon, on doit aller demander le uid au proprio.
1850 else if (owner_min >= 0) {
1851 need_more_comm = true;
1852 face_lite.m_owner_new_face = owner_min;
1853 face_lite.m_uid_new_face = -2;
1854
1855 if (request_uid[my_proc].empty()) {
1856 request_uid[my_proc].resize(subDomain()->nbSubDomain(), 0);
1857 }
1858
1859 request_uid[my_proc][owner_min]++;
1860
1861 requested_faces2[owner_min].add(requested_faces[sr][pos_node_in_array]);
1862
1863 // debug() << "[Face][" << my_proc << " -> " << face_lite.m_owner_new_face << "] Recevoir UID pour Nœud"
1864 // << " -- UID0 : " << face_lite.m_node0->m_uid_new_node
1865 // << " -- UID1 : " << face_lite.m_node1->m_uid_new_node;
1866 }
1867 else {
1868 ARCANE_FATAL("Face owner not found");
1869 }
1870 }
1871 }
1872
1873 if (need_more_comm) {
1874 {
1875 UniqueArray<Parallel::Request> requests(subDomain()->nbSubDomain());
1876
1877 // On envoie les UID complémentaires.
1878 debug() << "[Face][" << my_proc << "] Étape 3.2";
1879
1880 for (Int32 sr = 0; sr < subDomain()->nbSubDomain(); ++sr) {
1881 if (sr == subDomain()->subDomainId()) {
1882 // debug() << "[Face][" << my_proc << " -> " << sr << "] Mes RequêtesC : " << request_uid[sr];
1883 continue;
1884 }
1885 if (request_uid[sr].empty()) continue;
1886 requests[sr] = pm->send(request_uid[sr], sr, false);
1887 // debug() << "[Face][" << my_proc << " -> " << sr << "] RequêtesC : " << request_uid[sr];
1888 }
1889
1890 pm->waitAllRequests(requests);
1891 }
1892
1893 // On reçoit et traite les UID complémentaires.
1894 debug() << "[Face][" << my_proc << "] Étape 7";
1895
1896 if (!request_uid[my_proc].empty()) {
1897 for (Int32 sr = 0; sr < subDomain()->nbSubDomain(); ++sr) {
1898 if (sr == subDomain()->subDomainId()) {
1899 continue;
1900 }
1901
1902 Int64 size = request_uid[my_proc][sr];
1903 if (size == 0) continue;
1904
1905 // debug() << "[Face][" << my_proc << " -> " << sr << "] Taille reçue : " << size;
1906
1907
1908 UniqueArray<Int64> requested_uid(size);
1909 pm->recv(requested_uid, sr);
1910
1911 for (Int32 i = 0; i < size; ++i) {
1912 FaceLite& face_lite = new_faces[requested_faces2[sr][i]];
1913 face_lite.m_uid_new_face = requested_uid[i];
1914 // debug() << "[Face][" << my_proc << "] Appliquer2"
1915 // << " -- UID0 : " << face_lite.m_node0->m_uid_new_node
1916 // << " -- UID1 : " << face_lite.m_node1->m_uid_new_node
1917 // << " -- Nouveau UID : " << face_lite.m_uid_new_face;
1918 }
1919 }
1920 }
1921 }
1922}
1923
1924/*---------------------------------------------------------------------------*/
1925/*---------------------------------------------------------------------------*/
1926
1927Int32 MeshCutService::
1928_makeUniqueFaceUID(Int32 sd_nb_face, UniqueArray<FaceLite>& new_faces)
1929{
1930 IParallelMng* pm = subDomain()->parallelMng();
1931
1932 UniqueArray<Int32> all_nb_node(pm->commSize());
1933 pm->allGather(ArrayView{ 1, &sd_nb_face }, all_nb_node);
1934
1935 Int32 sum = 0;
1936 for (auto& elem : all_nb_node) {
1937 const Int32 old = elem;
1938 elem = sum;
1939 sum += old;
1940 }
1941
1942 // info() << "[" << pm->commRank() << "] Résultat de la collecte (rectifié) : " << all_nb_node;
1943
1944 for (auto& elem : new_faces) {
1945 elem.m_uid_new_face += all_nb_node[elem.m_owner_new_face];
1946 }
1947 return sum;
1948}
1949
1950/*---------------------------------------------------------------------------*/
1951/*---------------------------------------------------------------------------*/
1952
1953void MeshCutService::
1954_compute()
1955{
1956 Int32 nb_cell = 0;
1957 Int32 g_nb_node = 0;
1958 Int32 g_nb_face = 0;
1959
1960 UniqueArray<Cell> ori_cells;
1961
1962 UniqueArray<Int64> new_cells;
1963
1964 UniqueArray<NodeIntersection> new_nodes;
1965 UniqueArray<FaceLite> new_faces;
1966
1967 for (Int32 i = 0; i < m_plans.size(); ++i) {
1968
1969 debug() << "Plan : " << m_plans[i].first << ", " << m_plans[i].second;
1970
1971 UniqueArray<NodeIntersection> plan_new_nodes;
1972 UniqueArray<FaceLite> plan_new_faces;
1973
1974 Int32 ajust = new_nodes.size();
1975
1976 Int32 previous_g_nb_node = g_nb_node;
1977 Int32 previous_g_nb_face = g_nb_face;
1978
1979 Int32 nb_node_for_this_plan = g_nb_node;
1980 Int32 nb_face_for_this_plan = g_nb_face;
1981 {
1982 debug() << "[" << subDomain()->parallelMng()->commRank() << "] _createNodesAndCells";
1983 _createNodesAndCells(i, g_nb_node, plan_new_nodes, ajust, nb_cell, new_cells, g_nb_face, plan_new_faces, ori_cells);
1984
1985 // for (auto& elem : plan_new_nodes) {
1986 // debug() << "New node"
1987 // << " -- UID : " << elem.m_new_node->m_uid_new_node
1988 // << " -- Owner : " << elem.m_new_node->m_owner_new_node
1989 // << " -- Pos : " << elem.m_intersection_pos
1990 // << " -- Edge node0 : " << elem.m_new_node->m_uid_node0
1991 // << " -- Edge node1 : " << elem.m_new_node->m_uid_node1;
1992 // }
1993 }
1994
1995 {
1996 debug() << "[" << subDomain()->parallelMng()->commRank() << "] _fillNodeUID";
1997 _fillNodeUID(g_nb_node, plan_new_nodes);
1998 nb_node_for_this_plan = g_nb_node - nb_node_for_this_plan;
1999
2000 nb_node_for_this_plan = _makeUniqueNodeUID(nb_node_for_this_plan, plan_new_nodes);
2001 g_nb_node = previous_g_nb_node + nb_node_for_this_plan;
2002
2003 // for (auto& elem : plan_new_nodes) {
2004 // debug() << "Fix node"
2005 // << " -- UID : " << elem.m_new_node->m_uid_new_node
2006 // << " -- Owner : " << elem.m_new_node->m_owner_new_node
2007 // << " -- Pos : " << elem.m_intersection_pos
2008 // << " -- Edge node0 : " << elem.m_new_node->m_uid_node0
2009 // << " -- Edge node1 : " << elem.m_new_node->m_uid_node1;
2010 // }
2011 //
2012 // for (auto& elem : plan_new_faces) {
2013 // debug() << "New face"
2014 // << " -- UID : " << elem.m_uid_new_face
2015 // << " -- Owner : " << elem.m_owner_new_face
2016 // << " -- Node0 : " << elem.m_node0->m_uid_new_node
2017 // << " -- Node00 : " << elem.m_node0->m_uid_node0
2018 // << " -- Node01 : " << elem.m_node0->m_uid_node1
2019 // << " -- Node1 : " << elem.m_node1->m_uid_new_node
2020 // << " -- Node10 : " << elem.m_node1->m_uid_node0
2021 // << " -- Node11 : " << elem.m_node1->m_uid_node1;
2022 // }
2023 }
2024
2025 {
2026 _fillFaceUID(g_nb_face, plan_new_faces);
2027 nb_face_for_this_plan = g_nb_face - nb_face_for_this_plan;
2028
2029 nb_face_for_this_plan = _makeUniqueFaceUID(nb_face_for_this_plan, plan_new_faces);
2030 g_nb_face = previous_g_nb_face + nb_face_for_this_plan;
2031
2032 // for (auto& elem : plan_new_faces) {
2033 // debug() << "Fix face"
2034 // << " -- UID : " << elem.m_uid_new_face
2035 // << " -- Owner : " << elem.m_owner_new_face
2036 // << " -- Node0 : " << elem.m_node0->m_uid_new_node
2037 // << " -- Node00 : " << elem.m_node0->m_uid_node0
2038 // << " -- Node01 : " << elem.m_node0->m_uid_node1
2039 // << " -- Node1 : " << elem.m_node1->m_uid_new_node
2040 // << " -- Node10 : " << elem.m_node1->m_uid_node0
2041 // << " -- Node11 : " << elem.m_node1->m_uid_node1;
2042 // }
2043 }
2044
2045 new_nodes.addRange(plan_new_nodes);
2046 new_faces.addRange(plan_new_faces);
2047 }
2048
2049 _makeUniqueCellUID(nb_cell, new_cells, new_nodes);
2050
2051 {
2052 // Int32 pos0 = 0;
2053 // while (pos0 < new_cells.size()) {
2054 // StringBuilder logs;
2055 // logs += "Nouvelle maille -- Type : ";
2056 // Int64 type = new_cells[pos0++];
2057 // logs += type;
2058 // logs += " -- UID : ";
2059 // logs += new_cells[pos0++];
2060 //
2061 // for (Int32 i = 0; i < type; ++i) {
2062 // logs += " -- Nœud";
2063 // logs += i;
2064 // logs += " : ";
2065 // logs += new_cells[pos0++];
2066 // }
2067 // debug() << logs;
2068 // }
2069 }
2070
2071 _addFaces(new_faces);
2072 _addCells(new_cells);
2073 m_cloned_mesh->modifier()->endUpdate();
2074 _setCoordNodesAndOwner(new_nodes);
2075 _setFacesOwner(new_faces);
2076
2077 _updateVariables(ori_cells);
2078
2079 m_cloned_mesh->nodeFamily()->notifyItemsOwnerChanged();
2080 m_cloned_mesh->faceFamily()->notifyItemsOwnerChanged();
2081
2082 info() << "Nouveau maillage : " << m_cloned_mesh->name() << " -- NbNœuds : " << m_cloned_mesh->nbNode() << " -- NbMailles : " << m_cloned_mesh->nbCell();
2083}
2084
2085/*---------------------------------------------------------------------------*/
2086/*---------------------------------------------------------------------------*/
2087
2088std::optional<Int64> MeshCutService::
2089_find(Span<FaceLite> new_faces, Int64 node_uid0, Int64 node_uid1)
2090{
2091 for (Int32 i = 0; i < new_faces.size(); ++i) {
2092 if (new_faces[i].m_node0->m_uid_new_node == node_uid0 && new_faces[i].m_node1->m_uid_new_node == node_uid1) {
2093 return i;
2094 }
2095 }
2096 return std::nullopt;
2097}
2098
2099/*---------------------------------------------------------------------------*/
2100/*---------------------------------------------------------------------------*/
2101
2102void MeshCutService::
2103_addFaces(UniqueArray<FaceLite>& new_faces) const
2104{
2105 UniqueArray<Int64> faces_infos;
2106 faces_infos.reserve(new_faces.size() * 4);
2107
2108 Int32 nb_faces = 0;
2109
2110 for (auto& elem : new_faces) {
2111 faces_infos.add(ITI_Line2);
2112 faces_infos.add(elem.m_uid_new_face);
2113 faces_infos.add(elem.m_node0->m_uid_new_node);
2114 faces_infos.add(elem.m_node1->m_uid_new_node);
2115 nb_faces++;
2116 }
2117
2118 m_cloned_mesh->modifier()->addFaces(nb_faces, faces_infos);
2119}
2120
2121/*---------------------------------------------------------------------------*/
2122/*---------------------------------------------------------------------------*/
2123
2124void MeshCutService::
2125_addCells(UniqueArray<Int64>& new_cells)
2126{
2127 Int32 nb_cells = 0;
2128
2129 Int32 pos0 = 0;
2130 while (pos0 < new_cells.size()) {
2131 const Int64 type = new_cells[pos0];
2132
2133 if (type == 3)
2134 new_cells[pos0++] = ITI_Triangle3;
2135 else if (type == 4)
2136 new_cells[pos0++] = ITI_Quad4;
2137 else if (type == 5)
2138 new_cells[pos0++] = ITI_Pentagon5;
2139 else if (type == 6)
2140 new_cells[pos0++] = ITI_Hexagon6;
2141 else
2142 ARCANE_FATAL("Pas implem : {0}", type);
2143
2144 pos0 += 1 + static_cast<Int32>(type); // (1)=UID + (type)=UIDNœuds
2145
2146 nb_cells++;
2147 }
2148
2149 m_cloned_mesh->modifier()->addCells(nb_cells, new_cells);
2150}
2151
2152/*---------------------------------------------------------------------------*/
2153/*---------------------------------------------------------------------------*/
2154
2155void MeshCutService::
2156_setCoordNodesAndOwner(UniqueArray<NodeIntersection>& new_nodes)
2157{
2158 VariableNodeReal3& node_coords(m_cloned_mesh->nodesCoordinates());
2159 ENUMERATE_ (Node, inode, m_cloned_mesh->allNodes()) {
2160 const Int64 uid = inode->uniqueId();
2161 for (const auto& elem : new_nodes) {
2162 if (elem.m_new_node->m_uid_new_node == uid) {
2163 node_coords[inode] = elem.m_intersection_pos;
2164 inode->mutableItemBase().setOwner(elem.m_new_node->m_owner_new_node, subDomain()->subDomainId());
2165 // debug() << "UID Nœud : " << uid << " -- Coordonnée : " << node_coords[inode];
2166 }
2167 }
2168 }
2169}
2170
2171/*---------------------------------------------------------------------------*/
2172/*---------------------------------------------------------------------------*/
2173
2174void MeshCutService::
2175_setFacesOwner(UniqueArray<FaceLite>& new_faces)
2176{
2177 ENUMERATE_ (Face, iface, m_cloned_mesh->allFaces()) {
2178 const Int64 uid = iface->uniqueId();
2179 for (const auto& elem : new_faces) {
2180 if (elem.m_uid_new_face == uid) {
2181 iface->mutableItemBase().setOwner(elem.m_owner_new_face, subDomain()->subDomainId());
2182 }
2183 }
2184 }
2185}
2186
2187/*---------------------------------------------------------------------------*/
2188
2189void MeshCutService::
2190_updateVariables(UniqueArray<Cell>& ori_cells)
2191{
2192 for (Int32 type = 0; type < NB_ARCANE_DATA_TYPE; ++type) {
2193 switch (type) {
2194 case DT_Byte: {
2195 _updateVariablesT(ori_cells, type, Byte());
2196 } break;
2197 case DT_Real: {
2198 _updateVariablesT(ori_cells, type, Real());
2199 } break;
2200 case DT_Real2: {
2201 _updateVariablesT(ori_cells, type, Real2());
2202 } break;
2203 case DT_Real2x2: {
2204 _updateVariablesT(ori_cells, type, Real2x2());
2205 } break;
2206 case DT_Real3: {
2207 _updateVariablesT(ori_cells, type, Real3());
2208 } break;
2209 case DT_Real3x3: {
2210 _updateVariablesT(ori_cells, type, Real3x3());
2211 } break;
2212 case DT_Int8: {
2213 _updateVariablesT(ori_cells, type, Int8());
2214 } break;
2215 case DT_Int16: {
2216 _updateVariablesT(ori_cells, type, Int16());
2217 } break;
2218 case DT_Int32: {
2219 _updateVariablesT(ori_cells, type, Int32());
2220 } break;
2221 case DT_Int64: {
2222 _updateVariablesT(ori_cells, type, Int64());
2223 } break;
2224 case DT_Float32: {
2225 _updateVariablesT(ori_cells, type, Float32());
2226 } break;
2227 case DT_Float16: {
2228 _updateVariablesT(ori_cells, type, Float16());
2229 } break;
2230 case DT_BFloat16: {
2231 _updateVariablesT(ori_cells, type, BFloat16());
2232 } break;
2233 default:
2234 break;
2235 }
2236 }
2237}
2238
2239/*---------------------------------------------------------------------------*/
2240/*---------------------------------------------------------------------------*/
2241
2242template <class T>
2243void MeshCutService::
2244_updateVariablesT(UniqueArray<Cell>& ori_cells, Int32 type, T)
2245{
2246 VariableOriClone<T> voc;
2247
2248 if (m_variables_cloned.count() != m_variables_ori.count()) {
2249 ARCANE_FATAL("Bad size -- m_variables_cloned : {0} -- m_variables_ori : {1}", m_variables_cloned.count(), m_variables_ori.count());
2250 }
2251
2252 VariableCollection::Enumerator iclone(m_variables_cloned);
2253 VariableCollection::Enumerator iori(m_variables_ori);
2254
2255 while (++iclone && ++iori) {
2256 IVariable* ori = *iori;
2257 if (ori->dataType() == type) {
2258 IVariable* clone = *iclone;
2259 if (ori->dimension() == 1) {
2260 auto* ori_data = ARCANE_CHECK_POINTER(dynamic_cast<IArrayDataT<T>*>(ori->data()));
2261 auto* clo_data = ARCANE_CHECK_POINTER(dynamic_cast<IArrayDataT<T>*>(clone->data()));
2262 if (ori->itemKind() == IK_Unknown) {
2263 voc.ori.unknown.dim1.add(ori_data->view());
2264 voc.clone.unknown.dim1.add(clo_data->view());
2265 }
2266 else if (ori->itemKind() == IK_Cell) {
2267 voc.ori.cells.dim1.add(ori_data->view());
2268 voc.clone.cells.dim1.add(clo_data->view());
2269 }
2270 else {
2271 ARCANE_FATAL("Variable type not supported -- Type : {0}", ori->itemKind());
2272 }
2273 }
2274 else if (ori->dimension() == 2) {
2275 if (ori->itemKind() == IK_Cell) {
2276 VariableResizeArgs vra(-1);
2277 vra.setNewSizeDim2(ori->nbElement() / mesh()->nbCell());
2278 clone->_internalApi()->resize(vra);
2279 auto* ori_data = ARCANE_CHECK_POINTER(dynamic_cast<IArray2DataT<T>*>(ori->data()));
2280 auto* clo_data = ARCANE_CHECK_POINTER(dynamic_cast<IArray2DataT<T>*>(clone->data()));
2281 voc.ori.cells.dim2.add(ori_data->view());
2282 voc.clone.cells.dim2.add(clo_data->view());
2283 }
2284 }
2285 else {
2286 ARCANE_FATAL("Variable dim not supported -- Dim : {0}", ori->dimension());
2287 }
2288 }
2289 }
2290
2291 _updateArrayVariable(ori_cells, T(), voc);
2292}
2293
2294/*---------------------------------------------------------------------------*/
2295/*---------------------------------------------------------------------------*/
2296
2297template <class T>
2298void MeshCutService::
2299_updateArrayVariable(UniqueArray<Cell>& ori_cells, T, VariableOriClone<T>& voc)
2300{
2301 if (voc.ori.isUnknownUsed()) {
2302 for (Int32 i = 0; i < voc.ori.unknown.dim1.size(); ++i) {
2303 voc.clone.unknown.dim1[i].copy(voc.ori.unknown.dim1[i]);
2304 }
2305 for (Int32 i = 0; i < voc.ori.unknown.dim2.size(); ++i) {
2306 for (Int32 j = 0; j < voc.ori.unknown.dim2[i].dim2Size(); ++j) {
2307 voc.clone.unknown.dim2[i][j].copy(voc.ori.unknown.dim2[i][j]);
2308 }
2309 }
2310 }
2311
2312 ENUMERATE_ (Cell, icell, m_cloned_mesh->ownCells()) {
2313 Cell ori_cell = ori_cells[icell.localId()];
2314 if (voc.ori.isCellsUsed()) {
2315 for (Int32 i = 0; i < voc.ori.cells.dim1.size(); ++i) {
2316 voc.clone.cells.dim1[i][icell.localId()] = voc.ori.cells.dim1[i][ori_cell.localId()];
2317 }
2318 for (Int32 i = 0; i < voc.ori.cells.dim2.size(); ++i) {
2319 voc.clone.cells.dim2[i][icell.localId()].copy(voc.ori.cells.dim2[i][ori_cell.localId()]);
2320 }
2321 }
2322 }
2323}
2324
2325/*---------------------------------------------------------------------------*/
2326/*---------------------------------------------------------------------------*/
2327
2328} // namespace Arcane
2329
2330/*---------------------------------------------------------------------------*/
2331/*---------------------------------------------------------------------------*/
#define ARCANE_CHECK_POINTER(ptr)
Macro retournant le pointeur ptr s'il est non nul ou lancant une exception s'il est nul.
#define ARCANE_FATAL(...)
Macro envoyant une exception FatalErrorException.
#define ENUMERATE_(type, name, group)
Enumérateur générique d'un groupe d'entité
CaseOptionsMeshCut * options() const
Options du jeu de données du service.
ArcaneMeshCutObject(const Arcane::ServiceBuildInfo &sbi)
Constructeur.
virtual void clearItems()=0
Supprime toutes les entitées de toutes les familles de ce maillage.
virtual IMeshModifier * modifier()=0
Interface de modification associée.
CellGroup ownCells() const
Retourne le groupe contenant toutes les mailles propres à ce domaine.
NodeGroup allNodes() const
Retourne le groupe contenant tous les noeuds.
void addPlane(const Real3 &p0, const Real3 &normal) override
Méthode permettant d'ajouter un plan au service de coupe. L'utilisation de ces plans dépend du servic...
void setServiceMeshUniqueId(Int32 unique_id) override
Méthode permettant de définir un identifiant unique pour créer plusieurs services de section pour un ...
void setVariables(VariableCollection variables) override
Méthode permettant d'ajouter un ensemble de variables à copier sur le nouveau maillage.
VariableCollection variables() override
Méthode permettant d'obtenir un ensemble de variables copiées sur le nouveau maillage.
void updateSection() override
Méthode permettant de mettre à jour la section du maillage avec tous les plans.
MeshHandle meshSection() override
Méthode permettant d'obtenir la section du maillage.
Handle sur un maillage.
Definition MeshHandle.h:48
Classe gérant un vecteur réel de 3 dimensions.
eServiceType creationType() const
Type du service pouvant être créé par cette instance.
Structure contenant les informations pour créer un service.
Vue d'un tableau d'éléments de type T.
Definition Span.h:633
Vecteur 1D de données avec sémantique par valeur (style STL).
Collection de variables.
__host__ __device__ Real dot(Real2 u, Real2 v)
Produit scalaire de u par v dans .
Definition MathUtils.h:94
Real3 normalizedCrossProduct3(Real3 v1, Real3 v2)
Produit vectoriel normalisé.
Definition MathUtils.h:792
__host__ __device__ Real3 normalizeReal3(Real3 v)
Normalisation d'un Real3.
Definition MathUtils.h:777
MeshVariableScalarRefT< Node, Real3 > VariableNodeReal3
Grandeur au noeud de type coordonnées.
MeshVariableScalarRefT< Node, Real > VariableNodeReal
Grandeur au noeud de type réel.
constexpr __host__ __device__ bool isNearlyZeroWithEpsilon(const Type &a, const Type &epsilon)
Teste si une valeur est approximativement égale à zéro dans un epsilon.
Definition TypeEqual.h:292
-- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature --
std::int8_t Int8
Type entier signé sur 8 bits.
std::int64_t Int64
Type entier signé sur 64 bits.
Int32 Integer
Type représentant un entier.
bool operator<(const Item &item1, const Item &item2)
Compare deux entités.
Definition Item.h:551
eServiceType
Type du service.
@ ST_CaseOption
Le service s'utilise au niveau du jeu de données.
double Real
Type représentant un réel.
unsigned char Byte
Type d'un octet.
Definition BaseTypes.h:42
auto makeRef(InstanceType *t) -> Ref< InstanceType >
Créé une référence sur un pointeur.
float Float32
Type flottant IEEE-753 simple précision.
@ DT_Float32
Donnée de type 'Float32'.
Definition DataTypes.h:52
@ DT_Real2x2
Donnée de type tenseur 3x3.
Definition DataTypes.h:48
@ DT_Int16
Donnée de type entier 16 bits.
Definition DataTypes.h:42
@ DT_Int8
Donnée de type entier sur 8 bits.
Definition DataTypes.h:53
@ DT_Real3x3
Donnée de type tenseur 3x3.
Definition DataTypes.h:49
@ DT_Int32
Donnée de type entier 32 bits.
Definition DataTypes.h:43
@ DT_Real3
Donnée de type vecteur 3.
Definition DataTypes.h:47
@ DT_Float16
Donnée de type 'Float16'.
Definition DataTypes.h:51
@ DT_Int64
Donnée de type entier 64 bits.
Definition DataTypes.h:44
@ DT_BFloat16
Donnée de type 'BFloat16'.
Definition DataTypes.h:50
@ DT_Real2
Donnée de type vecteur 2.
Definition DataTypes.h:46
@ DT_Real
Donnée de type réel.
Definition DataTypes.h:41
@ DT_Byte
Donnée de type octet.
Definition DataTypes.h:40
@ Cell
Le maillage est AMR par maille.
Definition MeshKind.h:52
std::int32_t Int32
Type entier signé sur 32 bits.
Arcane::BFloat16 BFloat16
Type 'Brain Float16'.
Arcane::Float16 Float16
Type 'Float16' (binary16).