Arcane  4.2.3.0
Documentation développeur
Chargement...
Recherche...
Aucune correspondance
UnstructuredMeshUtilities.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/* UnstructuredMeshUtilities.cc (C) 2000-2025 */
9/* */
10/* Fonctions utilitaires sur un maillage. */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arcane/utils/InvalidArgumentException.h"
15#include "arcane/utils/FatalErrorException.h"
16#include "arcane/utils/ParallelFatalErrorException.h"
17#include "arcane/utils/ArgumentException.h"
18#include "arcane/utils/Array.h"
19#include "arcane/utils/Real3.h"
20#include "arcane/utils/HashTableMap.h"
21#include "arcane/utils/OStringStream.h"
22#include "arcane/utils/ScopedPtr.h"
23#include "arcane/utils/StringBuilder.h"
24#include "arcane/utils/CheckedConvert.h"
25#include "arcane/utils/PlatformUtils.h"
26#include "arcane/utils/SmallArray.h"
27#include "arcane/utils/NotImplementedException.h"
28
29#include "arcane/core/IMesh.h"
30#include "arcane/core/IMeshWriter.h"
31#include "arcane/core/IParallelMng.h"
33#include "arcane/core/IItemFamily.h"
34#include "arcane/core/ItemPrinter.h"
35#include "arcane/core/VariableTypes.h"
36#include "arcane/core/ItemPairGroup.h"
37#include "arcane/core/ISubDomain.h"
38#include "arcane/core/ServiceBuilder.h"
39#include "arcane/core/IParallelReplication.h"
40#include "arcane/core/Timer.h"
41#include "arcane/core/IMeshPartitioner.h"
42#include "arcane/core/IPrimaryMesh.h"
43#include "arcane/core/IMeshChecker.h"
44#include "arcane/core/IItemFamilyNetwork.h"
45#include "arcane/core/NodesOfItemReorderer.h"
46
47#include "arcane/mesh/UnstructuredMeshUtilities.h"
48#include "arcane/ConnectivityItemVector.h"
49#include "arcane/mesh/NewItemOwnerBuilder.h"
50#include "arcane/mesh/ParticleFamily.h"
51#include "arcane/mesh/GraphDoFs.h"
52#include "arcane/mesh/BasicItemPairGroupComputeFunctor.h"
53#include "arcane/mesh/MeshNodeMerger.h"
54#include "arcane/mesh/ConnectivityNewWithDependenciesTypes.h"
55#include "arcane/mesh/ItemsOwnerBuilder.h"
56
57#include <algorithm>
58
59/*---------------------------------------------------------------------------*/
60/*---------------------------------------------------------------------------*/
61
62namespace Arcane
63{
64
65/*---------------------------------------------------------------------------*/
66/*---------------------------------------------------------------------------*/
67
68UnstructuredMeshUtilities::
69UnstructuredMeshUtilities(IMesh* mesh)
70: TraceAccessor(mesh->traceMng())
71, m_mesh(mesh)
72, m_compute_adjacency_functor(new BasicItemPairGroupComputeFunctor(traceMng()))
73{
74}
75
76/*---------------------------------------------------------------------------*/
77/*---------------------------------------------------------------------------*/
78
79UnstructuredMeshUtilities::
80~UnstructuredMeshUtilities()
81{
82 delete m_compute_adjacency_functor;
83}
84
85/*---------------------------------------------------------------------------*/
86/*---------------------------------------------------------------------------*/
87
88void UnstructuredMeshUtilities::
89changeOwnersFromCells()
90{
91 // On suppose qu'on connait les nouveaux propriétaires des mailles, qui
92 // se trouvent dans cells_owner. Il faut
93 // maintenant déterminer les nouveaux propriétaires des noeuds et
94 // des faces. En attendant d'avoir un algorithme qui équilibre mieux
95 // les messages, on applique le suivant:
96 // - chaque sous-domaine est responsable pour déterminer le nouveau
97 // propriétaire des noeuds et des faces qui lui appartiennent.
98 // - pour les noeuds, le nouveau propriétaire est le nouveau propriétaire
99 // de la maille connectée à ce noeud dont le uniqueId() est le plus petit.
100 // - pour les faces, le nouveau propriétaire est le nouveau propriétaire
101 // de la maille qui est derrière cette face s'il s'agit d'une face
102 // interne et de la maille connectée s'il s'agit d'une face frontière.
103 // - pour les noeuds duaux, le nouveau propriétaire est le nouveau propriétaire
104 // de la maille connectée à l'élément dual
105 // - pour les liaisons, le nouveau propriétaire est le nouveau propriétaire
106 // de la maille connectée au premier noeud dual, c'est-à-dire le propriétaire
107 // du premier noeud dual de la liaison
108
109 // Outil d'affectation des owners pour les items
110 mesh::NewItemOwnerBuilder owner_builder;
111
112 VariableItemInt32& nodes_owner(m_mesh->nodeFamily()->itemsNewOwner());
113 VariableItemInt32& edges_owner(m_mesh->edgeFamily()->itemsNewOwner());
114 VariableItemInt32& faces_owner(m_mesh->faceFamily()->itemsNewOwner());
115 VariableItemInt32& cells_owner(m_mesh->cellFamily()->itemsNewOwner());
116
117 // Détermine les nouveaux propriétaires des noeuds
118 {
119 ENUMERATE_NODE(i_node,m_mesh->ownNodes()){
120 const Node node = *i_node;
121 const Cell cell = owner_builder.connectedCellOfItem(node);
122#ifdef ARCANE_DEBUG_LOAD_BALANCING
123 if (nodes_owner[node]!= cells_owner[cell]){
124 info() << "New owner for node: " << ItemPrinter(node) << " cell=" << ItemPrinter(cell)
125 << " old_owner=" << nodes_owner[node]
126 << " current_cell_owner=" << cell.owner()
127 << " new_owner=" << cells_owner[cell];
128 }
129#endif /* ARCANE_DEBUG_LOAD_BALANCING */
130 nodes_owner[node] = cells_owner[cell];
131 }
132 nodes_owner.synchronize();
133 }
134
135 // Détermine les nouveaux propriétaires des arêtes
136 {
137 ENUMERATE_EDGE(i_edge,m_mesh->ownEdges()){
138 const Edge edge = *i_edge;
139 const Cell cell = owner_builder.connectedCellOfItem(edge);
140#ifdef ARCANE_DEBUG_LOAD_BALANCING
141 if (edges_owner[edge] != cells_owner[cell]) {
142 info() << "New owner for edge: " << ItemPrinter(edge) << " cell=" << ItemPrinter(cell)
143 << " old_owner=" << edges_owner[edge]
144 << " current_cell_owner=" << cell.owner()
145 << " new_owner=" << cells_owner[cell];
146 }
147#endif /* ARCANE_DEBUG_LOAD_BALANCING */
148 edges_owner[edge] = cells_owner[cell];
149 }
150 edges_owner.synchronize();
151 }
152
153 // Détermine les nouveaux propriétaires des faces
154 {
155 ENUMERATE_FACE(i_face,m_mesh->ownFaces()){
156 const Face face = *i_face;
157 const Cell cell = owner_builder.connectedCellOfItem(face);
158 faces_owner[face] = cells_owner[cell];
159 }
160 faces_owner.synchronize();
161 }
162
163
164 // Détermine les nouveaux propriétaires des particules
165 // Les particules ont le même propriétaire que celui de la maille dans
166 // laquelle elle se trouve.
167 for( IItemFamily* family : m_mesh->itemFamilies() ){
168 if (family->itemKind()!=IK_Particle)
169 continue;
170 // Positionne les nouveaux propriétaires des particle
171 VariableItemInt32& particles_owner(family->itemsNewOwner());
172 ENUMERATE_PARTICLE (i_particle, family->allItems()) {
173 Particle particle = *i_particle;
174 Cell pcell = particle.cell();
175 if (!pcell.null())
176 particles_owner[particle] = cells_owner[pcell];
177 else
178 // Si la particule n'a pas de maille, son nouveau propriétaire est le propriétaire actuel.
179 particles_owner[particle] = particle.owner();
180 }
181 }
182
183 // GraphOnDoF
184 if(m_mesh->itemFamilyNetwork())
185 {
186 // Dof with Mesh Item connectivity
187 for( IItemFamily* family : m_mesh->itemFamilies() )
188 {
189 if (family->itemKind()!=IK_DoF || family->name()==mesh::GraphDoFs::linkFamilyName())
190 continue;
191 VariableItemInt32& dofs_new_owner(family->itemsNewOwner());
192 std::array<Arcane::eItemKind,5> dualitem_kinds = {IK_Cell,IK_Face,IK_Edge,IK_Node,IK_Particle} ;
193 for(auto dualitem_kind : dualitem_kinds)
194 {
195 IItemFamily* dualitem_family = dualitem_kind==IK_Particle? m_mesh->findItemFamily(dualitem_kind,mesh::ParticleFamily::defaultFamilyName(), false):
196 m_mesh->itemFamily(dualitem_kind) ;
197 if(dualitem_family)
198 {
199
200 VariableItemInt32& dualitems_new_owner(dualitem_family->itemsNewOwner());
201 auto connectivity_name = mesh::connectivityName(family,dualitem_family) ;
202 bool is_dof2dual = true ;
203 auto connectivity = m_mesh->itemFamilyNetwork()->getConnectivity(family,dualitem_family,connectivity_name) ;
204 if(!connectivity)
205 {
206 connectivity = m_mesh->itemFamilyNetwork()->getConnectivity(dualitem_family,family,connectivity_name) ;
207 is_dof2dual = false ;
208 }
209
210 if(connectivity)
211 {
212 ConnectivityItemVector accessor(connectivity);
213 if(is_dof2dual)
214 {
215 ENUMERATE_ITEM(item, family->allItems().own())
216 {
217 auto connected_items = accessor.connectedItems(ItemLocalId(item)) ;
218 if(connected_items.size()>0)
219 {
220 dofs_new_owner[*item] = dualitems_new_owner[connected_items[0]] ;
221 }
222 }
223 }
224 else
225 {
226 ENUMERATE_ITEM(item, dualitem_family->allItems())
227 {
228 ENUMERATE_ITEM(connected_item,accessor.connectedItems(ItemLocalId(item)))
229 {
230 dofs_new_owner[*connected_item] = dualitems_new_owner[*item] ;
231 }
232 }
233 }
234 }
235 }
236 }
237 }
238 // Dof with DoF connectivity
239 IItemFamily* links_family = m_mesh->findItemFamily(IK_DoF, mesh::GraphDoFs::linkFamilyName(), false);
240 if(links_family)
241 {
242 VariableItemInt32& links_new_owner(links_family->itemsNewOwner());
243 IItemFamily* dualnodes_family = m_mesh->findItemFamily(IK_DoF, mesh::GraphDoFs::dualNodeFamilyName(), false);
244 if(dualnodes_family)
245 {
246 VariableItemInt32& dualnodes_new_owner(dualnodes_family->itemsNewOwner());
247 auto connectivity_name = mesh::connectivityName(links_family,dualnodes_family) ;
248 auto connectivity = m_mesh->itemFamilyNetwork()->getConnectivity(links_family,dualnodes_family,connectivity_name) ;
249 if(connectivity)
250 {
251 ConnectivityItemVector accessor(connectivity);
252 ENUMERATE_ITEM(item, links_family->allItems().own())
253 {
254 auto connected_items = accessor.connectedItems(ItemLocalId(item)) ;
255 if(connected_items.size()>0)
256 {
257 links_new_owner[*item] = dualnodes_new_owner[connected_items[0]] ;
258 }
259 }
260 }
261 }
262 }
263 }
264}
265
266/*---------------------------------------------------------------------------*/
267/*---------------------------------------------------------------------------*/
268
269void UnstructuredMeshUtilities::
270localIdsFromConnectivity(eItemKind item_kind,
271 IntegerConstArrayView items_nb_node,
272 Int64ConstArrayView items_connectivity,
273 Int32ArrayView local_ids,
274 bool allow_null)
275{
276 Integer nb_item = items_nb_node.size();
277 if (item_kind!=IK_Face && item_kind!=IK_Cell)
278 throw InvalidArgumentException(A_FUNCINFO,"item_kind",
279 "IK_Cell or IK_Face expected",
280 (int)item_kind);
281 if (item_kind==IK_Cell)
282 throw NotImplementedException(A_FUNCINFO,"not implemented for cells");
283
284 if (nb_item!=local_ids.size())
285 throw InvalidArgumentException(A_FUNCINFO,"local_ids",
286 "Size different from 'items_nb_node'",
287 (int)item_kind);
288
289 Integer item_connectivity_index = 0;
291 buf.reserve(256);
292 ItemInternalList nodes(m_mesh->itemsInternal(IK_Node));
293 for( Integer i=0; i<nb_item; ++i ){
294 Integer current_nb_node = items_nb_node[i];
295 Int64ConstArrayView current_nodes(current_nb_node,items_connectivity.data()+item_connectivity_index);
296 item_connectivity_index += current_nb_node;
297 if (item_kind==IK_Face){
298 buf.resize(current_nb_node);
299 mesh_utils::reorderNodesOfFace(current_nodes,buf);
300 Int64 first_node_uid = buf[0];
301 Int64ArrayView first_node_uid_array(1,&first_node_uid);
302 Int32 first_node_lid = CheckedConvert::toInt32(buf[0]);
303 Int32ArrayView first_node_lid_array(1,&first_node_lid);
304 m_mesh->nodeFamily()->itemsUniqueIdToLocalId(first_node_lid_array,first_node_uid_array,!allow_null);
305 if (first_node_lid == NULL_ITEM_LOCAL_ID){
306 if (allow_null){
307 local_ids[i] = NULL_ITEM_LOCAL_ID;
308 }
309 else {
310 StringBuilder sb("Face with nodes (");
311 for(Integer j=0;j<current_nb_node;++j) {
312 if (j != 0) sb += " ";
313 sb += current_nodes[j];
314 }
315 sb += ") not found (first node ";
316 sb += first_node_uid;
317 sb += " not found)";
319 }
320 continue;
321 }
322
323 Node node(nodes[first_node_lid]);
324 Face face(mesh_utils::getFaceFromNodesUnique(node,buf));
325 if (face.null()){
326 if (allow_null){
327 local_ids[i] = NULL_ITEM_LOCAL_ID;
328 }
329 else {
330 StringBuilder sb("Face with nodes (");
331 for(Integer j=0;j<current_nb_node;++j) {
332 if (j != 0) sb += " ";
333 sb += current_nodes[j];
334 }
335 sb += ") not found";
337 }
338 }
339 else
340 local_ids[i] = face.localId();
341 }
342 }
343}
344
345/*---------------------------------------------------------------------------*/
346/*---------------------------------------------------------------------------*/
347
348void UnstructuredMeshUtilities::
349getFacesLocalIdFromConnectivity(ConstArrayView<ItemTypeId> items_type,
350 ConstArrayView<Int64> items_connectivity,
351 ArrayView<Int32> local_ids,
352 bool allow_null)
353{
354 Int32 nb_item = items_type.size();
355
356 if (nb_item!=local_ids.size())
357 throw InvalidArgumentException(A_FUNCINFO,"local_ids",
358 "Size different from 'items_type'");
359
360 Int32 item_connectivity_index = 0;
361 ItemTypeMng* item_type_mng = m_mesh->itemTypeMng();
362 NodesOfItemReorderer face_reorderer(item_type_mng);
363
364 ItemInternalList nodes(m_mesh->itemsInternal(IK_Node));
365 for( Integer i=0; i<nb_item; ++i ){
366 ItemTypeId current_type_id = items_type[i];
367 Int32 current_nb_node = item_type_mng->typeFromId(current_type_id)->nbLocalNode();
368 Int64ConstArrayView current_nodes(current_nb_node,items_connectivity.data()+item_connectivity_index);
369 item_connectivity_index += current_nb_node;
370
371 face_reorderer.reorder(current_type_id,current_nodes);
372 ConstArrayView<Int64> buf(face_reorderer.sortedNodes());
373 Int64 first_node_uid = buf[0];
374 Int64ArrayView first_node_uid_array(1,&first_node_uid);
375 Int32 first_node_lid = CheckedConvert::toInt32(buf[0]);
376 Int32ArrayView first_node_lid_array(1,&first_node_lid);
377 m_mesh->nodeFamily()->itemsUniqueIdToLocalId(first_node_lid_array,first_node_uid_array,!allow_null);
378 if (first_node_lid == NULL_ITEM_LOCAL_ID){
379 if (allow_null){
380 local_ids[i] = NULL_ITEM_LOCAL_ID;
381 }
382 else {
383 StringBuilder sb("Face with nodes (");
384 for(Integer j=0;j<current_nb_node;++j) {
385 if (j != 0) sb += " ";
386 sb += current_nodes[j];
387 }
388 sb += ") not found (first node ";
389 sb += first_node_uid;
390 sb += " not found)";
392 }
393 continue;
394 }
395
396 Node node(nodes[first_node_lid]);
397 Face face(MeshUtils::getFaceFromNodesUnique(node,buf));
398 if (face.null()){
399 if (allow_null){
400 local_ids[i] = NULL_ITEM_LOCAL_ID;
401 }
402 else {
403 StringBuilder sb("Face with nodes (");
404 for(Integer j=0;j<current_nb_node;++j) {
405 if (j != 0) sb += " ";
406 sb += current_nodes[j];
407 }
408 sb += ") not found";
410 }
411 }
412 else
413 local_ids[i] = face.localId();
414 }
415}
416
417/*---------------------------------------------------------------------------*/
418/*---------------------------------------------------------------------------*/
419
420Real3 UnstructuredMeshUtilities::
421_round(Real3 value)
422{
423 Real3 rvalue = value;
424 // Evite les problèmes d'arrondi numérique
425 if (math::isNearlyZero(value.x))
426 rvalue.x = 0.0;
427 if (math::isNearlyZero(value.y))
428 rvalue.y = 0.0;
429 if (math::isNearlyZero(value.z))
430 rvalue.z = 0.0;
431
432 if (math::isNearlyEqual(value.x,1.0))
433 rvalue.x = 1.0;
434 if (math::isNearlyEqual(value.y,1.0))
435 rvalue.y = 1.0;
436 if (math::isNearlyEqual(value.z,1.0))
437 rvalue.z = 1.0;
438
439 if (math::isNearlyEqual(value.x,-1.0))
440 rvalue.x = -1.0;
441 if (math::isNearlyEqual(value.y,-1.0))
442 rvalue.y = -1.0;
443 if (math::isNearlyEqual(value.z,-1.0))
444 rvalue.z = -1.0;
445
446 return rvalue;
447}
448
449/*---------------------------------------------------------------------------*/
450/*---------------------------------------------------------------------------*/
451
452Real3 UnstructuredMeshUtilities::
453computeNormal(const FaceGroup& face_group,const VariableNodeReal3& nodes_coord)
454{
455 String func_name = "UnstructuredMeshUtilities::computeNormal";
456 IParallelMng* pm = m_mesh->parallelMng();
457 Integer rank = pm->commRank();
458 //Integer nb_item = face_group.size();
459 Integer nb_node = 0;
460 Integer mesh_dimension = m_mesh->dimension();
461 // 'global_normal' et 'total_global_normal' servent à determiner
462 // une direction pour la surface en supposant qu'il s'agit
463 // d'une surface externe. La direction sert ensuite à orienter
464 // la normale calculée dans le sens normal sortante.
465 Real3 global_normal = Real3::null();
466 Real nb_global_normal_used_node = 0.0;
467 ENUMERATE_FACE(iface,face_group){
468 Face face = *iface;
469 Integer face_nb_node = face.nbNode();
470 nb_node += face_nb_node;
471
472 if (face.isSubDomainBoundary() && face.isOwn()){
473 Real3 face_normal;
474 if (mesh_dimension==3){
475 Real3 v1 = nodes_coord[face.node(1)] - nodes_coord[face.node(0)];
476 Real3 v2 = nodes_coord[face.node(2)] - nodes_coord[face.node(0)];
477 face_normal = math::vecMul(v1,v2);
478 }
479 else if (mesh_dimension==2){
480 Real3 dir = nodes_coord[face.node(0)] - nodes_coord[face.node(1)];
481 face_normal.x = - dir.y;
482 face_normal.y = dir.x;
483 face_normal.z = 0.0;
484 }
485 if (face.boundaryCell()==face.frontCell())
486 face_normal = -face_normal;
487 //info() << "ADD NORMAL normal=" << face_normal;
488 global_normal = global_normal + face_normal;
489 nb_global_normal_used_node += 1.0;
490 }
491 }
492 // L'algorithme tente de déterminer les noeuds aux extrémités de la surface
493 // On considère qu'il s'agit de ceux qui n'appartiennent qu'à une seule
494 // face de la surface. Cela fonctionne bien si toutes les faces ont au moins
495 // quatres arêtes. S'il y a des triangles, on suppose que ce n'est pas
496 // partout et dans ce cas on a au moins deux noeuds extrèmes connus.
497 typedef HashTableMapT<Int32,Int32> NodeOccurenceMap;
498 // Range dans la table pour chaque noeud le nombre de faces de la surface
499 // auxquelles il est connecté.
500 NodeOccurenceMap nodes_occurence(nb_node*2,true,nb_node);
501 ENUMERATE_FACE(iface,face_group){
502 Face face = *iface;
503 for( NodeLocalId inode : face.nodeIds() ){
504 NodeOccurenceMap::Data* v = nodes_occurence.lookup(inode);
505 if (v)
506 ++v->value();
507 else{
508 //info() << " ADD NODE " << ItemPrinter(*inode) << " coord=" << nodes_coord[inode];
509 nodes_occurence.add(inode,1);
510 }
511 }
512 }
513 UniqueArray<Node> single_nodes;
514 NodeLocalIdToNodeConverter nodes_internal(m_mesh->nodeFamily());
515 nodes_occurence.each([&](NodeOccurenceMap::Data* d){
516 if (d->value()==1){
517 Node node = nodes_internal[d->key()];
518 // En parallèle, ne traite que les noeuds qui nous appartiennent
519 if (node.owner()==rank){
520 single_nodes.add(node);
521 //info() << "SINGLE NODE OWNER lid=" << d->key() << " " << ItemPrinter(node)
522 // << " coord=" << nodes_coord[node];
523 }
524 }
525 });
526 // Chaque sous-domaine collecte les coordonnées des noeuds des autres sous-domaines
527 Integer nb_single_node = single_nodes.size();
528 //info() << "NB SINGLE NODE= " << nb_single_node;
529 Integer total_nb_single_node = pm->reduce(Parallel::ReduceSum,nb_single_node);
530 Real3 total_global_normal;
531 {
532 Real all_min = 0.0;
533 Real all_max = 0.0;
534 Real all_sum = 0.0;
535 Int32 min_rank = -1;
536 Int32 max_rank = -1;
537 pm->computeMinMaxSum(nb_global_normal_used_node,all_min,all_max,all_sum,min_rank,max_rank);
538 Real3 buf;
539 if (max_rank==rank){
540 // Je suis celui qui a le point le plus loin. Je l'envoie aux autres.
541 buf = global_normal;
542 }
543 pm->broadcast(Real3ArrayView(1,&buf),max_rank);
544 total_global_normal = buf;
545 }
546
547 info() << "TOTAL SINGLE NODE= " << total_nb_single_node << " surface=" << face_group.name()
548 << " global_normal = " << total_global_normal;
549 if (total_nb_single_node<2){
550 ARCANE_FATAL("not enough nodes connected to only one face");
551 }
552 Real3UniqueArray coords(nb_single_node);
553 for( Integer i=0; i<nb_single_node; ++i ){
554 coords[i] = nodes_coord[single_nodes[i]];
555 }
556 //Array<Real> all_nodes_coord_real;
557 UniqueArray<Real3> all_nodes_coord;
558 pm->allGatherVariable(coords,all_nodes_coord);
559 //info() << " ALL NODES COORD n=" << all_nodes_coord_real.size();
560 //Array<Real3> all_nodes_coord(total_nb_single_node);
561 Real3 barycentre = Real3::null();
562 for( Integer i=0; i<total_nb_single_node; ++i ){
563 barycentre += all_nodes_coord[i];
564 }
565 barycentre /= (Real)total_nb_single_node;
566 if (total_nb_single_node==2 && m_mesh->dimension()==3){
567 // On a que deux noeuds. Il en faut au moins un troisième pour déterminer
568 // la normale au plan. Pour cela, chaque processeur cherche le noeud de
569 // la surface qui est le plus éloigné du barycentre des deux noeuds déjà
570 // trouvé. Ce noeud le plus éloigné servira de troisième point pour le
571 // plan.
572 Real max_distance = 0.0;
573 Node farthest_node;
574 Real3 s0 = all_nodes_coord[0];
575 Real3 s1 = all_nodes_coord[1];
576 nodes_occurence.each([&](NodeOccurenceMap::Data* d){
577 Node node = nodes_internal[d->key()];
578 Real3 coord = nodes_coord[node];
579 // Ne traite pas les deux noeuds du déjà trouvés
580 if (math::isNearlyEqual(coord,s0))
581 return;
582 if (math::isNearlyEqual(coord,s1))
583 return;
584 Real distance = (coord - barycentre).squareNormL2();
585 if (distance>max_distance){
586 Real3 normal = math::cross(coord-s0, s1-s0);
587 // On ne prend le noeud que s'il n'est pas aligné avec les deux autres.
588 if (!math::isNearlyZero(normal.squareNormL2())){
589 max_distance = distance;
590 farthest_node = node;
591 }
592 }
593 });
594
595 if (!farthest_node.null()){
596 info() << " FARTHEST NODE= " << ItemPrinter(farthest_node) << " dist=" << max_distance;
597 }
598 {
599 Real3 farthest_coord = _broadcastFarthestNode(max_distance,farthest_node,nodes_coord);
600 info() << " FARTHEST NODE ALL coord=" << farthest_coord;
601 all_nodes_coord.add(farthest_coord);
602 }
603 }
604 // Trie les noeuds pour que le calcul ne dépende pas de l'ordre des
605 // opérations en parallèle.
606 std::sort(std::begin(all_nodes_coord),std::end(all_nodes_coord));
607 Integer nb_final_node = all_nodes_coord.size();
608 Real3 full_normal = Real3::null();
609 info() << " NB FINAL NODE=" << nb_final_node;
610 for( Integer i=0; i<nb_final_node; ++i )
611 info() << " NODE=" << all_nodes_coord[i];
612 if (m_mesh->dimension()==2){
613 if (nb_final_node!=2){
614 ARCANE_FATAL("should have 2 border nodes in 2D mesh");
615 }
616 Real3 direction = all_nodes_coord[1] - all_nodes_coord[0];
617 full_normal.x = - direction.y;
618 full_normal.y = direction.x;
619 }
620 else if (m_mesh->dimension()==3){
621 //nb_final_node = 3; // On prend que les 3 premiers points.
622 // NOTE: on pourrait prendre tous les points, car si les trois premiers sont
623 // alignés, la normale ne sera pas bonne.
624 // Si on prend tous les points, il faut être sur qu'ils soient ordonnés
625 // de telle sorte que la normale de trois points consécutifs est toujours
626 // dans le même sens. Cela signifie si on a quatres points par exemple,
627 // que ces 4 points forment un quadrangle non croisé (pas en forme
628 // de papillon)
629 Real3 first_normal = math::vecMul(all_nodes_coord[2]-all_nodes_coord[0],
630 all_nodes_coord[1]-all_nodes_coord[0]);
631 for( Integer i=0; i<nb_final_node; ++i ){
632 Real3 s0 = all_nodes_coord[i];
633 Real3 s1 = all_nodes_coord[(i+nb_final_node-1)%nb_final_node];
634 Real3 s2 = all_nodes_coord[(i+1)%nb_final_node];
635 Real3 normal = math::vecMul(s2-s0, s1-s0);
636 info() << " ADD NORMAL: " << normal;
637 full_normal += normal;
638 info() << " FULL: " << full_normal;
639 }
640 if (math::isNearlyZero(full_normal.squareNormL2()))
641 full_normal = first_normal;
642 }
643 else
644 ARCANE_FATAL("invalid mesh dimension (should be 2 or 3)");
645 Real a = full_normal.normL2();
646 if (math::isZero(a))
647 ARCANE_FATAL("invalid value for normal");
648 full_normal /= a;
649 Real b = total_global_normal.normL2();
650 Real dir = 0.0;
651 info() << " Normal is " << full_normal;
652 if (!math::isZero(b)){
653 total_global_normal /= b;
654 dir = math::scaMul(full_normal,total_global_normal);
655 if (dir<0.0)
656 full_normal = -full_normal;
657 }
658 full_normal = _round(full_normal);
659 info() << " Final normal is " << full_normal << " dir=" << dir;
660 return full_normal;
661}
662
663/*---------------------------------------------------------------------------*/
664/*---------------------------------------------------------------------------*/
672Real3 UnstructuredMeshUtilities::
673computeDirection(const NodeGroup& node_group,
674 const VariableNodeReal3& nodes_coord,
675 Real3* n1,Real3* n2)
676{
677 String func_name = "UnstructuredMeshUtilities::computeDirection";
678 IParallelMng* pm = m_mesh->parallelMng();
679 //Integer rank = pm->commRank();
680 //Integer nb_node_own = node_group.own().size();
681 //Integer total_nb_node_own = pm->reduce(Parallel::ReduceSum,nb_node_own);
682 Real3 barycentre = Real3::null();
683 ENUMERATE_NODE(inode,node_group.own()){
684 barycentre += nodes_coord[inode];
685 }
686 Real r_barycentre[3];
687 r_barycentre[0] = barycentre.x;
688 r_barycentre[1] = barycentre.y;
689 r_barycentre[2] = barycentre.z;
690 RealArrayView rav(3,r_barycentre);
692 barycentre = Real3(rav[0],rav[1],rav[2]);
693 debug() << " BARYCENTRE COMPUTE DIRECTION = " << barycentre;
694 pm->barrier();
695
696 // Détermine le noeud le plus éloigné du barycentre
697 Real3 first_boundary_coord;
698 {
699 Real max_distance = 0.0;
700 Node farthest_node;
701 ENUMERATE_NODE(inode,node_group.own()){
702 Node node = *inode;
703 Real3 coord = nodes_coord[node];
704 Real distance = (coord - barycentre).squareNormL2();
705 if (distance>max_distance){
706 max_distance = distance;
707 farthest_node = node;
708 }
709 }
710 first_boundary_coord = _broadcastFarthestNode(max_distance,farthest_node,nodes_coord);
711 if (n1)
712 *n1 = first_boundary_coord;
713 }
714 Real3 second_boundary_coord;
715 {
716 Real max_distance = 0.0;
717 Node farthest_node;
718 ENUMERATE_NODE(inode,node_group.own()){
719 Node node = *inode;
720 Real3 coord = nodes_coord[node];
721 Real distance = (coord - first_boundary_coord).squareNormL2();
722 if (distance>max_distance){
723 max_distance = distance;
724 farthest_node = node;
725 }
726 }
727 second_boundary_coord = _broadcastFarthestNode(max_distance,farthest_node,nodes_coord);
728 if (n2)
729 *n2 = second_boundary_coord;
730 }
731 Real3 direction = second_boundary_coord - first_boundary_coord;
732 Real norm = direction.normL2();
733 if (math::isZero(norm)){
734 ARCANE_FATAL("Direction is null for group '{0}' first_coord={1} second_coord={2}",
735 node_group.name(),first_boundary_coord,second_boundary_coord);
736 }
737 return direction / norm;
738}
739
740/*---------------------------------------------------------------------------*/
741/*---------------------------------------------------------------------------*/
742
743Real3 UnstructuredMeshUtilities::
744_broadcastFarthestNode(Real distance,const Node& farthest_node,
745 const VariableNodeReal3& nodes_coord)
746{
747 String func_name = "UnstructuredMeshUtilities::_broadcastFarthestNode()";
748 IParallelMng* pm = m_mesh->parallelMng();
749 Integer rank = pm->commRank();
750
751 Real all_min_distance = 0.0;
752 Real all_max_distance = 0.0;
753 Real all_sum_distance = 0.0;
754 Int32 min_rank = -1;
755 Int32 max_rank = -1;
756 pm->computeMinMaxSum(distance,all_min_distance,all_max_distance,all_sum_distance,min_rank,max_rank);
757 Real3 buf;
758 if (!farthest_node.null())
759 debug() << " FARTHEST NODE myself coord=" << nodes_coord[farthest_node]
760 << " distance=" << distance
761 << " rank=" << max_rank
762 << " max_distance=" << all_max_distance;
763 else
764 debug() << " FARTHEST NODE myself coord=none"
765 << " distance=" << distance
766 << " rank=" << max_rank
767 << " max_distance=" << all_max_distance;
768
769 if (max_rank==rank){
770 if (farthest_node.null())
771 ARCANE_FATAL("can not find farthest node");
772 // Je suis celui qui a le point le plus loin. Je l'envoie aux autres.
773 buf = nodes_coord[farthest_node];
774 debug() << " I AM FARTHEST NODE ALL coord=" << buf;
775 }
776 pm->broadcast(Real3ArrayView(1,&buf),max_rank);
777 Real3 farthest_coord(buf);
778 debug() << " FARTHEST NODE ALL coord=" << farthest_coord
779 << " rank=" << max_rank
780 << " max_distance=" << all_max_distance;
781 return farthest_coord;
782}
783
784/*---------------------------------------------------------------------------*/
785/*---------------------------------------------------------------------------*/
786
787void UnstructuredMeshUtilities::
788computeAdjency(ItemPairGroup adjency_array,eItemKind link_kind,Integer nb_layer)
789{
790 m_compute_adjacency_functor->computeAdjacency(adjency_array, link_kind, nb_layer);
791}
792
793/*---------------------------------------------------------------------------*/
794/*---------------------------------------------------------------------------*/
795
796bool UnstructuredMeshUtilities::
797writeToFile(const String& file_name,const String& service_name)
798{
799 ServiceBuilder<IMeshWriter> sb(m_mesh->handle());
800 auto mesh_writer = sb.createReference(service_name,SB_AllowNull);
801
802 if (!mesh_writer){
803 UniqueArray<String> available_names;
804 sb.getServicesNames(available_names);
805 warning() << String::format("The specified service '{0}' to write the mesh is not available."
806 " Valid names are {1}",service_name,available_names);
807 return true;
808 }
809 mesh_writer->writeMeshToFile(m_mesh,file_name);
810 return false;
811}
812
813/*---------------------------------------------------------------------------*/
814/*---------------------------------------------------------------------------*/
815
816void UnstructuredMeshUtilities::
817partitionAndExchangeMeshWithReplication(IMeshPartitionerBase* partitioner,
818 bool initial_partition)
819{
820 IPrimaryMesh* primary_mesh = partitioner->primaryMesh();
821 IMesh* mesh = primary_mesh;
822 if (mesh!=this->m_mesh)
823 throw ArgumentException(A_FUNCINFO,"partitioner->mesh() != this->m_mesh");
824
825 IParallelMng* pm = mesh->parallelMng();
826 ITimeStats* ts = pm->timeStats();
828 bool has_replication = pr->hasReplication();
829
830 // En mode réplication, seul le premier réplicat fait l'équilibrage.
831 // Il doit ensuite envoyer aux autres ces informations de maillage
832 // pour qu'ils aient le même maillage.
833
834 info() << "Partition start date=" << platform::getCurrentDateTime();
835 if (pr->isMasterRank()){
836 Timer::Action ts_action1(ts,"MeshPartition",true);
837 info() << "Partitioning the mesh (initial?=" << initial_partition << ")";
838 partitioner->partitionMesh(initial_partition);
839 }
840 else
841 info() << "Waiting for partition information from the master replica";
842
843 if (has_replication){
844 pm->barrier();
845
846 // Vérifie que toute les familles sont les mêmes.
847 mesh->checker()->checkValidReplication();
848
849 Int32 replica_master_rank = pr->masterReplicationRank();
850 IParallelMng* rep_pm = pr->replicaParallelMng();
851
852 // Seul le replica maitre a les bons propriétaires. Il faut mettre
853 // à jour les autres à partir de celui-ci. Pour cela on synchronize
854 // les propriétaires des mailles et ensuite on met à jour les autres familles
855 // à partir des propriétaires des mailles.
856 {
857 Int32ArrayView owners = mesh->cellFamily()->itemsNewOwner().asArray();
858 rep_pm->broadcast(owners,replica_master_rank);
859 if (!pr->isMasterRank()){
861 }
862 }
863 }
864 info() << "Partition end date=" << platform::getCurrentDateTime();
865 {
866 Timer::Action ts_action2(ts,"MeshExchange",true);
867 primary_mesh->exchangeItems();
868 }
869 info() << "Exchange end date=" << platform::getCurrentDateTime();
870 partitioner->notifyEndPartition();
871
872 // Il faut recompacter pour se retrouver dans la même situation que
873 // si on avait juste lu un maillage directement (qui fait un prepareForDump()
874 // lors de l'appel à endAllocate()).
875 // On le fait aussi en cas de réplication pour éviter d'éventuelles
876 // incohérences si par la suite certains réplicas appellent cette
877 // méthode et pas les autres.
878 // TODO: regarder s'il faut le faire aussi en cas de repartitionnement.
879 if (initial_partition || has_replication)
880 mesh->prepareForDump();
881}
882
883/*---------------------------------------------------------------------------*/
884/*---------------------------------------------------------------------------*/
885
886void UnstructuredMeshUtilities::
887mergeNodes(Int32ConstArrayView nodes_local_id,
888 Int32ConstArrayView nodes_to_merge_local_id,
889 bool allow_non_corresponding_face)
890{
891 mesh::MeshNodeMerger merger(m_mesh);
892 merger.mergeNodes(nodes_local_id, nodes_to_merge_local_id, allow_non_corresponding_face);
893}
894
895/*---------------------------------------------------------------------------*/
896/*---------------------------------------------------------------------------*/
897
898void UnstructuredMeshUtilities::
899computeAndSetOwnersForNodes()
900{
901 mesh::ItemsOwnerBuilder owner_builder(m_mesh);
902 owner_builder.computeNodesOwner();
903}
904
905/*---------------------------------------------------------------------------*/
906/*---------------------------------------------------------------------------*/
907
908void UnstructuredMeshUtilities::
909computeAndSetOwnersForEdges()
910{
911 mesh::ItemsOwnerBuilder owner_builder(m_mesh);
912 owner_builder.computeEdgesOwner();
913}
914
915/*---------------------------------------------------------------------------*/
916/*---------------------------------------------------------------------------*/
917
918void UnstructuredMeshUtilities::
919computeAndSetOwnersForFaces()
920{
921 mesh::ItemsOwnerBuilder owner_builder(m_mesh);
922 owner_builder.computeFacesOwner();
923}
924
925/*---------------------------------------------------------------------------*/
926/*---------------------------------------------------------------------------*/
927namespace
928{
929 void _recomputeUniqueIds(IItemFamily* family)
930 {
931 SmallArray<Int64> unique_ids;
932 ENUMERATE_ (ItemWithNodes, iitem, family->allItems()) {
933 ItemWithNodes item = *iitem;
934 Int32 index = 0;
935 unique_ids.resize(item.nbNode());
936 for (Node node : item.nodes()) {
937 unique_ids[index] = node.uniqueId();
938 ++index;
939 }
940 Int64 new_uid = MeshUtils::generateHashUniqueId(unique_ids);
941 item.mutableItemBase().setUniqueId(new_uid);
942 }
944 }
945} // namespace
946
947/*---------------------------------------------------------------------------*/
948/*---------------------------------------------------------------------------*/
949
950void UnstructuredMeshUtilities::
951recomputeItemsUniqueIdFromNodesUniqueId()
952{
953 IMesh* mesh = m_mesh;
955 ITraceMng* tm = mesh->traceMng();
956
957 tm->info() << "Calling RecomputeItemsUniqueIdFromNodesUniqueId()";
958 // D'abord indiquer que les noeuds ont changés pour éventuellement
959 // remettre à jour l'orientation des faces.
960 mesh->nodeFamily()->notifyItemsUniqueIdChanged();
961 _recomputeUniqueIds(mesh->edgeFamily());
962 _recomputeUniqueIds(mesh->faceFamily());
963 _recomputeUniqueIds(mesh->cellFamily());
964}
965
966/*---------------------------------------------------------------------------*/
967/*---------------------------------------------------------------------------*/
968
969} // End namespace Arcane
970
971/*---------------------------------------------------------------------------*/
972/*---------------------------------------------------------------------------*/
#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_FACE(name, group)
Enumérateur générique d'un groupe de faces.
#define ENUMERATE_(type, name, group)
Enumérateur générique d'un groupe d'entité
#define ENUMERATE_PARTICLE(name, group)
Enumérateur générique d'un groupe de particules.
#define ENUMERATE_EDGE(name, group)
Enumérateur générique d'un groupe d'arêtes.
#define ENUMERATE_ITEM(name, group)
Enumérateur générique d'un groupe de noeuds.
#define ENUMERATE_NODE(name, group)
Enumérateur générique d'un groupe de noeuds.
Fonctions utilitaires sur le maillage.
bool reorderNodesOfFace(Int64ConstArrayView before_ids, Int64ArrayView after_ids)
Réordonne les noeuds d'une face.
Integer size() const
Nombre d'éléments du vecteur.
Exception lorsqu'un argument est invalide.
Vue modifiable d'un tableau d'un type T.
constexpr Integer size() const noexcept
Retourne la taille du tableau.
void resize(Int64 s)
Change le nombre d'éléments du tableau à s.
void reserve(Int64 new_capacity)
Réserve le mémoire pour new_capacity éléments.
void add(ConstReferenceType val)
Ajoute l'élément val à la fin du tableau.
Maille d'un maillage.
Definition Item.h:1214
Gère la récupération des informations de connectivité.
ItemVectorView connectedItems(ItemLocalId item)
Retourne les entités connectées à item.
Vue constante d'un tableau de type T.
constexpr const_pointer data() const noexcept
Pointeur sur la mémoire allouée.
constexpr Integer size() const noexcept
Nombre d'éléments du tableau.
Arête d'une maille.
Definition Item.h:826
Face d'une maille.
Definition Item.h:964
Cell frontCell() const
Maille devant la face (maille nulle si aucune).
Definition Item.h:1652
bool isSubDomainBoundary() const
Indique si la face est au bord du sous-domaine (i.e nbCell()==1).
Definition Item.h:1061
Cell boundaryCell() const
Maille associée à cette face frontière (maille nulle si aucune).
Definition Item.h:1640
Table de hachage pour tableaux associatifs.
Interface d'une famille d'entités.
Definition IItemFamily.h:86
virtual void notifyItemsUniqueIdChanged()=0
Notifie que les numéros uniques des entités ont été modifiées.
virtual ItemGroup allItems() const =0
Groupe de toutes les entités.
virtual VariableItemInt32 & itemsNewOwner()=0
Variable contenant le numéro du nouveau sous-domaine propriétaire de l'entité.
Interface d'un partitionneur de maillage.
virtual IPrimaryMesh * primaryMesh()=0
Maillage associé
virtual void partitionMesh(bool initial_partition)=0
virtual void notifyEndPartition()=0
Notification lors de la fin d'un re-partitionnement (après échange des entités).
Interface du gestionnaire de parallélisme pour un sous-domaine.
virtual void computeMinMaxSum(char val, char &min_val, char &max_val, char &sum_val, Int32 &min_rank, Int32 &max_rank)=0
Calcule en une opération la somme, le min, le max d'une valeur.
virtual Int32 commRank() const =0
Rang de cette instance dans le communicateur.
virtual IParallelReplication * replication() const =0
Informations sur la réplication.
virtual ITimeStats * timeStats() const =0
Gestionnaire de statistiques associé (peut être nul).
virtual void allGatherVariable(ConstArrayView< char > send_buf, Array< char > &recv_buf)=0
Effectue un regroupement sur tous les processeurs.
virtual char reduce(eReduceType rt, char v)=0
Effectue la réduction de type rt sur le réel v et retourne la valeur.
virtual void barrier()=0
Effectue une barière.
Informations sur la réplication des sous-domaines en parallèle.
virtual bool hasReplication() const =0
Indique si la réplication est active.
virtual IParallelMng * replicaParallelMng() const =0
Communicateur associé à tous les réplicats représentant un même sous-domaine.
virtual Int32 masterReplicationRank() const =0
Rang dans la réplication du maître.
virtual bool isMasterRank() const =0
Indique si ce rang de réplication est le maître.
virtual void exchangeItems()=0
Change les sous-domaines propriétaires des entités.
Interface gérant les statistiques sur les temps d'exécution.
Definition ITimeStats.h:43
Interface du gestionnaire de traces.
virtual TraceMessage info()=0
Flot pour un message d'information.
Exception lorsqu'une erreur fatale est survenue.
const String & name() const
Nom du groupe.
Definition ItemGroup.h:76
ItemGroup own() const
Groupe équivalent à celui-ci mais contenant uniquement les éléments propres au sous-domaine.
Definition ItemGroup.cc:190
Index d'un Item dans une variable.
Definition ItemLocalId.h:41
Tableau de listes d'entités.
Classe utilitaire pour imprimer les infos sur une entité.
Definition ItemPrinter.h:34
Type d'une entité (Item).
Definition ItemTypeId.h:32
Integer nbLocalNode() const
Nombre de noeuds de l'entité
Gestionnaire des types d'entités d'un maillage.
Definition ItemTypeMng.h:64
ItemTypeInfo * typeFromId(Integer id) const
Type correspondant au numéro id.
Elément de maillage s'appuyant sur des noeuds (Edge,Face,Cell).
Definition Item.h:736
Node node(Int32 i) const
i-ème noeud de l'entité
Definition Item.h:791
NodeConnectedListViewType nodes() const
Liste des noeuds de l'entité
Definition Item.h:794
Int32 nbNode() const
Nombre de noeuds de l'entité
Definition Item.h:788
NodeLocalIdView nodeIds() const
Liste des noeuds de l'entité
Definition Item.h:797
impl::MutableItemBase mutableItemBase() const
Partie interne modifiable de l'entité.
Definition Item.h:380
constexpr Int32 localId() const
Identifiant local de l'entité dans le sous-domaine du processeur.
Definition Item.h:219
Int32 owner() const
Numéro du sous-domaine propriétaire de l'entité
Definition Item.h:238
constexpr bool null() const
true si l'entité est nul (i.e. non connecté au maillage)
Definition Item.h:216
constexpr bool isOwn() const
true si l'entité est appartient au sous-domaine
Definition Item.h:253
Classe pour convertir un NodeLocalId vers une arête.
Noeud d'un maillage.
Definition Item.h:582
Classe utilitaire pour réordonner les noeuds d'une entité.
Exception lorsqu'une fonction n'est pas implémentée.
Particule.
Definition Item.h:1423
Cell cell() const
Maille à laquelle appartient la particule. Il faut appeler setCell() avant d'appeler cette fonction....
Definition Item.h:1488
Classe gérant un vecteur réel de 3 dimensions.
__host__ __device__ Real normL2() const
Retourne la norme L2 du triplet $ .
Definition MathReal3.h:100
constexpr __host__ __device__ Real squareNormL2() const
Retourne le carré de la norme L2 du triplet $ .
Classe utilitaire pour instantier un service d'une interface donnée.
Ref< InterfaceType > createReference(const String &name, eServiceBuilderProperties properties=SB_None)
Créé une instance implémentant l'interface InterfaceType.
void getServicesNames(Array< String > &names) const
Remplit names avec les noms des services disponibles pour instantier cette interface.
Tableau 1D de données avec buffer pré-alloué sur la pile.
Constructeur de chaîne de caractère unicode.
String toString() const
Retourne la chaîne de caractères construite.
Chaîne de caractères unicode.
Postionne le nom de l'action en cours d'exécution.
Definition Timer.h:110
TraceMessageDbg debug(Trace::eDebugLevel=Trace::Medium) const
Flot pour un message de debug.
TraceMessage info() const
Flot pour un message d'information.
TraceMessage warning() const
Flot pour un message d'avertissement.
Vecteur 1D de données avec sémantique par valeur (style STL).
void changeOwnersFromCells() override
Positionne les nouveaux propriétaires des noeuds, arêtes et faces à partir des mailles.
Classe générique pour calculer les propriétaires des entités.
Fusion de nœuds d'un maillage.
void mergeNodes(Int32ConstArrayView nodes_local_id, Int32ConstArrayView nodes_to_merge_local_id, bool allow_non_corresponding_face=false)
__host__ __device__ Real3 vecMul(Real3 u, Real3 v)
Produit vectoriel de u par v dans .
Definition MathUtils.h:48
__host__ __device__ Real scaMul(Real2 u, Real2 v)
Produit scalaire de u par v dans .
Definition MathUtils.h:112
__host__ __device__ Real3 cross(Real3 v1, Real3 v2)
Produit vectoriel de deux vecteurs à 3 composantes.
Definition MathUtils.h:758
ItemGroupT< Face > FaceGroup
Groupe de faces.
Definition ItemTypes.h:178
ItemGroupT< Node > NodeGroup
Groupe de noeuds.
Definition ItemTypes.h:167
MeshVariableScalarRefT< Node, Real3 > VariableNodeReal3
Grandeur au noeud de type coordonnées.
ItemVariableScalarRefT< Int32 > VariableItemInt32
Grandeur de type entier 32 bits.
@ ReduceSum
Somme des valeurs.
bool isZero(const BuiltInProxy< _Type > &a)
Teste si une valeur est exactement égale à zéro.
constexpr __host__ __device__ bool isNearlyEqual(const Type &a, const Type &b)
Teste si deux valeurs sont approximativement égales. Pour les types entiers, cette fonction est équiv...
Definition TypeEqual.h:228
String getCurrentDateTime()
Date et l'heure courante sous la forme ISO 8601.
-- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature --
ArrayView< Int64 > Int64ArrayView
Equivalent C d'un tableau à une dimension d'entiers 64 bits.
Definition UtilsTypes.h:449
@ SB_AllowNull
Autorise l'absence du service.
UniqueArray< Int64 > Int64UniqueArray
Tableau dynamique à une dimension d'entiers 64 bits.
Definition UtilsTypes.h:337
std::int64_t Int64
Type entier signé sur 64 bits.
ArrayView< Real3 > Real3ArrayView
Equivalent C d'un tableau à une dimension de Real3.
Definition UtilsTypes.h:465
Int32 Integer
Type représentant un entier.
UniqueArray< Real3 > Real3UniqueArray
Tableau dynamique à une dimension de vecteurs de rang 3.
Definition UtilsTypes.h:361
ConstArrayView< Int32 > Int32ConstArrayView
Equivalent C d'un tableau à une dimension d'entiers 32 bits.
Definition UtilsTypes.h:480
ConstArrayView< ItemInternal * > ItemInternalList
Type de la liste interne des entités.
Definition ItemTypes.h:466
ConstArrayView< Int64 > Int64ConstArrayView
Equivalent C d'un tableau à une dimension d'entiers 64 bits.
Definition UtilsTypes.h:478
ArrayView< Int32 > Int32ArrayView
Equivalent C d'un tableau à une dimension d'entiers 32 bits.
Definition UtilsTypes.h:451
eItemKind
Genre d'entité de maillage.
@ IK_Particle
Entité de maillage de genre particule.
@ IK_Node
Entité de maillage de genre noeud.
@ IK_Cell
Entité de maillage de genre maille.
@ IK_Face
Entité de maillage de genre face.
@ IK_DoF
Entité de maillage de genre degre de liberte.
@ IK_Edge
Entité de maillage de genre arête.
double Real
Type représentant un réel.
ConstArrayView< Integer > IntegerConstArrayView
Equivalent C d'un tableau à une dimension d'entiers.
Definition UtilsTypes.h:484
ArrayView< Real > RealArrayView
Equivalent C d'un tableau à une dimension de réels.
Definition UtilsTypes.h:457
std::int32_t Int32
Type entier signé sur 32 bits.
Real y
deuxième composante du triplet
Real z
troisième composante du triplet
Real x
première composante du triplet