Arcane  4.2.3.0
Developer documentation
Loading...
Searching...
No Matches
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/* Utility functions for a mesh. */
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 // We assume that we know the new owners of the cells, which
92 // are found in cells_owner. We must
93 // now determine the new owners of the nodes and
94 // faces. Pending an algorithm that better balances
95 // messages, we apply the following:
96 // - each subdomain is responsible for determining the new
97 // owner of the nodes and faces belonging to it.
98 // - for nodes, the new owner is the new owner
99 // of the cell connected to this node whose uniqueId() is the smallest.
100 // - for faces, the new owner is the new owner
101 // of the cell behind this face if it is an internal face
102 // and of the connected cell if it is a boundary face.
103 // - for dual nodes, the new owner is the new owner
104 // of the cell connected to the dual element
105 // - for links, the new owner is the new owner
106 // of the cell connected to the first dual node, that is, the owner
107 // of the link's first dual node
108
109 // Utility for assigning owners to 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 // Determine the new owners of nodes
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 // Determine the new owners of edges
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 // Determine the new owners of 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 // Determine the new owners of particles
164 // Particles have the same owner as the cell they are in.
165 for (IItemFamily* family : m_mesh->itemFamilies()) {
166 if (family->itemKind() != IK_Particle)
167 continue;
168 // Position the new owners of particles
169 VariableItemInt32& particles_owner(family->itemsNewOwner());
170 ENUMERATE_PARTICLE (i_particle, family->allItems()) {
171 Particle particle = *i_particle;
172 Cell pcell = particle.cell();
173 if (!pcell.null())
174 particles_owner[particle] = cells_owner[pcell];
175 else
176 // If the particle has no cell, its new owner is the current owner.
177 particles_owner[particle] = particle.owner();
178 }
179 }
180
181 // GraphOnDoF
182 if (m_mesh->itemFamilyNetwork()) {
183 // Dof with Mesh Item connectivity
184 for (IItemFamily* family : m_mesh->itemFamilies()) {
185 if (family->itemKind() != IK_DoF || family->name() == mesh::GraphDoFs::linkFamilyName())
186 continue;
187 VariableItemInt32& dofs_new_owner(family->itemsNewOwner());
188 std::array<Arcane::eItemKind, 5> dualitem_kinds = { IK_Cell, IK_Face, IK_Edge, IK_Node, IK_Particle };
189 for (auto dualitem_kind : dualitem_kinds) {
190 IItemFamily* dualitem_family = dualitem_kind == IK_Particle ? m_mesh->findItemFamily(dualitem_kind, mesh::ParticleFamily::defaultFamilyName(), false) : m_mesh->itemFamily(dualitem_kind);
191 if (dualitem_family) {
192
193 VariableItemInt32& dualitems_new_owner(dualitem_family->itemsNewOwner());
194 auto connectivity_name = mesh::connectivityName(family, dualitem_family);
195 bool is_dof2dual = true;
196 auto connectivity = m_mesh->itemFamilyNetwork()->getConnectivity(family, dualitem_family, connectivity_name);
197 if (!connectivity) {
198 connectivity = m_mesh->itemFamilyNetwork()->getConnectivity(dualitem_family, family, connectivity_name);
199 is_dof2dual = false;
200 }
201
202 if (connectivity) {
203 ConnectivityItemVector accessor(connectivity);
204 if (is_dof2dual) {
205 ENUMERATE_ITEM (item, family->allItems().own()) {
206 auto connected_items = accessor.connectedItems(ItemLocalId(item));
207 if (connected_items.size() > 0) {
208 dofs_new_owner[*item] = dualitems_new_owner[connected_items[0]];
209 }
210 }
211 }
212 else {
213 ENUMERATE_ITEM (item, dualitem_family->allItems()) {
214 ENUMERATE_ITEM (connected_item, accessor.connectedItems(ItemLocalId(item))) {
215 dofs_new_owner[*connected_item] = dualitems_new_owner[*item];
216 }
217 }
218 }
219 }
220 }
221 }
222 }
223 // Dof with DoF connectivity
224 IItemFamily* links_family = m_mesh->findItemFamily(IK_DoF, mesh::GraphDoFs::linkFamilyName(), false);
225 if (links_family) {
226 VariableItemInt32& links_new_owner(links_family->itemsNewOwner());
227 IItemFamily* dualnodes_family = m_mesh->findItemFamily(IK_DoF, mesh::GraphDoFs::dualNodeFamilyName(), false);
228 if (dualnodes_family) {
229 VariableItemInt32& dualnodes_new_owner(dualnodes_family->itemsNewOwner());
230 auto connectivity_name = mesh::connectivityName(links_family, dualnodes_family);
231 auto connectivity = m_mesh->itemFamilyNetwork()->getConnectivity(links_family, dualnodes_family, connectivity_name);
232 if (connectivity) {
233 ConnectivityItemVector accessor(connectivity);
234 ENUMERATE_ITEM (item, links_family->allItems().own()) {
235 auto connected_items = accessor.connectedItems(ItemLocalId(item));
236 if (connected_items.size() > 0) {
237 links_new_owner[*item] = dualnodes_new_owner[connected_items[0]];
238 }
239 }
240 }
241 }
242 }
243 }
244}
245
246/*---------------------------------------------------------------------------*/
247/*---------------------------------------------------------------------------*/
248
249void UnstructuredMeshUtilities::
250localIdsFromConnectivity(eItemKind item_kind,
251 IntegerConstArrayView items_nb_node,
252 Int64ConstArrayView items_connectivity,
253 Int32ArrayView local_ids,
254 bool allow_null)
255{
256 Integer nb_item = items_nb_node.size();
257 if (item_kind != IK_Face && item_kind != IK_Cell)
258 throw InvalidArgumentException(A_FUNCINFO, "item_kind",
259 "IK_Cell or IK_Face expected",
260 (int)item_kind);
261 if (item_kind == IK_Cell)
262 throw NotImplementedException(A_FUNCINFO, "not implemented for cells");
263
264 if (nb_item != local_ids.size())
265 throw InvalidArgumentException(A_FUNCINFO, "local_ids",
266 "Size different from 'items_nb_node'",
267 (int)item_kind);
268
269 Integer item_connectivity_index = 0;
271 buf.reserve(256);
272 ItemInternalList nodes(m_mesh->itemsInternal(IK_Node));
273 for (Integer i = 0; i < nb_item; ++i) {
274 Integer current_nb_node = items_nb_node[i];
275 Int64ConstArrayView current_nodes(current_nb_node, items_connectivity.data() + item_connectivity_index);
276 item_connectivity_index += current_nb_node;
277 if (item_kind == IK_Face) {
278 buf.resize(current_nb_node);
279 mesh_utils::reorderNodesOfFace(current_nodes, buf);
280 Int64 first_node_uid = buf[0];
281 Int64ArrayView first_node_uid_array(1, &first_node_uid);
282 Int32 first_node_lid = CheckedConvert::toInt32(buf[0]);
283 Int32ArrayView first_node_lid_array(1, &first_node_lid);
284 m_mesh->nodeFamily()->itemsUniqueIdToLocalId(first_node_lid_array, first_node_uid_array, !allow_null);
285 if (first_node_lid == NULL_ITEM_LOCAL_ID) {
286 if (allow_null) {
287 local_ids[i] = NULL_ITEM_LOCAL_ID;
288 }
289 else {
290 StringBuilder sb("Face with nodes (");
291 for (Integer j = 0; j < current_nb_node; ++j) {
292 if (j != 0)
293 sb += " ";
294 sb += current_nodes[j];
295 }
296 sb += ") not found (first node ";
297 sb += first_node_uid;
298 sb += " not found)";
300 }
301 continue;
302 }
303
304 Node node(nodes[first_node_lid]);
305 Face face(mesh_utils::getFaceFromNodesUnique(node, buf));
306 if (face.null()) {
307 if (allow_null) {
308 local_ids[i] = NULL_ITEM_LOCAL_ID;
309 }
310 else {
311 StringBuilder sb("Face with nodes (");
312 for (Integer j = 0; j < current_nb_node; ++j) {
313 if (j != 0)
314 sb += " ";
315 sb += current_nodes[j];
316 }
317 sb += ") not found";
319 }
320 }
321 else
322 local_ids[i] = face.localId();
323 }
324 }
325}
326
327/*---------------------------------------------------------------------------*/
328/*---------------------------------------------------------------------------*/
329
330void UnstructuredMeshUtilities::
331getFacesLocalIdFromConnectivity(ConstArrayView<ItemTypeId> items_type,
332 ConstArrayView<Int64> items_connectivity,
333 ArrayView<Int32> local_ids,
334 bool allow_null)
335{
336 Int32 nb_item = items_type.size();
337
338 if (nb_item != local_ids.size())
339 throw InvalidArgumentException(A_FUNCINFO, "local_ids",
340 "Size different from 'items_type'");
341
342 Int32 item_connectivity_index = 0;
343 ItemTypeMng* item_type_mng = m_mesh->itemTypeMng();
344 NodesOfItemReorderer face_reorderer(item_type_mng);
345
346 ItemInternalList nodes(m_mesh->itemsInternal(IK_Node));
347 for (Integer i = 0; i < nb_item; ++i) {
348 ItemTypeId current_type_id = items_type[i];
349 Int32 current_nb_node = item_type_mng->typeFromId(current_type_id)->nbLocalNode();
350 Int64ConstArrayView current_nodes(current_nb_node, items_connectivity.data() + item_connectivity_index);
351 item_connectivity_index += current_nb_node;
352
353 face_reorderer.reorder(current_type_id, current_nodes);
354 ConstArrayView<Int64> buf(face_reorderer.sortedNodes());
355 Int64 first_node_uid = buf[0];
356 Int64ArrayView first_node_uid_array(1, &first_node_uid);
357 Int32 first_node_lid = CheckedConvert::toInt32(buf[0]);
358 Int32ArrayView first_node_lid_array(1, &first_node_lid);
359 m_mesh->nodeFamily()->itemsUniqueIdToLocalId(first_node_lid_array, first_node_uid_array, !allow_null);
360 if (first_node_lid == NULL_ITEM_LOCAL_ID) {
361 if (allow_null) {
362 local_ids[i] = NULL_ITEM_LOCAL_ID;
363 }
364 else {
365 StringBuilder sb("Face with nodes (");
366 for (Integer j = 0; j < current_nb_node; ++j) {
367 if (j != 0)
368 sb += " ";
369 sb += current_nodes[j];
370 }
371 sb += ") not found (first node ";
372 sb += first_node_uid;
373 sb += " not found)";
375 }
376 continue;
377 }
378
379 Node node(nodes[first_node_lid]);
380 Face face(MeshUtils::getFaceFromNodesUnique(node, buf));
381 if (face.null()) {
382 if (allow_null) {
383 local_ids[i] = NULL_ITEM_LOCAL_ID;
384 }
385 else {
386 StringBuilder sb("Face with nodes (");
387 for (Integer j = 0; j < current_nb_node; ++j) {
388 if (j != 0)
389 sb += " ";
390 sb += current_nodes[j];
391 }
392 sb += ") not found";
394 }
395 }
396 else
397 local_ids[i] = face.localId();
398 }
399}
400
401/*---------------------------------------------------------------------------*/
402/*---------------------------------------------------------------------------*/
403
404Real3 UnstructuredMeshUtilities::
405_round(Real3 value)
406{
407 Real3 rvalue = value;
408 // Avoid numerical rounding issues
409 if (math::isNearlyZero(value.x))
410 rvalue.x = 0.0;
411 if (math::isNearlyZero(value.y))
412 rvalue.y = 0.0;
413 if (math::isNearlyZero(value.z))
414 rvalue.z = 0.0;
415
416 if (math::isNearlyEqual(value.x, 1.0))
417 rvalue.x = 1.0;
418 if (math::isNearlyEqual(value.y, 1.0))
419 rvalue.y = 1.0;
420 if (math::isNearlyEqual(value.z, 1.0))
421 rvalue.z = 1.0;
422
423 if (math::isNearlyEqual(value.x, -1.0))
424 rvalue.x = -1.0;
425 if (math::isNearlyEqual(value.y, -1.0))
426 rvalue.y = -1.0;
427 if (math::isNearlyEqual(value.z, -1.0))
428 rvalue.z = -1.0;
429
430 return rvalue;
431}
432
433/*---------------------------------------------------------------------------*/
434/*---------------------------------------------------------------------------*/
435
436Real3 UnstructuredMeshUtilities::
437computeNormal(const FaceGroup& face_group, const VariableNodeReal3& nodes_coord)
438{
439 String func_name = "UnstructuredMeshUtilities::computeNormal";
440 IParallelMng* pm = m_mesh->parallelMng();
441 Integer rank = pm->commRank();
442 //Integer nb_item = face_group.size();
443 Integer nb_node = 0;
444 Integer mesh_dimension = m_mesh->dimension();
445 // 'global_normal' and 'total_global_normal' are used to determine
446 // a direction for the surface assuming it is
447 // an external surface. The direction is then used to orient
448 // the calculated normal in the outward normal direction.
449 Real3 global_normal = Real3::null();
450 Real nb_global_normal_used_node = 0.0;
451 ENUMERATE_FACE (iface, face_group) {
452 Face face = *iface;
453 Integer face_nb_node = face.nbNode();
454 nb_node += face_nb_node;
455
456 if (face.isSubDomainBoundary() && face.isOwn()) {
457 Real3 face_normal;
458 if (mesh_dimension == 3) {
459 Real3 v1 = nodes_coord[face.node(1)] - nodes_coord[face.node(0)];
460 Real3 v2 = nodes_coord[face.node(2)] - nodes_coord[face.node(0)];
461 face_normal = math::vecMul(v1, v2);
462 }
463 else if (mesh_dimension == 2) {
464 Real3 dir = nodes_coord[face.node(0)] - nodes_coord[face.node(1)];
465 face_normal.x = -dir.y;
466 face_normal.y = dir.x;
467 face_normal.z = 0.0;
468 }
469 if (face.boundaryCell() == face.frontCell())
470 face_normal = -face_normal;
471 //info() << "ADD NORMAL normal=" << face_normal;
472 global_normal = global_normal + face_normal;
473 nb_global_normal_used_node += 1.0;
474 }
475 }
476 // The algorithm attempts to determine the nodes at the ends of the surface
477 // It assumes these are nodes that belong to only one
478 // face of the surface. This works well if all faces have at least
479 // four edges. If there are triangles, we assume this is not
480 // everywhere, and in this case, we have at least two known extreme nodes.
481 typedef HashTableMapT<Int32, Int32> NodeOccurenceMap;
482 // Range in the table for each node the number of surface faces
483 // to which it is connected.
484 NodeOccurenceMap nodes_occurence(nb_node * 2, true, nb_node);
485 ENUMERATE_FACE (iface, face_group) {
486 Face face = *iface;
487 for (NodeLocalId inode : face.nodeIds()) {
488 NodeOccurenceMap::Data* v = nodes_occurence.lookup(inode);
489 if (v)
490 ++v->value();
491 else {
492 //info() << " ADD NODE " << ItemPrinter(*inode) << " coord=" << nodes_coord[inode];
493 nodes_occurence.add(inode, 1);
494 }
495 }
496 }
497 UniqueArray<Node> single_nodes;
498 NodeLocalIdToNodeConverter nodes_internal(m_mesh->nodeFamily());
499 nodes_occurence.each([&](NodeOccurenceMap::Data* d) {
500 if (d->value() == 1) {
501 Node node = nodes_internal[d->key()];
502 // In parallel, only process the nodes that belong to us
503 if (node.owner() == rank) {
504 single_nodes.add(node);
505 //info() << "SINGLE NODE OWNER lid=" << d->key() << " " << ItemPrinter(node)
506 // << " coord=" << nodes_coord[node];
507 }
508 }
509 });
510 // Each sub-domain collects the coordinates of the nodes from the other sub-domains
511 Integer nb_single_node = single_nodes.size();
512 //info() << "NB SINGLE NODE= " << nb_single_node;
513 Integer total_nb_single_node = pm->reduce(Parallel::ReduceSum, nb_single_node);
514 Real3 total_global_normal;
515 {
516 Real all_min = 0.0;
517 Real all_max = 0.0;
518 Real all_sum = 0.0;
519 Int32 min_rank = -1;
520 Int32 max_rank = -1;
521 pm->computeMinMaxSum(nb_global_normal_used_node, all_min, all_max, all_sum, min_rank, max_rank);
522 Real3 buf;
523 if (max_rank == rank) {
524 // I am the one with the farthest point. I send it to the others.
525 buf = global_normal;
526 }
527 pm->broadcast(Real3ArrayView(1, &buf), max_rank);
528 total_global_normal = buf;
529 }
530
531 info() << "TOTAL SINGLE NODE= " << total_nb_single_node << " surface=" << face_group.name()
532 << " global_normal = " << total_global_normal;
533 if (total_nb_single_node < 2) {
534 ARCANE_FATAL("not enough nodes connected to only one face");
535 }
536 Real3UniqueArray coords(nb_single_node);
537 for (Integer i = 0; i < nb_single_node; ++i) {
538 coords[i] = nodes_coord[single_nodes[i]];
539 }
540 //Array<Real> all_nodes_coord_real;
541 UniqueArray<Real3> all_nodes_coord;
542 pm->allGatherVariable(coords, all_nodes_coord);
543 //info() << " ALL NODES COORD n=" << all_nodes_coord_real.size();
544 //Array<Real3> all_nodes_coord(total_nb_single_node);
545 Real3 barycentre = Real3::null();
546 for (Integer i = 0; i < total_nb_single_node; ++i) {
547 barycentre += all_nodes_coord[i];
548 }
549 barycentre /= (Real)total_nb_single_node;
550 if (total_nb_single_node == 2 && m_mesh->dimension() == 3) {
551 // We only have two nodes. We need at least a third one to determine
552 // the normal to the plane. For this, each processor searches for the node
553 // on the surface that is farthest from the barycenter of the two nodes already
554 // found. This farthest node will serve as the third point for the
555 // plane.
556 Real max_distance = 0.0;
557 Node farthest_node;
558 Real3 s0 = all_nodes_coord[0];
559 Real3 s1 = all_nodes_coord[1];
560 nodes_occurence.each([&](NodeOccurenceMap::Data* d) {
561 Node node = nodes_internal[d->key()];
562 Real3 coord = nodes_coord[node];
563 // Do not process the two nodes already found
564 if (math::isNearlyEqual(coord, s0))
565 return;
566 if (math::isNearlyEqual(coord, s1))
567 return;
568 Real distance = (coord - barycentre).squareNormL2();
569 if (distance > max_distance) {
570 Real3 normal = math::cross(coord - s0, s1 - s0);
571 // We only take the node if it is not aligned with the other two.
572 if (!math::isNearlyZero(normal.squareNormL2())) {
573 max_distance = distance;
574 farthest_node = node;
575 }
576 }
577 });
578
579 if (!farthest_node.null()) {
580 info() << " FARTHEST NODE= " << ItemPrinter(farthest_node) << " dist=" << max_distance;
581 }
582 {
583 Real3 farthest_coord = _broadcastFarthestNode(max_distance, farthest_node, nodes_coord);
584 info() << " FARTHEST NODE ALL coord=" << farthest_coord;
585 all_nodes_coord.add(farthest_coord);
586 }
587 }
588 // Sort the nodes so that the calculation does not depend on the order of
589 // parallel operations.
590 std::sort(std::begin(all_nodes_coord), std::end(all_nodes_coord));
591 Integer nb_final_node = all_nodes_coord.size();
592 Real3 full_normal = Real3::null();
593 info() << " NB FINAL NODE=" << nb_final_node;
594 for (Integer i = 0; i < nb_final_node; ++i)
595 info() << " NODE=" << all_nodes_coord[i];
596 if (m_mesh->dimension() == 2) {
597 if (nb_final_node != 2) {
598 ARCANE_FATAL("should have 2 border nodes in 2D mesh");
599 }
600 Real3 direction = all_nodes_coord[1] - all_nodes_coord[0];
601 full_normal.x = -direction.y;
602 full_normal.y = direction.x;
603 }
604 else if (m_mesh->dimension() == 3) {
605 //nb_final_node = 3; // We only take the first 3 points.
606 // NOTE: we could take all points, because if the first three are
607 // aligned, the normal will not be correct.
608 // If we take all points, we must ensure they are ordered
609 // such that the normal of three consecutive points is always
610 // in the same direction. This means if we have four points, for example,
611 // that these 4 points form a non-crossed quadrangle (not in the shape
612 // of a butterfly)
613 Real3 first_normal = math::vecMul(all_nodes_coord[2] - all_nodes_coord[0],
614 all_nodes_coord[1] - all_nodes_coord[0]);
615 for (Integer i = 0; i < nb_final_node; ++i) {
616 Real3 s0 = all_nodes_coord[i];
617 Real3 s1 = all_nodes_coord[(i + nb_final_node - 1) % nb_final_node];
618 Real3 s2 = all_nodes_coord[(i + 1) % nb_final_node];
619 Real3 normal = math::vecMul(s2 - s0, s1 - s0);
620 info() << " ADD NORMAL: " << normal;
621 full_normal += normal;
622 info() << " FULL: " << full_normal;
623 }
624 if (math::isNearlyZero(full_normal.squareNormL2()))
625 full_normal = first_normal;
626 }
627 else
628 ARCANE_FATAL("invalid mesh dimension (should be 2 or 3)");
629 Real a = full_normal.normL2();
630 if (math::isZero(a))
631 ARCANE_FATAL("invalid value for normal");
632 full_normal /= a;
633 Real b = total_global_normal.normL2();
634 Real dir = 0.0;
635 info() << " Normal is " << full_normal;
636 if (!math::isZero(b)) {
637 total_global_normal /= b;
638 dir = math::scaMul(full_normal, total_global_normal);
639 if (dir < 0.0)
640 full_normal = -full_normal;
641 }
642 full_normal = _round(full_normal);
643 info() << " Final normal is " << full_normal << " dir=" << dir;
644 return full_normal;
645}
646
647/*---------------------------------------------------------------------------*/
648/*---------------------------------------------------------------------------*/
656Real3 UnstructuredMeshUtilities::
657computeDirection(const NodeGroup& node_group,
658 const VariableNodeReal3& nodes_coord,
659 Real3* n1, Real3* n2)
660{
661 String func_name = "UnstructuredMeshUtilities::computeDirection";
662 IParallelMng* pm = m_mesh->parallelMng();
663 //Integer rank = pm->commRank();
664 //Integer nb_node_own = node_group.own().size();
665 //Integer total_nb_node_own = pm->reduce(Parallel::ReduceSum,nb_node_own);
666 Real3 barycentre = Real3::null();
667 ENUMERATE_NODE (inode, node_group.own()) {
668 barycentre += nodes_coord[inode];
669 }
670 Real r_barycentre[3];
671 r_barycentre[0] = barycentre.x;
672 r_barycentre[1] = barycentre.y;
673 r_barycentre[2] = barycentre.z;
674 RealArrayView rav(3, r_barycentre);
675 pm->reduce(Parallel::ReduceSum, rav);
676 barycentre = Real3(rav[0], rav[1], rav[2]);
677 debug() << " BARYCENTRE COMPUTE DIRECTION = " << barycentre;
678 pm->barrier();
679
680 // Determines the node farthest from the barycenter
681 Real3 first_boundary_coord;
682 {
683 Real max_distance = 0.0;
684 Node farthest_node;
685 ENUMERATE_NODE (inode, node_group.own()) {
686 Node node = *inode;
687 Real3 coord = nodes_coord[node];
688 Real distance = (coord - barycentre).squareNormL2();
689 if (distance > max_distance) {
690 max_distance = distance;
691 farthest_node = node;
692 }
693 }
694 first_boundary_coord = _broadcastFarthestNode(max_distance, farthest_node, nodes_coord);
695 if (n1)
696 *n1 = first_boundary_coord;
697 }
698 Real3 second_boundary_coord;
699 {
700 Real max_distance = 0.0;
701 Node farthest_node;
702 ENUMERATE_NODE (inode, node_group.own()) {
703 Node node = *inode;
704 Real3 coord = nodes_coord[node];
705 Real distance = (coord - first_boundary_coord).squareNormL2();
706 if (distance > max_distance) {
707 max_distance = distance;
708 farthest_node = node;
709 }
710 }
711 second_boundary_coord = _broadcastFarthestNode(max_distance, farthest_node, nodes_coord);
712 if (n2)
713 *n2 = second_boundary_coord;
714 }
715 Real3 direction = second_boundary_coord - first_boundary_coord;
716 Real norm = direction.normL2();
717 if (math::isZero(norm)) {
718 ARCANE_FATAL("Direction is null for group '{0}' first_coord={1} second_coord={2}",
719 node_group.name(), first_boundary_coord, second_boundary_coord);
720 }
721 return direction / norm;
722}
723
724/*---------------------------------------------------------------------------*/
725/*---------------------------------------------------------------------------*/
726
727Real3 UnstructuredMeshUtilities::
728_broadcastFarthestNode(Real distance, const Node& farthest_node,
729 const VariableNodeReal3& nodes_coord)
730{
731 String func_name = "UnstructuredMeshUtilities::_broadcastFarthestNode()";
732 IParallelMng* pm = m_mesh->parallelMng();
733 Integer rank = pm->commRank();
734
735 Real all_min_distance = 0.0;
736 Real all_max_distance = 0.0;
737 Real all_sum_distance = 0.0;
738 Int32 min_rank = -1;
739 Int32 max_rank = -1;
740 pm->computeMinMaxSum(distance, all_min_distance, all_max_distance, all_sum_distance, min_rank, max_rank);
741 Real3 buf;
742 if (!farthest_node.null())
743 debug() << " FARTHEST NODE myself coord=" << nodes_coord[farthest_node]
744 << " distance=" << distance
745 << " rank=" << max_rank
746 << " max_distance=" << all_max_distance;
747 else
748 debug() << " FARTHEST NODE myself coord=none"
749 << " distance=" << distance
750 << " rank=" << max_rank
751 << " max_distance=" << all_max_distance;
752
753 if (max_rank == rank) {
754 if (farthest_node.null())
755 ARCANE_FATAL("can not find farthest node");
756 // I am the one with the farthest point. I send it to the others.
757 buf = nodes_coord[farthest_node];
758 debug() << " I AM FARTHEST NODE ALL coord=" << buf;
759 }
760 pm->broadcast(Real3ArrayView(1, &buf), max_rank);
761 Real3 farthest_coord(buf);
762 debug() << " FARTHEST NODE ALL coord=" << farthest_coord
763 << " rank=" << max_rank
764 << " max_distance=" << all_max_distance;
765 return farthest_coord;
766}
767
768/*---------------------------------------------------------------------------*/
769/*---------------------------------------------------------------------------*/
770
771void UnstructuredMeshUtilities::
772computeAdjency(ItemPairGroup adjency_array, eItemKind link_kind, Integer nb_layer)
773{
774 m_compute_adjacency_functor->computeAdjacency(adjency_array, link_kind, nb_layer);
775}
776
777/*---------------------------------------------------------------------------*/
778/*---------------------------------------------------------------------------*/
779
780bool UnstructuredMeshUtilities::
781writeToFile(const String& file_name, const String& service_name)
782{
783 ServiceBuilder<IMeshWriter> sb(m_mesh->handle());
784 auto mesh_writer = sb.createReference(service_name, SB_AllowNull);
785
786 if (!mesh_writer) {
787 UniqueArray<String> available_names;
788 sb.getServicesNames(available_names);
789 warning() << String::format("The specified service '{0}' to write the mesh is not available."
790 " Valid names are {1}",
791 service_name, available_names);
792 return true;
793 }
794 mesh_writer->writeMeshToFile(m_mesh, file_name);
795 return false;
796}
797
798/*---------------------------------------------------------------------------*/
799/*---------------------------------------------------------------------------*/
800
801void UnstructuredMeshUtilities::
802partitionAndExchangeMeshWithReplication(IMeshPartitionerBase* partitioner,
803 bool initial_partition)
804{
805 IPrimaryMesh* primary_mesh = partitioner->primaryMesh();
806 IMesh* mesh = primary_mesh;
807 if (mesh != this->m_mesh)
808 throw ArgumentException(A_FUNCINFO, "partitioner->mesh() != this->m_mesh");
809
810 IParallelMng* pm = mesh->parallelMng();
811 ITimeStats* ts = pm->timeStats();
813 bool has_replication = pr->hasReplication();
814
815 // In replication mode, only the first replica performs the balancing.
816 // It must then send this mesh information to the others
817 // so that they have the same mesh.
818
819 info() << "Partition start date=" << platform::getCurrentDateTime();
820 if (pr->isMasterRank()) {
821 Timer::Action ts_action1(ts, "MeshPartition", true);
822 info() << "Partitioning the mesh (initial?=" << initial_partition << ")";
823 partitioner->partitionMesh(initial_partition);
824 }
825 else
826 info() << "Waiting for partition information from the master replica";
827
828 if (has_replication) {
829 pm->barrier();
830
831 // Checks that all families are the same.
832 mesh->checker()->checkValidReplication();
833
834 Int32 replica_master_rank = pr->masterReplicationRank();
835 IParallelMng* rep_pm = pr->replicaParallelMng();
836
837 // Only the master replica has the correct owners. It must update
838 // the others from this. To do this, we synchronize
839 // the owners of the cells and then update the other families
840 // from the cell owners.
841 {
842 Int32ArrayView owners = mesh->cellFamily()->itemsNewOwner().asArray();
843 rep_pm->broadcast(owners, replica_master_rank);
844 if (!pr->isMasterRank()) {
846 }
847 }
848 }
849 info() << "Partition end date=" << platform::getCurrentDateTime();
850 {
851 Timer::Action ts_action2(ts, "MeshExchange", true);
852 primary_mesh->exchangeItems();
853 }
854 info() << "Exchange end date=" << platform::getCurrentDateTime();
855 partitioner->notifyEndPartition();
856
857 // It must be compacted to return to the same situation as
858 // if we had just read a mesh directly (which performs a prepareForDump()
859 // when calling endAllocate()).
860 // We do this also in case of replication to avoid potential
861 // inconsistencies if later some replicas call this
862 // method and others do not.
863 // TODO: check if it should also be done in case of repartitioning.
864 if (initial_partition || has_replication)
865 mesh->prepareForDump();
866}
867
868/*---------------------------------------------------------------------------*/
869/*---------------------------------------------------------------------------*/
870
871void UnstructuredMeshUtilities::
872mergeNodes(Int32ConstArrayView nodes_local_id,
873 Int32ConstArrayView nodes_to_merge_local_id,
874 bool allow_non_corresponding_face)
875{
876 mesh::MeshNodeMerger merger(m_mesh);
877 merger.mergeNodes(nodes_local_id, nodes_to_merge_local_id, allow_non_corresponding_face);
878}
879
880/*---------------------------------------------------------------------------*/
881/*---------------------------------------------------------------------------*/
882
883void UnstructuredMeshUtilities::
884computeAndSetOwnersForNodes()
885{
886 mesh::ItemsOwnerBuilder owner_builder(m_mesh);
887 owner_builder.computeNodesOwner();
888}
889
890/*---------------------------------------------------------------------------*/
891/*---------------------------------------------------------------------------*/
892
893void UnstructuredMeshUtilities::
894computeAndSetOwnersForEdges()
895{
896 mesh::ItemsOwnerBuilder owner_builder(m_mesh);
897 owner_builder.computeEdgesOwner();
898}
899
900/*---------------------------------------------------------------------------*/
901/*---------------------------------------------------------------------------*/
902
903void UnstructuredMeshUtilities::
904computeAndSetOwnersForFaces()
905{
906 mesh::ItemsOwnerBuilder owner_builder(m_mesh);
907 owner_builder.computeFacesOwner();
908}
909
910/*---------------------------------------------------------------------------*/
911/*---------------------------------------------------------------------------*/
912namespace
913{
914 void _recomputeUniqueIds(IItemFamily* family)
915 {
916 SmallArray<Int64> unique_ids;
917 ENUMERATE_ (ItemWithNodes, iitem, family->allItems()) {
918 ItemWithNodes item = *iitem;
919 Int32 index = 0;
920 unique_ids.resize(item.nbNode());
921 for (Node node : item.nodes()) {
922 unique_ids[index] = node.uniqueId();
923 ++index;
924 }
925 Int64 new_uid = MeshUtils::generateHashUniqueId(unique_ids);
926 item.mutableItemBase().setUniqueId(new_uid);
927 }
929 }
930} // namespace
931
932/*---------------------------------------------------------------------------*/
933/*---------------------------------------------------------------------------*/
934
935void UnstructuredMeshUtilities::
936recomputeItemsUniqueIdFromNodesUniqueId()
937{
938 IMesh* mesh = m_mesh;
940 ITraceMng* tm = mesh->traceMng();
941
942 tm->info() << "Calling RecomputeItemsUniqueIdFromNodesUniqueId()";
943 // First indicate that the nodes have changed to potentially
944 // update the orientation of the faces.
945 mesh->nodeFamily()->notifyItemsUniqueIdChanged();
946 _recomputeUniqueIds(mesh->edgeFamily());
947 _recomputeUniqueIds(mesh->faceFamily());
948 _recomputeUniqueIds(mesh->cellFamily());
949}
950
951/*---------------------------------------------------------------------------*/
952/*---------------------------------------------------------------------------*/
953
954} // End namespace Arcane
955
956/*---------------------------------------------------------------------------*/
957/*---------------------------------------------------------------------------*/
#define ARCANE_CHECK_POINTER(ptr)
Macro returning the pointer ptr if it is not null or throwing an exception if it is null.
#define ARCANE_FATAL(...)
Macro throwing a FatalErrorException.
#define ENUMERATE_FACE(name, group)
Generic enumerator for a face group.
#define ENUMERATE_(type, name, group)
Generic enumerator for an entity group.
#define ENUMERATE_PARTICLE(name, group)
Generic enumerator for a particle group.
#define ENUMERATE_EDGE(name, group)
Generic enumerator for an edge group.
#define ENUMERATE_ITEM(name, group)
Generic enumerator for a node group.
#define ENUMERATE_NODE(name, group)
Generic enumerator for a node group.
Utility functions for the mesh.
bool reorderNodesOfFace(Int64ConstArrayView before_ids, Int64ArrayView after_ids)
Reorders the nodes of a face.
Integer size() const
Number of elements in the vector.
Modifiable view of an array of type T.
constexpr Integer size() const noexcept
Returns the size of the array.
void resize(Int64 s)
Changes the number of elements in the array to s.
void add(ConstReferenceType val)
Adds element val to the end of the array.
void reserve(Int64 new_capacity)
Reserves memory for new_capacity elements.
Cell of a mesh.
Definition Item.h:1300
Manages the retrieval of connectivity information.
ItemVectorView connectedItems(ItemLocalId item)
Returns the entities connected to item.
Constant view of an array of type T.
constexpr const_pointer data() const noexcept
Pointer to the allocated memory.
constexpr Integer size() const noexcept
Number of elements in the array.
Edge of a cell.
Definition Item.h:875
Face of a cell.
Definition Item.h:1032
Cell frontCell() const
Cell in front of the face (null cell if none).
Definition Item.h:1780
bool isSubDomainBoundary() const
Indicates if the face is on the subdomain boundary (i.e nbCell()==1).
Definition Item.h:1148
Cell boundaryCell() const
Cell associated with this boundary face (null cell if none).
Definition Item.h:1768
Hash table for associative arrays.
Interface of an entity family.
Definition IItemFamily.h:85
virtual void notifyItemsUniqueIdChanged()=0
Notifies that the unique IDs of the entities have been modified.
virtual ItemGroup allItems() const =0
Group of all entities.
virtual VariableItemInt32 & itemsNewOwner()=0
Variable containing the number of the new subdomain owning the entity.
Interface of a mesh partitioner.
virtual IPrimaryMesh * primaryMesh()=0
Associated mesh.
virtual void partitionMesh(bool initial_partition)=0
virtual void notifyEndPartition()=0
Notification when a re-partitioning finishes (after entity exchange).
Interface of the parallelism manager for a subdomain.
virtual void computeMinMaxSum(char val, char &min_val, char &max_val, char &sum_val, Int32 &min_rank, Int32 &max_rank)=0
Calculates the sum, min, and max of a value in one operation.
virtual Int32 commRank() const =0
Rank of this instance in the communicator.
virtual IParallelReplication * replication() const =0
Replication information.
virtual ITimeStats * timeStats() const =0
Associated statistics manager (can be null).
virtual void allGatherVariable(ConstArrayView< char > send_buf, Array< char > &recv_buf)=0
Performs an all-gather operation across all processors.
virtual char reduce(eReduceType rt, char v)=0
Performs a reduction of type rt on the real v and returns the value.
virtual void barrier()=0
Performs a barrier.
Brief information on parallel subdomain replication.
virtual bool hasReplication() const =0
Indicates if replication is active.
virtual IParallelMng * replicaParallelMng() const =0
Communicator associated with all replicas representing the same subdomain.
virtual Int32 masterReplicationRank() const =0
Rank in the master replication.
virtual bool isMasterRank() const =0
Indicates if this replication rank is the master.
virtual void exchangeItems()=0
Changes the owning subdomains of entities.
Interface managing execution time statistics.
Definition ITimeStats.h:44
virtual TraceMessage info()=0
Stream for an information message.
Exception when a fatal error has occurred.
const String & name() const
Group name.
Definition ItemGroup.h:81
ItemGroup own() const
Group equivalent to this one but containing only the local elements of the subdomain.
Definition ItemGroup.cc:182
Index of an Item in a variable.
Definition ItemLocalId.h:42
Table of entity lists.
Utility class for printing information about an entity.
Definition ItemPrinter.h:35
Type of an entity (Item).
Definition ItemTypeId.h:33
Integer nbLocalNode() const
Number of nodes of the entity.
Mesh entity type manager.
Definition ItemTypeMng.h:64
ItemTypeInfo * typeFromId(Integer id) const
Type corresponding to the number id.
Mesh element based on nodes (Edge,Face,Cell).
Definition Item.h:773
Node node(Int32 i) const
i-th node of the entity
Definition Item.h:840
NodeConnectedListViewType nodes() const
List of nodes of the entity.
Definition Item.h:843
Int32 nbNode() const
Number of nodes of the entity.
Definition Item.h:837
NodeLocalIdView nodeIds() const
List of nodes of the entity.
Definition Item.h:846
impl::MutableItemBase mutableItemBase() const
Mutable internal part of the entity.
Definition Item.h:394
constexpr Int32 localId() const
Local identifier of the entity in the processor subdomain.
Definition Item.h:233
Int32 owner() const
Owner subdomain number of the entity.
Definition Item.h:252
constexpr bool null() const
true if the entity is null (i.e. not connected to the mesh)
Definition Item.h:230
constexpr bool isOwn() const
true if the entity belongs to the subdomain
Definition Item.h:267
Class to convert a NodeLocalId to an edge.
Node of a mesh.
Definition Item.h:598
Utility class to reorder the nodes of an entity.
Particle.
Definition Item.h:1529
Cell cell() const
Cell to which the particle belongs. You must call setCell() before calling this function....
Definition Item.h:1606
Class managing a 3-dimensional real vector.
__host__ __device__ Real normL2() const
Returns the L2 norm of the triplet $ .
Definition MathReal3.h:100
constexpr __host__ __device__ Real squareNormL2() const
Returns the square of the L2 norm of the triplet $ .
Utility class for instantiating a service of a given interface.
Ref< InterfaceType > createReference(const String &name, eServiceBuilderProperties properties=SB_None)
Creates an instance implementing the InterfaceType interface.
void getServicesNames(Array< String > &names) const
Fills names with the names of services available to instantiate this interface.
1D data array with pre-allocated stack buffer.
Unicode character string constructor.
String toString() const
Returns the constructed character string.
Positions the name of the currently executing action.
Definition Timer.h:119
TraceMessageDbg debug(Trace::eDebugLevel=Trace::Medium) const
Flow for a debug message.
TraceMessage info() const
Flow for an information message.
TraceMessage warning() const
Flow for a warning message.
1D data vector with value semantics (STL style).
void changeOwnersFromCells() override
Positions the new owners of nodes, edges, and faces based on the cells.
Generic class for calculating entity owners.
Merging nodes of a mesh.
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)
Vector cross product of u by v in .
Definition MathUtils.h:48
__host__ __device__ Real scaMul(Real2 u, Real2 v)
Dot product of u by v in .
Definition MathUtils.h:112
__host__ __device__ Real3 cross(Real3 v1, Real3 v2)
Cross product of two 3-component vectors.
Definition MathUtils.h:759
ItemGroupT< Face > FaceGroup
Group of faces.
Definition ItemTypes.h:179
ItemGroupT< Node > NodeGroup
Group of nodes.
Definition ItemTypes.h:168
MeshVariableScalarRefT< Node, Real3 > VariableNodeReal3
Coordinate type quantity at node.
ItemVariableScalarRefT< Int32 > VariableItemInt32
32-bit integer type quantity
bool isZero(const BuiltInProxy< _Type > &a)
Tests if a value is exactly equal to zero.
constexpr __host__ __device__ bool isNearlyEqual(const Type &a, const Type &b)
Tests if two values are approximately equal. For integer types, this function is equivalent to IsEqua...
Definition TypeEqual.h:228
String getCurrentDateTime()
Current date and time in ISO 8601 format.
-- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature --
ArrayView< Int64 > Int64ArrayView
C equivalent of a 1D array of 64-bit integers.
Definition UtilsTypes.h:445
@ SB_AllowNull
Allows the service to be absent.
UniqueArray< Int64 > Int64UniqueArray
Dynamic 1D array of 64-bit integers.
Definition UtilsTypes.h:333
std::int64_t Int64
Signed integer type of 64 bits.
ArrayView< Real3 > Real3ArrayView
C equivalent of a 1D array of Real3.
Definition UtilsTypes.h:461
Int32 Integer
Type representing an integer.
UniqueArray< Real3 > Real3UniqueArray
Dynamic 1D array of rank 3 vectors.
Definition UtilsTypes.h:357
ConstArrayView< Int32 > Int32ConstArrayView
C equivalent of a 1D array of 32-bit integers.
Definition UtilsTypes.h:476
ConstArrayView< ItemInternal * > ItemInternalList
Type of the internal list of entities.
Definition ItemTypes.h:466
ConstArrayView< Int64 > Int64ConstArrayView
C equivalent of a 1D array of 64-bit integers.
Definition UtilsTypes.h:474
ArrayView< Int32 > Int32ArrayView
C equivalent of a 1D array of 32-bit integers.
Definition UtilsTypes.h:447
eItemKind
Mesh entity type.
@ IK_Particle
Particle mesh entity.
@ IK_Node
Node mesh entity.
@ IK_Cell
Cell mesh entity.
@ IK_Face
Face mesh entity.
@ IK_DoF
Degree of Freedom mesh entity.
@ IK_Edge
Edge mesh entity.
double Real
Type representing a real number.
ConstArrayView< Integer > IntegerConstArrayView
C equivalent of a 1D array of integers.
Definition UtilsTypes.h:480
ArrayView< Real > RealArrayView
C equivalent of a 1D array of reals.
Definition UtilsTypes.h:453
std::int32_t Int32
Signed integer type of 32 bits.
Real y
second component of the triplet
Real z
third component of the triplet
Real x
first component of the triplet