diff --git a/arcane/src/arcane/core/IMeshSection.h b/arcane/src/arcane/core/IMeshSection.h
new file mode 100644
index 000000000..b06f9431b
--- /dev/null
+++ b/arcane/src/arcane/core/IMeshSection.h
@@ -0,0 +1,105 @@
+// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
+//-----------------------------------------------------------------------------
+// Copyright 2000-2026 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com)
+// See the top-level COPYRIGHT file for details.
+// SPDX-License-Identifier: Apache-2.0
+//-----------------------------------------------------------------------------
+/*---------------------------------------------------------------------------*/
+/* IMeshSection.h (C) 2000-2026 */
+/* */
+/* Service interface allowing the creation of a mesh with a section of */
+/* another mesh. */
+/*---------------------------------------------------------------------------*/
+#ifndef ARCANE_CORE_IMESHSECTION_H
+#define ARCANE_CORE_IMESHSECTION_H
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+#include "arcane/core/ArcaneTypes.h"
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+namespace Arcane
+{
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+class VariableCollection;
+class MeshHandle;
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+/*!
+ * \brief Service interface allowing the creation of a mesh with a section of
+ * another mesh.
+ */
+class ARCANE_CORE_EXPORT IMeshSection
+{
+ public:
+
+ //! Releases resources
+ virtual ~IMeshSection() = default;
+
+ public:
+
+ /*!
+ * \brief Method allowing to add a plane to the cut service. The use of
+ * these planes depends on the service.
+ *
+ * \param p0 Point of the plane.
+ * \param normal Normal of the plane.
+ */
+ virtual void addPlane(const Real3& p0, const Real3& normal) = 0;
+
+ /*!
+ * \brief Method allowing to add a set of variables to copy on the
+ * new mesh.
+ *
+ * \param variables A set of variable on the original mesh.
+ */
+ virtual void setVariables(VariableCollection variables) = 0;
+
+ /*!
+ * \brief Method allowing to get a set of variables copied on the new
+ * mesh.
+ *
+ * \return A set of variables on the cloned mesh.
+ */
+ virtual VariableCollection variables() = 0;
+
+ /*!
+ * \brief Method allowing to set a unique id allowing to create multiple
+ * section services for a mesh.
+ *
+ * \param unique_id Mesh unique id to apply.
+ */
+ virtual void setServiceMeshUniqueId(Int32 unique_id) = 0;
+
+ /*!
+ * \brief Method allowing to update the mesh section with all planes.
+ *
+ * If a previous call has edited the mesh, all the cells will be destroyed
+ * before update.
+ */
+ virtual void updateSection() = 0;
+
+ /*!
+ * \brief Méthod allowing to get the mesh section.
+ *
+ * \return The mesh section.
+ */
+ virtual MeshHandle meshSection() = 0;
+};
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+} // namespace Arcane
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+#endif
diff --git a/arcane/src/arcane/core/srcs.cmake b/arcane/src/arcane/core/srcs.cmake
index efef2268d..b9fa187e0 100644
--- a/arcane/src/arcane/core/srcs.cmake
+++ b/arcane/src/arcane/core/srcs.cmake
@@ -248,6 +248,7 @@ set(ARCANE_ORIGINAL_SOURCES
IMeshPartitionConstraintMng.h
IMeshPartitioner.h
IMeshPartitionerBase.h
+ IMeshSection.h
IMeshStats.h
IMeshSubMeshTransition.h
IMeshUniqueIdMng.h
diff --git a/arcane/src/arcane/std/MeshCut.axl b/arcane/src/arcane/std/MeshCut.axl
new file mode 100644
index 000000000..2d127bf02
--- /dev/null
+++ b/arcane/src/arcane/std/MeshCut.axl
@@ -0,0 +1,21 @@
+
+
+
+
+ Service allowing the creation of a mesh with a cut of another mesh.
+
+
+
+
+
+
+
+
+
+
+ UniqueID of service mesh. Useful if you need to create multiple instances of this service for a mesh.
+
+
+
+
+
diff --git a/arcane/src/arcane/std/MeshCutService.cc b/arcane/src/arcane/std/MeshCutService.cc
new file mode 100644
index 000000000..7e3888b42
--- /dev/null
+++ b/arcane/src/arcane/std/MeshCutService.cc
@@ -0,0 +1,2088 @@
+// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
+//-----------------------------------------------------------------------------
+// Copyright 2000-2026 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com)
+// See the top-level COPYRIGHT file for details.
+// SPDX-License-Identifier: Apache-2.0
+//-----------------------------------------------------------------------------
+/*---------------------------------------------------------------------------*/
+/* MeshCutService.cc (C) 2000-2026 */
+/* */
+/* Service allowing the creation of a mesh with a cut of another mesh. */
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+#include "arcane/utils/StringBuilder.h"
+
+#include "arcane/core/IGhostLayerMng.h"
+#include "arcane/core/IItemFamily.h"
+#include "arcane/core/IMesh.h"
+#include "arcane/core/IMeshFactoryMng.h"
+#include "arcane/core/IMeshMng.h"
+#include "arcane/core/IMeshModifier.h"
+#include "arcane/core/IMeshSection.h"
+#include "arcane/core/IPrimaryMesh.h"
+#include "arcane/core/MeshBuildInfo.h"
+
+#include "arcane/std/MeshCut_axl.h"
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+namespace Arcane
+{
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+namespace
+{
+/*!
+ * \brief Noeud sur arête.
+ * Représente le noeud à l'intersection entre une arête du maillage 3D et
+ * un plan.
+ * Si les deux noeuds de la struct sont identiques, c'est qu'il y a un
+ * noeud du maillage 3D qui est sur le plan.
+ */
+struct NodeOnEdge
+{
+ NodeOnEdge(const Node first_node, const Node second_node)
+ {
+ const Int64 a = first_node.uniqueId();
+ const Int64 b = second_node.uniqueId();
+ if (a < b) {
+ m_node0 = first_node;
+ m_node1 = second_node;
+ m_uid_node0 = a;
+ m_uid_node1 = b;
+ }
+ else {
+ m_node0 = second_node;
+ m_node1 = first_node;
+ m_uid_node0 = b;
+ m_uid_node1 = a;
+ }
+ }
+
+ NodeOnEdge(Int64 first_node, Int64 second_node)
+ {
+ if (first_node < second_node) {
+ m_uid_node0 = first_node;
+ m_uid_node1 = second_node;
+ }
+ else {
+ m_uid_node0 = second_node;
+ m_uid_node1 = first_node;
+ }
+ }
+
+ NodeOnEdge() = default;
+
+ bool operator<(const NodeOnEdge& other) const
+ {
+ if (m_uid_node0 != other.m_uid_node0) {
+ return m_uid_node0 < other.m_uid_node0;
+ }
+ return m_uid_node1 < other.m_uid_node1;
+ }
+
+ bool operator==(const NodeOnEdge& other) const
+ {
+ return m_uid_node0 == other.m_uid_node0 && m_uid_node1 == other.m_uid_node1;
+ }
+
+ Node m_node0;
+ Node m_node1;
+
+ Int64 m_uid_node0 = -1;
+ Int64 m_uid_node1 = -1;
+
+ Int64 m_uid_new_node = -1;
+ Int32 m_owner_new_node = -1;
+};
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+/*!
+ * \brief Face du maillage 2D issue de deux noeuds d'arêtes.
+ */
+struct FaceLite
+{
+ FaceLite(const Ref& node0, const Ref& node1)
+ : m_node0(node0->m_uid_node0 < node1->m_uid_node0 ? node0 : node1)
+ , m_node1(node0->m_uid_node0 < node1->m_uid_node0 ? node1 : node0)
+ {
+ const Int64 node00 = node0->m_uid_node0;
+ const Int64 node01 = node0->m_uid_node1;
+ const Int64 node10 = node1->m_uid_node0;
+ const Int64 node11 = node1->m_uid_node1;
+
+ if (node00 < node10) {
+ m_node0 = node0;
+ m_node1 = node1;
+ }
+ else if (node00 > node10) {
+ m_node0 = node1;
+ m_node1 = node0;
+ }
+ else {
+ if (node01 == node11) {
+ ARCANE_FATAL("Impossible (normalement...)");
+ }
+ if (node01 < node11) {
+ m_node0 = node0;
+ m_node1 = node1;
+ }
+ else {
+ m_node0 = node1;
+ m_node1 = node0;
+ }
+ }
+ }
+
+ bool operator<(const FaceLite& other) const
+ {
+ return m_node0->operator<(*(other.m_node0.get())) || (m_node0->operator==(*(other.m_node0.get())) && m_node1->operator<(*(other.m_node1.get())));
+ }
+
+ bool operator==(const FaceLite& other) const
+ {
+ return m_node0->operator==(*(other.m_node0.get())) && m_node1->operator==(*(other.m_node1.get()));
+ }
+
+ Ref m_node0;
+ Ref m_node1;
+
+ Int64 m_uid_new_face = -1;
+ Int32 m_owner_new_face = -1;
+};
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+/*!
+ * \brief Position d'un noeud d'arête.
+ */
+struct NodeIntersection
+{
+ NodeIntersection(const Node first_node, const Node second_node, const Real3& intersection_pos)
+ : m_new_node(makeRef(new NodeOnEdge(first_node, second_node)))
+ , m_intersection_pos(intersection_pos)
+ {}
+
+ NodeIntersection(Int64 first_node, Int64 second_node, const Real3& intersection_pos)
+ : m_new_node(makeRef(new NodeOnEdge(first_node, second_node)))
+ , m_intersection_pos(intersection_pos)
+ {}
+
+ NodeIntersection() = default;
+
+ bool operator<(const NodeIntersection& other) const
+ {
+ return m_new_node->operator<(*(other.m_new_node.get()));
+ }
+
+ bool operator==(const NodeIntersection& other) const
+ {
+ return m_new_node->operator==(*(other.m_new_node.get()));
+ }
+
+ Ref m_new_node;
+ Real3 m_intersection_pos{ -1 };
+};
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+struct UnknownNodeOrFace
+{
+ UnknownNodeOrFace(Int64 node0_uid, Int64 node1_uid, Int32 who) : m_node0_uid(node0_uid), m_node1_uid(node1_uid), m_who(who){}
+ bool null() const {return m_who == -1;}
+ bool operator==(const UnknownNodeOrFace& other) const{return m_node0_uid == other.m_node0_uid && m_node1_uid == other.m_node1_uid;}
+ Int64 m_node0_uid;
+ Int64 m_node1_uid;
+ Int32 m_who;
+};
+} // namespace
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+/*!
+ * \brief Service allowing the creation of a mesh with a cut of another mesh.
+ *
+ * For each plan, a cut will be realized. All cuts will be stored in the mesh
+ * created by this service. To get this mesh, you can call \a meshSection()
+ * method.
+ */
+class MeshCutService
+: public ArcaneMeshCutObject
+{
+ public:
+
+ explicit MeshCutService(const ServiceBuildInfo& sbi)
+ : ArcaneMeshCutObject(sbi)
+ , m_creation_type(sbi.creationType())
+ {}
+
+ ~MeshCutService() override = default;
+
+ public:
+
+ void addPlane(const Real3& p0, const Real3& normal) override;
+
+ void setVariables(VariableCollection variables) override;
+
+ void setServiceMeshUniqueId(Int32 unique_id) override;
+
+ VariableCollection variables() override { return {}; }
+
+ void updateSection() override;
+
+ MeshHandle meshSection() override
+ {
+ return m_cloned_mesh->handle();
+ }
+
+ private:
+
+ void _createMesh();
+ void _createNodesAndCells(Int32 plan_pos, Int32& sd_nb_node, UniqueArray& new_nodes, Int32 ajust_node_pos, Int32& sd_nb_cell, UniqueArray& new_cells, Int32& sd_nb_face, UniqueArray& new_faces);
+ void _makeUniqueCellUID(Int32 sd_nb_cell, UniqueArray& new_cells, UniqueArray& new_nodes);
+
+ void _fillNodeUID(Int32& sd_nb_node, UniqueArray& new_nodes);
+ Int32 _makeUniqueNodeUID(Int32 sd_nb_node, UniqueArray& new_nodes);
+ void _fillFaceUID(Int32& sd_nb_face, UniqueArray& new_faces);
+ Int32 _makeUniqueFaceUID(Int32 sd_nb_face, UniqueArray& new_faces);
+ void _compute();
+
+ std::optional _find(Span new_faces, Int64 node_uid0, Int64 node_uid1);
+
+ void _addFaces(UniqueArray& new_faces) const;
+ void _addCells(UniqueArray& new_cells);
+ void _setCoordNodesAndOwner(UniqueArray& new_nodes);
+ void _setFacesOwner(UniqueArray& new_faces);
+
+ private:
+
+ eServiceType m_creation_type;
+ // VariableCollection m_variables_ori;
+ IPrimaryMesh* m_cloned_mesh = nullptr;
+ UniqueArray> m_plans;
+ Int32 m_mesh_uid = -1;
+};
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+ARCANE_REGISTER_SERVICE_MESHCUT(MeshCut, MeshCutService);
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+void MeshCutService::
+addPlane(const Real3& p0, const Real3& normal)
+{
+ m_plans.add({ p0, math::normalizeReal3(normal) });
+}
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+void MeshCutService::
+setVariables(VariableCollection variables)
+{
+ ARCANE_UNUSED(variables);
+ ARCANE_NOT_YET_IMPLEMENTED("Not supported yet");
+ // m_variables_ori = variables;
+}
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+void MeshCutService::
+setServiceMeshUniqueId(Int32 unique_id)
+{
+ m_mesh_uid = unique_id;
+}
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+void MeshCutService::
+updateSection()
+{
+ if (mesh()->ghostLayerMng()->nbGhostLayer() < 1) {
+ ARCANE_FATAL("A ghost layer is required for this service");
+ }
+ _createMesh();
+ _compute();
+}
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+void MeshCutService::
+_createMesh()
+{
+ if (m_cloned_mesh != nullptr) {
+ m_cloned_mesh->modifier()->clearItems();
+ }
+
+ if (mesh()->dimension() != 3) {
+ ARCANE_FATAL("Only 3D meshes are supported");
+ }
+
+ IMeshMng* mm = subDomain()->meshMng();
+
+ if (m_mesh_uid == -1) {
+ if (m_creation_type == ST_CaseOption) {
+ m_mesh_uid = options()->getUniqueIdServiceMesh();
+ }
+ else {
+ m_mesh_uid = 0;
+ }
+ }
+
+ String service_mesh_name = mesh()->name() + "_MeshCut" + m_mesh_uid;
+
+ MeshHandle* mesh_handle = mm->findMeshHandle(service_mesh_name, false);
+
+ if (mesh_handle == nullptr) {
+ IParallelMng* pm = subDomain()->parallelMng();
+ MeshBuildInfo mbi(service_mesh_name);
+ // auto mesh_kind = mbi.meshKind();
+ // mesh_kind.setMeshDimensionKind(eMeshCellDimensionKind::NonManifold);
+ // mbi.addMeshKind(mesh_kind);
+ mbi.addParallelMng(makeRef(pm));
+ m_cloned_mesh = mm->meshFactoryMng()->createMesh(mbi);
+ m_cloned_mesh->modifier()->setDynamic(true);
+ m_cloned_mesh->setDimension(2);
+ m_cloned_mesh->endAllocate();
+ }
+ else {
+ m_cloned_mesh = mesh_handle->mesh()->toPrimaryMesh();
+ m_cloned_mesh->modifier()->clearItems();
+ }
+}
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+void MeshCutService::
+_createNodesAndCells(Int32 plan_pos, Int32& sd_nb_node, UniqueArray& new_nodes, Int32 ajust_node_pos, Int32& sd_nb_cell, UniqueArray& new_cells, Int32& sd_nb_face, UniqueArray& new_faces)
+{
+ auto [p0, normal] = m_plans[plan_pos];
+
+ const Int32 mesh_dim = mesh()->dimension();
+
+ VariableNodeReal3& node_coord = mesh()->nodesCoordinates();
+ VariableNodeReal node_dist(VariableBuildInfo(mesh(), "NodeDist"));
+
+ UniqueArray face_already_computed;
+
+ // Calcul de la distance signée de chaque noeud par rapport au plan de coupe.
+ ENUMERATE_ (Node, inode, allNodes()) {
+ Real d = math::dot({ node_coord[inode] - p0 }, normal);
+ node_dist[inode] = math::isNearlyZeroWithEpsilon(d, 1e-10) ? 0 : d;
+ }
+
+ // Tableau qui contiendra tous les noeuds d'une future maille.
+ // Une maille 3D, si elle est coupée par le plan, donnera forcément une
+ // maille 2D. Les noeuds de cette maille seront stockés dans ce tableau,
+ // ainsi que, pour chaque noeud, sa position et l'arête dont il est issu
+ // (pour éviter les doublons).
+ UniqueArray point_coords_tmp;
+
+ ENUMERATE_ (Cell, icell, ownCells()) {
+ Cell cell = *icell;
+
+ bool has_face_on_plane = false;
+ {
+ bool cell_useful = true;
+ Int32 nb_node_on_plane = 0;
+
+ // On regarde si la maille traverse le plan ou si des noeuds sont sur le
+ // plan.
+ {
+ bool has_neg = false;
+ bool has_pos = false;
+ for (Node node : cell.nodes()) {
+ Real d = node_dist[node];
+ if (d < 0)
+ has_neg = true;
+ else if (d > 0)
+ has_pos = true;
+ else {
+ nb_node_on_plane++;
+ }
+ }
+
+ // Tous les noeuds sont du même coté du plan.
+ if (!(has_neg && has_pos))
+ cell_useful = false;
+ }
+
+ // Si le nombre de noeuds sur le plan correspond au nombre de noeuds
+ // minimum d'une face, il s'agit peut-être d'une face confondue au plan.
+ // Dans ce cas, il est nécessaire de faire un traitement spécial pour
+ // éviter un doublon de mailles dans le maillage final.
+ if (nb_node_on_plane >= mesh_dim) {
+ for (Face face : cell.faces()) {
+ // Pour éviter que deux processus créent la même maille.
+ if (!face.isOwn())
+ continue;
+ if (face.nbNode() != nb_node_on_plane)
+ continue;
+
+ bool has_egal = true;
+ for (Node node : face.nodes()) {
+ if (node_dist[node] != 0) {
+ has_egal = false;
+ break;
+ }
+ }
+
+ // On a trouvé la face confondue.
+ if (has_egal) {
+
+ // Si elle a déjà été traitée, on passe pour éviter un doublon.
+ if (face_already_computed.contains(face.localId())) {
+ cell_useful = false;
+ break;
+ }
+
+ // On sait que la face est confondue au plan et que la future
+ // maille créée grâce à cette face n'existe pas encore.
+ // On peut donc directement ajouter les noeuds de la face dans la
+ // liste des noeuds de la future maille.
+ face_already_computed.add(face.localId());
+ for (Node node : face.nodes()) {
+ auto ni = NodeIntersection{ node, node, node_coord[node] };
+ point_coords_tmp.add(ni);
+ }
+ has_face_on_plane = true;
+ cell_useful = true;
+ break;
+ }
+ }
+ }
+
+ if (!cell_useful)
+ continue;
+ }
+
+ if (!has_face_on_plane) {
+ // Tableaux définissant les arêtes pour chaque type d'élément.
+ // Chaque arête = [n0, n1] indices noeuds locaux de maille.
+ //
+ static constexpr Integer edges_tetraedron4[6][2] = {
+ { 0, 1 }, { 0, 2 }, { 0, 3 }, { 1, 2 }, { 1, 3 }, { 2, 3 }
+ };
+ static constexpr Integer edges_pyramid5[8][2] = {
+ { 0, 1 }, { 1, 2 }, { 2, 3 }, { 3, 0 }, { 0, 4 }, { 1, 4 }, { 2, 4 }, { 3, 4 }
+ };
+ static constexpr Integer edges_pentaedron6[9][2] = {
+ { 0, 1 }, { 1, 2 }, { 2, 0 }, { 0, 3 }, { 1, 4 }, { 2, 5 }, { 3, 4 }, { 4, 5 }, { 5, 3 }
+ };
+ static constexpr Integer edges_hexaedron8[12][2] = {
+ { 0, 1 }, { 1, 2 }, { 2, 3 }, { 3, 0 }, { 0, 4 }, { 1, 5 }, { 2, 6 }, { 3, 7 }, { 4, 5 }, { 5, 6 }, { 6, 7 }, { 7, 4 }
+ };
+
+ Integer nb_edges = 0;
+ const Integer(*edge_def)[2] = nullptr;
+
+ auto type = cell.itemTypeId();
+
+ if (type == ITI_Tetraedron4) {
+ nb_edges = 6;
+ edge_def = edges_tetraedron4;
+ }
+ else if (type == ITI_Pyramid5) {
+ nb_edges = 8;
+ edge_def = edges_pyramid5;
+ }
+ else if (type == ITI_Pentaedron6) {
+ nb_edges = 9;
+ edge_def = edges_pentaedron6;
+ }
+ else if (type == ITI_Hexaedron8) {
+ nb_edges = 12;
+ edge_def = edges_hexaedron8;
+ }
+
+ else {
+ ARCANE_FATAL("Cell type not supported -- type: {0}", type);
+ }
+
+ // Itère sur toutes les arêtes de la maille.
+ for (Integer i = 0; i < nb_edges; ++i) {
+ Node node0 = cell.node(edge_def[i][0]);
+ Node node1 = cell.node(edge_def[i][1]);
+
+ bool need_compute_intersection = true;
+
+ // Si le noeud 0 est sur le plan.
+ if (node_dist[node0] == 0) {
+ const Real3 p = node_coord[node0];
+
+ auto ni = NodeIntersection{ node0, node0, p };
+
+ if (!point_coords_tmp.contains(ni)) {
+ point_coords_tmp.add(ni);
+ }
+ need_compute_intersection = false;
+ }
+
+ // Si le noeud 1 est sur le plan.
+ if (node_dist[node1] == 0) {
+ const Real3 p = node_coord[node1];
+
+ auto ni = NodeIntersection{ node1, node1, p };
+
+ if (!point_coords_tmp.contains(ni)) {
+ point_coords_tmp.add(ni);
+ }
+ need_compute_intersection = false;
+ }
+
+ // Si l'arrête passe à travers le plan.
+ if (need_compute_intersection && node_dist[node0] * node_dist[node1] < 0) {
+
+ // Paramètre d'interpolation t dans [0,1] pour le point d'intersection
+ // le long de l'arête de node0 à node1.
+ Real t = std::abs(node_dist[node0]) / (std::abs(node_dist[node0]) + std::abs(node_dist[node1]));
+
+ // Calcul du point d'intersection par interpolation linéaire.
+ Real3 p;
+ p.x = node_coord[node0].x + t * (node_coord[node1].x - node_coord[node0].x);
+ p.y = node_coord[node0].y + t * (node_coord[node1].y - node_coord[node0].y);
+ p.z = node_coord[node0].z + t * (node_coord[node1].z - node_coord[node0].z);
+
+ auto ni = NodeIntersection{ node0, node1, p };
+
+ if (!point_coords_tmp.contains(ni)) {
+ point_coords_tmp.add(ni);
+ }
+ }
+ }
+ }
+
+ // Si le nombre de futurs noeuds est suffisant pour en faire une maille.
+ if (point_coords_tmp.size() >= mesh_dim) {
+
+ // Type cell.
+ if (point_coords_tmp.size() > 6)
+ ARCANE_FATAL("Pas implem : {0}", point_coords_tmp.size());
+
+ new_cells.add(point_coords_tmp.size());
+ new_cells.add(sd_nb_cell);
+
+ {
+ // Calcul du barycentre de tous les points d'intersection.
+ Real3 bary{ 0 };
+ for (const auto& node : point_coords_tmp) {
+ bary += node.m_intersection_pos;
+ }
+ bary /= point_coords_tmp.size();
+
+ // On choisit un vecteur de référence arbitraire non parallèle au normal.
+ // Si normal.x est grand (proche de l'axe X), utiliser l'axe Y ; sinon utiliser l'axe X.
+ const Real3 arbitrary = (std::abs(normal.x) > 0.9) ? Real3{ 0.0, 1.0, 0.0 } : Real3{ 1.0, 0.0, 0.0 };
+ // u et v forment une base orthonormale du plan de coupe.
+ // Ils sont perpendiculaires au normal et l'un à l'autre.
+ const Real3 u = math::normalizedCrossProduct3(arbitrary, normal);
+ const Real3 v = math::normalizedCrossProduct3(normal, u);
+
+ // On trie les points d'intersection par angle polaire autour du barycentre.
+ // Cela garantit que le polygone résultant est correctement ordonné (sens inverse des aiguilles d'une montre).
+ UniqueArray indices;
+ indices.reserve(point_coords_tmp.size());
+ for (Int64 i = 0; i < point_coords_tmp.size(); ++i) {
+ indices.add(i);
+ }
+
+ std::sort(indices.begin(), indices.end(),
+ [&](const Int64 ia, const Int64 ib) {
+ const Real3& pa = point_coords_tmp[ia].m_intersection_pos;
+ const Real3& pb = point_coords_tmp[ib].m_intersection_pos;
+
+ // Vecteurs allant du barycentre vers chaque point.
+ const Real3 va{ pa - bary };
+ const Real3 vb{ pb - bary };
+
+ // Projeter sur la base 2D du plan (u, v).
+ const Real a_x = math::dot(va, u);
+ const Real a_y = math::dot(va, v);
+
+ const Real b_x = math::dot(vb, u);
+ const Real b_y = math::dot(vb, v);
+
+ // Comparer les angles en utilisant atan2.
+ const Real angle_a = std::atan2(a_y, a_x);
+ const Real angle_b = std::atan2(b_y, b_x);
+
+ return angle_a < angle_b;
+ });
+
+ // On crée la nouvelle maille.
+ // On en profite pour modifier le tableau "indices" :
+ // - avant la boucle, ce tableau contient les indices de positions de
+ // noeuds dans le tableau point_coords_tmp.
+ // - après la boucle, ce tableau contiendra les indices de positions
+ // de ces mêmes noeuds mais dans le tableau "global" new_nodes.
+ for (Int64& idx : indices) {
+ // On regarde si le noeud est déjà présent dans le tableau "global".
+ std::optional pos = new_nodes.span().findFirst(point_coords_tmp[idx]);
+ if (pos) {
+ // Attention : on ajoute la position du noeud dans le tableau
+ // "global", pas le UID de celui-ci ! En effet, on ne le connait
+ // pas encore, ce sera ajusté dans la méthode
+ // "_makeUniqueCellUID()".
+ new_cells.add(static_cast(pos.value()) + ajust_node_pos);
+ idx = pos.value();
+ }
+ // S'il n'est pas présent, on le crée.
+ else {
+ NodeIntersection& elem = point_coords_tmp[idx];
+
+ // Si les deux noeuds ont le même proprio, on sait que le nouveau noeud aura le même proprio.
+ if (elem.m_new_node->m_node0.owner() == elem.m_new_node->m_node1.owner()) {
+ elem.m_new_node->m_owner_new_node = elem.m_new_node->m_node0.owner();
+ // Si les deux noeuds sont à nous, on est sûr d'être le
+ // propriétaire du nouveau noeud. C'est donc à nous de donner le
+ // UniqueID (unique pour ce processus, la correction sera faite
+ // plus tard, quand on saura le nombre de noeuds de chaque
+ // processus).
+ if (elem.m_new_node->m_node0.isOwn()) {
+ elem.m_new_node->m_uid_new_node = sd_nb_node++;
+ }
+ else {
+ // -2 = nécessite d'être ajouté plus tard.
+ elem.m_new_node->m_uid_new_node = -2;
+ }
+ }
+ new_cells.add(new_nodes.size() + ajust_node_pos);
+ idx = new_nodes.size();
+ new_nodes.add(elem);
+ }
+ }
+
+ // Maintenant, on ajoute les faces.
+ Int64 idxm1 = indices[indices.size() - 1];
+ for (Int64 idx : indices) {
+ FaceLite fl(new_nodes[idx].m_new_node, new_nodes[idxm1].m_new_node);
+ if (!new_faces.contains(fl)) {
+ // info() << "Add face"
+ // << " -- N00 " << fl.m_node0->m_uid_node0
+ // << " -- N01 " << fl.m_node0->m_uid_node1
+ // << " -- N10 " << fl.m_node1->m_uid_node0
+ // << " -- N11 " << fl.m_node1->m_uid_node1;
+
+ // Même principe qu'avec les noeuds juste au-dessus.
+ if (new_nodes[idx].m_new_node->m_owner_new_node == new_nodes[idxm1].m_new_node->m_owner_new_node) {
+ fl.m_owner_new_face = new_nodes[idx].m_new_node->m_owner_new_node;
+ if (fl.m_owner_new_face == subDomain()->subDomainId()) {
+ fl.m_uid_new_face = sd_nb_face++;
+ }
+ else {
+ fl.m_uid_new_face = -2;
+ }
+ }
+ new_faces.add(fl);
+ }
+ idxm1 = idx;
+ }
+ }
+ sd_nb_cell++;
+ }
+ point_coords_tmp.clear();
+ }
+}
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+void MeshCutService::
+_makeUniqueCellUID(Int32 sd_nb_cell, UniqueArray& new_cells, UniqueArray& new_nodes)
+{
+ IParallelMng* pm = subDomain()->parallelMng();
+
+ Int32 decal = sd_nb_cell;
+ pm->scan(MessagePassing::ReduceSum, ArrayView{ 1, &decal });
+
+ // Int32 nb_cells_global = decal;
+ // pm->broadcast(ArrayView{ 1, &nb_cells_global }, pm->commSize() - 1);
+
+ decal -= sd_nb_cell;
+
+ // info() << "[" << pm->commRank() << "] Scan result (rectified) : " << decal;
+
+ // En plus de décaler les uids des mailles pour les rendre unique sur tout le
+ // domaine, on ajoute les vrais UID des noeuds à la place des positions des
+ // noeuds dans le tableau "new_nodes".
+ Int32 pos0 = 0;
+ while (pos0 < new_cells.size()) {
+ Int64 type = new_cells[pos0++];
+ new_cells[pos0++] += decal;
+
+ for (Int32 i = 0; i < type; ++i) {
+ Int64& pos_to_uid = new_cells[pos0++];
+ pos_to_uid = new_nodes[pos_to_uid].m_new_node->m_uid_new_node;
+ }
+ }
+ // return nb_cells_global;
+}
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+void MeshCutService::
+_fillNodeUID(Int32& sd_nb_node, UniqueArray& new_nodes)
+{
+
+ // À partir d'ici, nous avons un tableau de noeuds. Les noeuds dont on est
+ // sûr qu'ils nous appartiennent ont déjà un UID/Owner.
+ //
+ // Il y a deux autres types de noeuds :
+ // - le noeud dont on pense connaitre le proprio à qui demander le UID,
+ // - le noeud dont on ne connait pas encore le proprio ni le UID.
+
+ IParallelMng* pm = subDomain()->parallelMng();
+ Int32 my_proc = pm->commRank();
+ UniqueArray>> requested_nodes(subDomain()->nbSubDomain());
+
+ //
+ //
+ // Première étape : détermination des proprios potentiels et création des
+ // requêtes.
+ //
+ // Détermination des proprios :
+ // Si un noeud n'a pas encore de proprio, on choisit le proprio parmi les
+ // mailles des noeuds (N0 et N1) de l'arête dont est issu notre noeud (NN).
+ //
+ // N0 NN N1
+ // *-------*-------*
+ //
+ // NN = elem.m_new_node
+ // N0 = elem.m_new_node->m_node0
+ // N1 = elem.m_new_node->m_node1
+ //
+ // Il peut y avoir des cas où le proprio choisi ne possède pas le noeud.
+ // Ce cas sera traité plus tard dans la méthode.
+ //
+ // À partir de là, on a un proprio pour tous les noeuds.
+ //
+ // Création des requêtes :
+ // On a un message pour chaque processus.
+ // Ce message est composé de pairs de UID : les UID des noeuds de l'arête
+ // dont est issu notre noeud (seul moyen d'identifier notre noeud sur tous
+ // les processus (si l'on ne souhaite pas utiliser sa position)).
+ //
+ // Exemple :
+ // request_uid[][] :
+ // P0 : --Nous--
+ // P1 : [NN09_N0_UID, NN09_N1_UID, NN01_N0_UID, NN01_N1_UID, NN00_N0_UID, NN00_N1_UID]
+ // P2 : [NN14_N0_UID, NN14_N1_UID, NN15_N0_UID, NN15_N1_UID]
+ // P3 : []
+ //
+ // request_uid[][] :
+ // P0 : []
+ // P1 : [NN09_N0_UID, NN09_N1_UID]
+ // P2 : --Nous--
+ // P3 : []
+ //
+ // request_uid[][] :
+ // P0 : []
+ // P1 : [NN00_N0_UID, NN00_N1_UID]
+ // P2 : []
+ // P3 : --Nous--
+ //
+ // On stocke aussi, dans un autre tableau, les NodeOnEdge correspondant
+ // aux paires de UID, afin de les retrouver facilement lorsque l'on aura
+ // reçu les réponses des processus.
+ //
+ // Exemple :
+ // requested_nodes[][] :
+ // P0 : --Nous--
+ // P1 : [NN09, NN01, NN00]
+ // P2 : [NN14, NN15]
+ // P3 : []
+ //
+ //
+ {
+ UniqueArray> request_uid(subDomain()->nbSubDomain());
+
+ debug() << "[Node][" << my_proc << "] Step 1";
+
+ for (auto& elem : new_nodes) {
+ // Si le uid est déjà mis, pas besoin de le rechercher...
+ if (elem.m_new_node->m_uid_new_node >= 0) {
+ continue;
+ }
+
+ // Si le proprio n'est pas définit, on doit faire une recherche.
+ if (elem.m_new_node->m_owner_new_node < 0) {
+
+ Node node0 = elem.m_new_node->m_node0;
+ Node node1 = elem.m_new_node->m_node1;
+
+ // Le propriétaire du noeud est le propriétaire de la maille ayant le plus
+ // petit UID, parmi les mailles en commun entre les deux noeuds d'origine.
+
+ // TODO Ajouter traitement particulier pour le cas où node0 == node1
+ Int64 min_uid = INT64_MAX;
+ Int32 owner_min = -1;
+ for (Cell cell0 : node0.cells()) {
+ for (Cell cell1 : node1.cells()) {
+ if (cell0 == cell1) {
+ if (cell0.uniqueId() < min_uid) {
+ min_uid = cell0.uniqueId();
+ owner_min = cell0.owner();
+ }
+ }
+ }
+ }
+
+ // S'il l'on est le proprio, on peut définir le uid du noeud.
+ if (owner_min == subDomain()->subDomainId()) {
+ elem.m_new_node->m_owner_new_node = subDomain()->subDomainId();
+ elem.m_new_node->m_uid_new_node = sd_nb_node++;
+
+ // debug() << "[Node][" << my_proc << " -> " << elem.m_new_node->m_owner_new_node << "] Set UID"
+ // << " -- UID0 : " << elem.m_new_node->m_uid_node0
+ // << " -- UID1 : " << elem.m_new_node->m_uid_node1
+ // << " -- New UID : " << elem.m_new_node->m_uid_new_node;
+ }
+
+ // Sinon, on doit aller demander le uid au proprio.
+ else {
+ elem.m_new_node->m_owner_new_node = owner_min;
+ elem.m_new_node->m_uid_new_node = -2;
+
+ // Une requête est composée uniquement des deux UID des noeuds de
+ // l'arête dont est issue le nouveau noeud. C'est le seul moyen
+ // d'identifier ce noeud pour l'instant (si on exclut l'identification
+ // par sa position).
+ request_uid[elem.m_new_node->m_owner_new_node].add(elem.m_new_node->m_uid_node0);
+ request_uid[elem.m_new_node->m_owner_new_node].add(elem.m_new_node->m_uid_node1);
+
+ requested_nodes[elem.m_new_node->m_owner_new_node].add(elem.m_new_node);
+
+ // debug() << "[Node][" << my_proc << " -> " << elem.m_new_node->m_owner_new_node << "] Ask1"
+ // << " -- UID0 : " << elem.m_new_node->m_uid_node0
+ // << " -- UID1 : " << elem.m_new_node->m_uid_node1;
+ }
+ }
+
+ // Si le proprio est déjà défini, on doit lui demander le uid du noeud.
+ else {
+ request_uid[elem.m_new_node->m_owner_new_node].add(elem.m_new_node->m_uid_node0);
+ request_uid[elem.m_new_node->m_owner_new_node].add(elem.m_new_node->m_uid_node1);
+
+ requested_nodes[elem.m_new_node->m_owner_new_node].add(elem.m_new_node);
+
+ // debug() << "[Node][" << my_proc << " -> " << elem.m_new_node->m_owner_new_node << "] Ask2"
+ // << " -- UID0 : " << elem.m_new_node->m_uid_node0
+ // << " -- UID1 : " << elem.m_new_node->m_uid_node1;
+ }
+ }
+
+ // On envoie les requêtes.
+ {
+ UniqueArray requests(subDomain()->nbSubDomain() * 2);
+
+ debug() << "[Node][" << my_proc << "] Step 1.2";
+
+ for (Int32 sr = 0; sr < subDomain()->nbSubDomain(); ++sr) {
+ if (sr == subDomain()->subDomainId()) {
+ continue;
+ }
+ Int32 size = request_uid[sr].size();
+ requests[sr * 2] = pm->send(ArrayView{ 1, &size }, sr, false);
+ requests[sr * 2 + 1] = pm->send(request_uid[sr], sr, false);
+ // debug() << "[Node][" << my_proc << " -> " << sr << "] Requests : " << request_uid[sr];
+ }
+
+ pm->waitAllRequests(requests);
+ }
+ }
+
+ //
+ //
+ // Deuxième étape : traitement des requêtes des autres processus et gestion
+ // des inconnus.
+ //
+ // Traitement des requêtes :
+ //
+ // On effectue le traitement à la réception (donc processus par processus).
+ // Pour chaque paire de UID (N0-N1), on la recherche dans notre tableau des
+ // noeuds.
+ // On l'a trouve, parfait, on place l'UID du noeud (NN) dans le tableau de
+ // réponse.
+ //
+ // Gestion des inconnus :
+ //
+ // Si on ne trouve pas la paire dans notre tableau, on est dans le cas où le
+ // noeud NN est placé sur un noeud du maillage 3D (donc N0 == N1 et
+ // position(N0) == position(NN)).
+ // Lorsque l'on itère sur les mailles de N0, on peut tomber sur des mailles
+ // qui ne sont pas coupées par le plan, donc qui ne possède pas le noeud.
+ //
+ // On n'a pas le noeud dans notre tableau. Par contre, on sait qui demande
+ // ce noeud ! Grâce aux mailles fantômes, on est sûr que tous les processus
+ // qui souhaite le UID du noeud NN vont le demander au même processus. Nous
+ // pouvons donc construire la liste de ces processus demandeurs. Processus
+ // demandeurs dont on est sûr qu'ils possèdent le noeud NN !
+ // Nous allons donc leur envoyer cette liste et il se débrouilleront pour
+ // trouver le bon proprio parmi les processus de cette liste !
+ //
+ // Réponses :
+ //
+ // Le tableau des réponses doit donc contenir deux parties :
+ // - les UIDs, s'ils sont trouvés,
+ // - les processus possédant les noeuds inconnus.
+ //
+ // Pour avoir la limite entre ces deux parties, on place la taille de la
+ // première partie en première position du tableau de réponse.
+ //
+ // Si un UID est trouvé, on l'ajoute. S'il n'est pas trouvé, on place "-1".
+ //
+ // La seconde partie est structurée ainsi :
+ // - Pour chaque inconnu (pour chaque "-1") :
+ // - UID du noeud N0
+ // - UID du noeud N1
+ // - Nombre de processus demandeurs (donc possédant forcément le noeud NN),
+ // - Rangs des processus demandeurs.
+ //
+ // TODO : A changer :
+ // L'ordre de la seconde partie n'a pas d'importance.
+ // En effet, l'ordre de cette partie est le même pour tous les processus (il
+ // n'y a pas de tri par processus selon l'ordre des requêtes (qui
+ // correspondrait à l'ordre des "-1")).
+ // C'est pour ça qu'il y a la présence des "UID du noeud N0" et "UID du
+ // noeud N1".
+ // (trier avant envoi économiserait de la mémoire et réduirait la taille de
+ // la réponse, donc TODO).
+ //
+ //
+ // (Pour l'exemple, on suit le précédent exemple)
+ // answers_uid[][] :
+ // P0 : [3, -1, NN01_UID, -1, NN09_N0_UID, NN09_N1_UID, 2, 0, 2, NN00_N0_UID, NN00_N1_UID, 2, 0, 3]
+ // P1 : --Nous--
+ // P2 : [1, -1, NN09_N0_UID, NN09_N1_UID, 2, 0, 2 ]
+ // P3 : [1, -1, NN00_N0_UID, NN00_N1_UID, 2, 0, 3 ]
+ //
+ //
+ {
+ UniqueArray> answers_uid(subDomain()->nbSubDomain());
+ {
+ UniqueArray unknown_node;
+ debug() << "[Node][" << my_proc << "] Step 2";
+
+ for (Int32 sr = 0; sr < subDomain()->nbSubDomain(); ++sr) {
+ if (sr == subDomain()->subDomainId()) {
+ continue;
+ }
+ Int32 size = 0;
+ pm->recv(ArrayView{ 1, &size }, sr);
+
+ UniqueArray requested_uid(size);
+ pm->recv(requested_uid, sr);
+
+ // Taille de la première partie.
+ answers_uid[sr].add(0);
+
+ // On traite chaque paire de noeud de la requête.
+ for (Int32 ipair_uid = 0; ipair_uid < requested_uid.size(); ipair_uid += 2) {
+
+ std::optional pos = new_nodes.span().findFirst(NodeIntersection{ requested_uid[ipair_uid], requested_uid[ipair_uid + 1], Real3{ 0 } });
+ if (pos) {
+ answers_uid[sr].add(new_nodes[pos.value()].m_new_node->m_uid_new_node);
+ // debug() << "[Node][" << my_proc << " <- " << sr << "] Found"
+ // << " -- UID0 : " << requested_uid[ipair_uid]
+ // << " -- UID1 : " << requested_uid[ipair_uid + 1]
+ // << " -- New UID : " << new_nodes[pos.value()].m_new_node->m_uid_new_node;
+ }
+
+ // Il peut arriver que l'on nous demande un noeud que nous n'avons pas.
+ // Par exemple, si un plan arrive pile sur une des arêtes de nos mailles.
+ // Par contre, on sait qui en a besoin, donc qui possède une maille avec
+ // le noeud en question.
+ // Pour donner cette information, on va ajouter "-1" puis, à la fin de
+ // la réponse, on va placer les processus en question.
+ else {
+ answers_uid[sr].add(-1);
+ unknown_node.add({ requested_uid[ipair_uid], requested_uid[ipair_uid + 1], sr });
+ // debug() << "[Node][" << my_proc << " <- " << sr << "] NOT Found"
+ // << " -- UID0 : " << requested_uid[ipair_uid]
+ // << " -- UID1 : " << requested_uid[ipair_uid + 1];
+ }
+ }
+ answers_uid[sr][0] = answers_uid[sr].size() - 1;
+ }
+
+ UniqueArray who;
+ debug() << "[Node][" << my_proc << "] Step 2.2";
+
+ // Dès qu'il y a eu une paire de noeuds inconnus dans une requête, il y
+ // a eu un enregistrement de fait dans le tableau unknown_node.
+ // Il faut maintenant lister, pour chaque paire, qui l'a aussi demandé
+ // (rechercher les doublons) et envoyer cette liste à chacun d'entre eux.
+ for (Int32 i = 0; i < unknown_node.size(); ++i) {
+ if (unknown_node[i].null())
+ continue;
+ who.clear();
+ who.add(unknown_node[i].m_who);
+ unknown_node[i].m_who = -1;
+ for (Int32 j = i + 1; j < unknown_node.size(); ++j) {
+ if (unknown_node[j].null())
+ continue;
+ if (unknown_node[j] == unknown_node[i]) {
+ who.add(unknown_node[j].m_who);
+ unknown_node[j].m_who = -1;
+ }
+ }
+ // debug() << "[Node] Additionnal infos -- Node0UID : " << unknown_node[i].m_node0_uid << " -- Node1UID : " << unknown_node[i].m_node1_uid << " -- Who : " << who;
+ for (auto proc : who) {
+ answers_uid[proc].add(unknown_node[i].m_node0_uid);
+ answers_uid[proc].add(unknown_node[i].m_node1_uid);
+ answers_uid[proc].add(who.size());
+ answers_uid[proc].addRange(who);
+ }
+ }
+ }
+
+ {
+ UniqueArray requests(subDomain()->nbSubDomain() * 2);
+
+ // On envoie les réponses.
+ debug() << "[Node][" << my_proc << "] Step 2.3";
+ for (Int32 sr = 0; sr < subDomain()->nbSubDomain(); ++sr) {
+ if (sr == subDomain()->subDomainId()) {
+ continue;
+ }
+
+ Int32 size = answers_uid[sr].size();
+ requests[sr * 2] = pm->send(ArrayView{ 1, &size }, sr, false);
+ requests[sr * 2 + 1] = pm->send(answers_uid[sr], sr, false);
+
+ // debug() << "[Node][" << my_proc << " -> " << sr << "] Send " << answers_uid[sr];
+ }
+
+ pm->waitAllRequests(requests);
+ }
+ }
+
+
+ //
+ //
+ // Troisième étape : Mise à jour des UIDs des noeuds et recherche
+ // complémentaire de proprios.
+ //
+ // On commence par découper la réponse en deux, pour retrouver les deux
+ // parties tèl que défini plus haut.
+ // On itère sur la première partie. L'ordre des UIDs reçus est le même que
+ // l'ordre des noeuds du tableau "requested_nodes" complété dans la première
+ // partie. On peut ainsi aisément récupérer l'objet correspondant
+ // (`node_on_edge` dans le code juste en dessous).
+ //
+ // Si nous avons reçu un UID valide, on met à jour le noeud correspondant
+ // et on passe au suivant.
+ //
+ // Si nous avons "-1", c'est que le propriétaire que nous avons défini dans
+ // la première partie n'est pas le bon. Nous devons donc organiser un nouvel
+ // envoi.
+ //
+ // Recherche complémentaire de proprios :
+ //
+ // Comme l'ordre des infos complémentaires de la seconde partie du message
+ // n'est pas forcément le même que l'ordre de la première partie (voir TODO
+ // au dessus), on doit d'abord rechercher, dans la seconde partie, la paire
+ // de UID correspondant au noeud que nous traitons actuellement.
+ //
+ // On trouve forcément une correspondance.
+ //
+ // On est sûr que l'ordre dans lequel est organisée la seconde partie est le
+ // même pour tous. On va donc se caler sur cette ordre pour transmettre le
+ // prochain message.
+ //
+ // Lorsqu'une correspondance est trouvée, on modifie le message. On remplace
+ // UID0 par la position de l'objet `Ref` dans le tableau
+ // `requested_nodes` (pour éviter une seconde recherche).
+ //
+ // Exemple :
+ // Rappel tableau requested_nodes[][] (Première étape de la méthode) :
+ // requested_nodes[][] :
+ // P0 : --Nous--
+ // P1 : [NN09, NN01, NN00]
+ //
+ // Avant modif :
+ // answered_uid[] :
+ // P0 : --Nous--
+ // P1 : [3, -1, NN01_UID, -1, NN09_N0_UID, NN09_N1_UID, 2, 0, 2, NN00_N0_UID, NN00_N1_UID, 2, 0, 3]
+ //
+ // Après modif :
+ // answered_uid[] :
+ // P0 : --Nous--
+ // P1 : [3, -1, NN01_UID, -1, 0, NN09_N1_UID, 2, 0, 2, 2, NN00_N1_UID, 2, 0, 3]
+ //
+ // Explication de l'exemple :
+ // On remplace "NN09_N0_UID" par "0" car le noeud "NN09" se trouve à la
+ // position 0 du tableau `requested_nodes[1][]` (le premier indice est le
+ // rang du processus cible (donc 1 ici)).
+ // On remplace "NN00_N0_UID" par "2" car le noeud "NN00" se trouve à la
+ // position 2 du tableau `requested_nodes[1][]`.
+ //
+ // Cette algorithme est en deux étapes pour préserver simplement l'ordre de
+ // la seconde partie de la réponse.
+ //
+ // Une fois `answered_uid[1]` modifié (donc une fois que l'on a fini
+ // d'itérer sur la première partie de la réponse), on passe à la recherche
+ // du propriétaire des noeuds restants.
+ //
+ // On itère sur la seconde partie de la réponse. Tous les processus
+ // itèreront dans le même ordre !
+ //
+ // On récupère l'objet `Ref` à l'aide de sa position dans le
+ // tableau `requested_nodes`. On reproduit l'algo de recherche de proprio de
+ // la première étape de la méthode en s'assurant de tomber sur un proprio de
+ // la liste des proprios potentiels.
+ //
+ // Par rapport à la première étape de la méthode, on sait de qui recevoir
+ // des informations et on sait dans quel ordre les interpréter. Donc inutile
+ // d'envoyer des messages vides si aucune infos n'est nécessaire.
+ //
+ // On va utiliser le tableau "request_uid" pour stocker les messages à
+ // envoyer mais aussi la taille des messages que l'on devra recevoir.
+ //
+ // Pour les messages à envoyer, on stocke uniquement les UIDs qui devront
+ // être appliqués.
+ // Pour chaque UID stocké du coté du processus envoyeur, l'objet
+ // `Ref` correspondant est stocké dans le tableau
+ // `requested_nodes2` du coté du processus receveur.
+ //
+ // Exemple :
+ // L'algo a déterminé que
+ // - "NN09" appartient à "2" (et non à "0") et
+ // - "NN00" appartient à "0" (et non à "3").
+ //
+ // request_uid[][] :
+ // P0 : --Nous-- [0, 0, 1, 0]
+ // P1 : []
+ // P2 : []
+ // P3 : [NN00_UID]
+ //
+ // (à lire : P0 attend un message de taille 1 de la part de P2)
+ // ( P0 envoie NN00_UID à P3)
+ //
+ // request_uid[][] :
+ // P0 : [NN09_UID]
+ // P1 : []
+ // P2 : --Nous-- [0, 0, 0, 0]
+ // P3 : []
+ //
+ // request_uid[][] :
+ // P0 : []
+ // P1 : []
+ // P2 : []
+ // P3 : --Nous-- [1, 0, 0, 0]
+ //
+ //
+ UniqueArray> request_uid(subDomain()->nbSubDomain());
+ UniqueArray>> requested_nodes2(subDomain()->nbSubDomain());
+
+ // On reçoit et traite les réponses.
+ debug() << "[Node][" << my_proc << "] Step 3";
+
+ // Permet de savoir si les prochaines étapes sont utiles.
+ bool need_more_comm = false;
+
+ for (Int32 sr = 0; sr < subDomain()->nbSubDomain(); ++sr) {
+ if (sr == subDomain()->subDomainId()) {
+ continue;
+ }
+
+ Int32 total_size = 0;
+ pm->recv(ArrayView{ 1, &total_size }, sr);
+
+ UniqueArray answered_uid(total_size);
+ pm->recv(answered_uid, sr);
+
+ if (total_size == 1) {
+ continue;
+ }
+
+ Int32 answer_to_request_size = static_cast(answered_uid[0]);
+
+ Span answer_to_request = answered_uid.subView(1, answer_to_request_size);
+ Span additionnal_answer = answered_uid.subView(answer_to_request_size+1, answered_uid.size() - (answer_to_request_size+1));
+
+ // debug() << "[Node][" << my_proc << " <- " << sr << "] Decoupe"
+ // << " -- answer_to_request : " << answer_to_request
+ // << " -- additionnal_answer : " << additionnal_answer;
+
+
+ for (Int32 answer = 0; answer < answer_to_request.size(); ++answer) {
+ Ref node_on_edge = requested_nodes[sr][answer];
+
+ if (answer_to_request[answer] != -1) {
+ node_on_edge->m_uid_new_node = answer_to_request[answer];
+ // debug() << "[Node][" << my_proc << "] Apply"
+ // << " -- UID0 : " << node_on_edge->m_uid_node0
+ // << " -- UID1 : " << node_on_edge->m_uid_node1
+ // << " -- New UID : " << node_on_edge->m_uid_new_node;
+ }
+ else {
+ Int64 pos = 0;
+ while (pos < additionnal_answer.size()) {
+ Int64& uid0 = additionnal_answer[pos++];
+ Int64 uid1 = additionnal_answer[pos++];
+ Int64 decal = additionnal_answer[pos++];
+ if (uid0 == node_on_edge->m_uid_node0 && uid1 == node_on_edge->m_uid_node1) {
+ uid0 = answer;
+ break;
+ }
+ pos += decal;
+ }
+ if (pos >= additionnal_answer.size()) {
+ ARCANE_FATAL("Unknown node -- UID0 : {0} -- UID1 : {1}", node_on_edge->m_uid_node0, node_on_edge->m_uid_node1);
+ }
+ }
+ }
+
+ Int64 pos = 0;
+ Span sub_additionnal_answer;
+
+ while (pos < additionnal_answer.size()) {
+ Int64 pos_node_in_array = additionnal_answer[pos++];
+ pos++;
+ Int64 decal = additionnal_answer[pos++];
+
+ Ref node_on_edge = requested_nodes[sr][pos_node_in_array];
+ sub_additionnal_answer = additionnal_answer.subSpan(pos, decal);
+
+ pos += decal;
+
+ // debug() << "[Node][" << my_proc << " <- " << sr << "] Sub"
+ // << " -- sub_additionnal_answer : " << sub_additionnal_answer;
+
+ Node node0 = node_on_edge->m_node0;
+ Node node1 = node_on_edge->m_node1;
+
+ // Le propriétaire du noeud est le propriétaire de la maille ayant le plus
+ // petit UID, parmi les mailles en commun entre les deux noeuds d'origine.
+
+ // TODO Ajouter traitement particulier pour le cas où node0 == node1
+ Int64 min_uid = INT64_MAX;
+ Int32 owner_min = -1;
+ for (Cell cell0 : node0.cells()) {
+ for (Cell cell1 : node1.cells()) {
+ if (cell0 == cell1) {
+ if (cell0.uniqueId() < min_uid && sub_additionnal_answer.contains(cell0.owner())) {
+ min_uid = cell0.uniqueId();
+ owner_min = cell0.owner();
+ }
+ }
+ }
+ }
+
+ // S'il l'on est le proprio, on peut définir le uid du noeud.
+ if (owner_min == subDomain()->subDomainId()) {
+ node_on_edge->m_owner_new_node = subDomain()->subDomainId();
+ node_on_edge->m_uid_new_node = sd_nb_node++;
+
+ // On enregistre le UID à envoyer.
+ if (sub_additionnal_answer.size() > 1) {
+ need_more_comm = true;
+
+ // debug() << "[Node][" << my_proc << "] Send"
+ // << " -- UID : " << node_on_edge->m_uid_new_node
+ // << " -- for Node UID0 : " << node_on_edge->m_uid_node0
+ // << " -- UID1 : " << node_on_edge->m_uid_node1
+ // << " -- to : " << sub_additionnal_answer;
+
+ for (auto proc : sub_additionnal_answer) {
+ if (proc == my_proc)
+ continue;
+ request_uid[proc].add(node_on_edge->m_uid_new_node);
+ }
+ }
+ }
+
+ // Sinon, on doit aller demander le uid au proprio.
+ else {
+ need_more_comm = true;
+ node_on_edge->m_owner_new_node = owner_min;
+ node_on_edge->m_uid_new_node = -2;
+
+ if (request_uid[my_proc].empty()) {
+ request_uid[my_proc].resize(subDomain()->nbSubDomain(), 0);
+ }
+
+ request_uid[my_proc][owner_min]++;
+
+ requested_nodes2[owner_min].add(node_on_edge);
+
+ // debug() << "[Node][" << my_proc << " -> " << node_on_edge->m_owner_new_node << "] Recv UID for Node"
+ // << " -- UID0 : " << node_on_edge->m_uid_node0
+ // << " -- UID1 : " << node_on_edge->m_uid_node1;
+ }
+ }
+ }
+
+ // Si nous n'avons pas de UID manquants et que personne n'a besoin de l'un
+ // de nos UIDs, on passe cette étape.
+ // Sinon :
+ if (need_more_comm) {
+ {
+ UniqueArray requests(subDomain()->nbSubDomain());
+
+ // On envoie les UID complémentaires.
+ debug() << "[Node][" << my_proc << "] Step 3.2";
+
+ for (Int32 sr = 0; sr < subDomain()->nbSubDomain(); ++sr) {
+ if (sr == subDomain()->subDomainId()) {
+ // debug() << "[Node][" << my_proc << " -> " << sr << "] My RequestsC : " << request_uid[sr];
+ continue;
+ }
+ if (request_uid[sr].empty()) continue;
+ requests[sr] = pm->send(request_uid[sr], sr, false);
+ // debug() << "[Node][" << my_proc << " -> " << sr << "] RequestsC : " << request_uid[sr];
+ }
+
+ pm->waitAllRequests(requests);
+ }
+
+ //
+ //
+ // Quatrième étape : Mise à jour des UIDs manquants.
+ //
+ // On est sûr d'avoir une réponse complète, donc c'est plus simple.
+ // On récupère le nombre de messages à recevoir de la part de l'autre
+ // processus et on traite son message dans l'ordre. Le tableau
+ // `requested_nodes2[][]` est dans le même ordre ce qui facilite les
+ // choses.
+ //
+ //
+
+ if (!request_uid[my_proc].empty()) {
+ // On reçoit et traite les UID complémentaires.
+ debug() << "[Node][" << my_proc << "] Step 4";
+
+ for (Int32 sr = 0; sr < subDomain()->nbSubDomain(); ++sr) {
+ if (sr == subDomain()->subDomainId()) {
+ continue;
+ }
+
+ Int64 size = request_uid[my_proc][sr];
+ if (size == 0) continue;
+
+ // debug() << "[Node][" << my_proc << " -> " << sr << "] Size recv : " << size;
+
+
+ UniqueArray requested_uid(size);
+ pm->recv(requested_uid, sr);
+
+ for (Int32 i = 0; i < size; ++i) {
+ Ref node_on_edge = requested_nodes2[sr][i];
+ node_on_edge->m_uid_new_node = requested_uid[i];
+ // debug() << "[Node][" << my_proc << "] Apply2"
+ // << " -- UID0 : " << node_on_edge->m_uid_node0
+ // << " -- UID1 : " << node_on_edge->m_uid_node1
+ // << " -- New UID : " << node_on_edge->m_uid_new_node;
+ }
+ }
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+Int32 MeshCutService::
+_makeUniqueNodeUID(Int32 sd_nb_node, UniqueArray& new_nodes)
+{
+ IParallelMng* pm = subDomain()->parallelMng();
+
+ UniqueArray all_nb_node(pm->commSize());
+ pm->allGather(ArrayView{ 1, &sd_nb_node }, all_nb_node);
+
+ Int32 sum = 0;
+ for (auto& elem : all_nb_node) {
+ const Int32 old = elem;
+ elem = sum;
+ sum += old;
+ }
+
+ // info() << "[" << pm->commRank() << "] Gather result (rectified) : " << all_nb_node;
+
+ for (auto& elem : new_nodes) {
+ elem.m_new_node->m_uid_new_node += all_nb_node[elem.m_new_node->m_owner_new_node];
+ }
+ return sum;
+}
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+void MeshCutService::
+_fillFaceUID(Int32& sd_nb_face, UniqueArray& new_faces)
+{
+ // Cet algo est repris de celui traitant les noeuds.
+ IParallelMng* pm = subDomain()->parallelMng();
+ Int32 my_proc = pm->commRank();
+ UniqueArray> requested_faces(subDomain()->nbSubDomain());
+
+ {
+ Int32 iter = -1;
+ UniqueArray> request_uid(subDomain()->nbSubDomain());
+
+ debug() << "[Face][" << my_proc << "] Step 1";
+
+ for (auto& elem : new_faces) {
+ iter++;
+ if (elem.m_uid_new_face >= 0) {
+ continue;
+ }
+ if (elem.m_owner_new_face < 0) {
+
+ Node node00 = elem.m_node0->m_node0;
+ Node node01 = elem.m_node0->m_node1;
+ Node node10 = elem.m_node1->m_node0;
+ Node node11 = elem.m_node1->m_node1;
+
+ Int64 min_uid = INT64_MAX;
+ Int32 owner_min = -1;
+
+ // TODO AH : C'est quand même TURBO moche
+ for (Cell cell00 : node00.cells()) {
+ for (Cell cell01 : node01.cells()) {
+ if (cell00 == cell01) {
+ for (Cell cell10 : node10.cells()) {
+ if (cell01 == cell10 ) {
+ for (Cell cell11 : node11.cells()) {
+ if (cell10 == cell11) {
+ if (cell11.uniqueId() < min_uid) {
+ min_uid = cell11.uniqueId();
+ owner_min = cell11.owner();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (owner_min == subDomain()->subDomainId()) {
+ elem.m_owner_new_face = subDomain()->subDomainId();
+ elem.m_uid_new_face = sd_nb_face++;
+
+ // debug() << "[Face][" << my_proc << " -> " << elem.m_owner_new_face << "] Set UID"
+ // << " -- UID0 : " << elem.m_node0->m_uid_new_node
+ // << " -- UID1 : " << elem.m_node1->m_uid_new_node
+ // << " -- New UID : " << elem.m_uid_new_face;
+ }
+ else {
+ elem.m_owner_new_face = owner_min;
+ elem.m_uid_new_face = -2;
+ request_uid[elem.m_owner_new_face].add(elem.m_node0->m_uid_new_node);
+ request_uid[elem.m_owner_new_face].add(elem.m_node1->m_uid_new_node);
+
+ requested_faces[elem.m_owner_new_face].add(iter);
+
+ // debug() << "[Face][" << my_proc << " -> " << elem.m_owner_new_face << "] Ask1"
+ // << " -- UID0 : " << elem.m_node0->m_uid_new_node
+ // << " -- UID1 : " << elem.m_node1->m_uid_new_node;
+ }
+ }
+ else {
+ request_uid[elem.m_owner_new_face].add(elem.m_node0->m_uid_new_node);
+ request_uid[elem.m_owner_new_face].add(elem.m_node1->m_uid_new_node);
+
+ requested_faces[elem.m_owner_new_face].add(iter);
+
+ // debug() << "[Face][" << my_proc << " -> " << elem.m_owner_new_face << "] Ask2"
+ // << " -- UID0 : " << elem.m_node0->m_uid_new_node
+ // << " -- UID1 : " << elem.m_node1->m_uid_new_node;
+ }
+ }
+
+ {
+ UniqueArray requests(subDomain()->nbSubDomain() * 2);
+
+ // On envoie les requêtes.
+ debug() << "[Face][" << my_proc << "] Step 1.2";
+
+ for (Int32 sr = 0; sr < subDomain()->nbSubDomain(); ++sr) {
+ if (sr == subDomain()->subDomainId()) {
+ continue;
+ }
+ Int32 size = request_uid[sr].size();
+ requests[sr * 2] = pm->send(ArrayView{ 1, &size }, sr, false);
+ requests[sr * 2 + 1] = pm->send(request_uid[sr], sr, false);
+ // debug() << "[Face][" << my_proc << " -> " << sr << "] Requests : " << request_uid[sr];
+ }
+
+ pm->waitAllRequests(requests);
+ }
+ }
+
+ {
+ UniqueArray> answers_uid(subDomain()->nbSubDomain());
+ {
+ UniqueArray unknown_face;
+ debug() << "[Face][" << my_proc << "] Step 2";
+
+ for (Int32 sr = 0; sr < subDomain()->nbSubDomain(); ++sr) {
+ if (sr == subDomain()->subDomainId()) {
+ continue;
+ }
+ Int32 size = 0;
+ pm->recv(ArrayView{ 1, &size }, sr);
+
+ UniqueArray requested_uid(size);
+ pm->recv(requested_uid, sr);
+
+ answers_uid[sr].add(0);
+
+ for (Int32 ipair_uid = 0; ipair_uid < requested_uid.size(); ipair_uid += 2) {
+ std::optional pos = _find(new_faces, requested_uid[ipair_uid], requested_uid[ipair_uid + 1]);
+ if (pos) {
+ answers_uid[sr].add(new_faces[pos.value()].m_uid_new_face);
+ // debug() << "[Face][" << my_proc << " <- " << sr << "] Found"
+ // << " -- UID0 : " << requested_uid[ipair_uid]
+ // << " -- UID1 : " << requested_uid[ipair_uid + 1]
+ // << " -- New UID : " << new_faces[pos.value()].m_uid_new_face;
+ }
+
+ // Il peut arriver que l'on nous demande une face que nous n'avons pas.
+ // Par exemple, si un plan arrive pile sur une des arêtes de nos mailles.
+ // Par contre, on sait qui en a besoin, donc qui possède une maille avec
+ // la face en question.
+ // Pour donner cette information, on va ajouter "-1" puis, à la fin de
+ // la réponse, on va placer les processus en question.
+ else {
+ answers_uid[sr].add(-1);
+ unknown_face.add({ requested_uid[ipair_uid], requested_uid[ipair_uid + 1], sr });
+ // debug() << "[Face][" << my_proc << " <- " << sr << "] NOT Found"
+ // << " -- UID0 : " << requested_uid[ipair_uid]
+ // << " -- UID1 : " << requested_uid[ipair_uid + 1];
+ }
+ }
+ answers_uid[sr][0] = answers_uid[sr].size() - 1;
+ }
+
+ UniqueArray who;
+ debug() << "[Face][" << my_proc << "] Step 2.2";
+
+ for (Int32 i = 0; i < unknown_face.size(); ++i) {
+ if (unknown_face[i].null())
+ continue;
+ who.clear();
+ who.add(unknown_face[i].m_who);
+ unknown_face[i].m_who = -1;
+ for (Int32 j = i + 1; j < unknown_face.size(); ++j) {
+ if (unknown_face[j].null())
+ continue;
+ if (unknown_face[j] == unknown_face[i]) {
+ who.add(unknown_face[j].m_who);
+ unknown_face[j].m_who = -1;
+ }
+ }
+ // debug() << "[Face] Additionnal infos -- Node0UID : " << unknown_face[i].m_node0_uid << " -- Node1UID : " << unknown_face[i].m_node1_uid << " -- Who : " << who;
+ for (auto proc : who) {
+ answers_uid[proc].add(unknown_face[i].m_node0_uid);
+ answers_uid[proc].add(unknown_face[i].m_node1_uid);
+ answers_uid[proc].add(who.size());
+ answers_uid[proc].addRange(who);
+ }
+ }
+ }
+
+ {
+ UniqueArray requests(subDomain()->nbSubDomain() * 2);
+
+ // On envoie les réponses.
+ debug() << "[Face][" << my_proc << "] Step 2.3";
+ for (Int32 sr = 0; sr < subDomain()->nbSubDomain(); ++sr) {
+ if (sr == subDomain()->subDomainId()) {
+ continue;
+ }
+
+ Int32 size = answers_uid[sr].size();
+ requests[sr * 2] = pm->send(ArrayView{ 1, &size }, sr, false);
+ requests[sr * 2 + 1] = pm->send(answers_uid[sr], sr, false);
+ }
+
+ pm->waitAllRequests(requests);
+ }
+ }
+
+ UniqueArray> request_uid(subDomain()->nbSubDomain());
+ UniqueArray> requested_faces2(subDomain()->nbSubDomain());
+
+ // On reçoit et traite les réponses.
+ debug() << "[Face][" << my_proc << "] Step 3";
+
+ bool need_more_comm = false;
+
+ for (Int32 sr = 0; sr < subDomain()->nbSubDomain(); ++sr) {
+ if (sr == subDomain()->subDomainId()) {
+ continue;
+ }
+
+ Int32 total_size = 0;
+ pm->recv(ArrayView{ 1, &total_size }, sr);
+
+ UniqueArray answered_uid(total_size);
+ pm->recv(answered_uid, sr);
+
+ if (total_size == 1) {
+ continue;
+ }
+
+ Int32 answer_to_request_size = static_cast(answered_uid[0]);
+
+ Span answer_to_request = answered_uid.subView(1, answer_to_request_size);
+ Span additionnal_answer = answered_uid.subView(answer_to_request_size + 1, answered_uid.size() - (answer_to_request_size + 1));
+
+ // debug() << "[Face][" << my_proc << " <- " << sr << "] Decoupe"
+ // << " -- answer_to_request : " << answer_to_request
+ // << " -- additionnal_answer : " << additionnal_answer;
+
+ for (Int32 answer = 0; answer < answer_to_request.size(); ++answer) {
+ FaceLite& face_lite = new_faces[requested_faces[sr][answer]];
+
+ if (answer_to_request[answer] != -1) {
+ face_lite.m_uid_new_face = answer_to_request[answer];
+ // debug() << "[Face][" << my_proc << "] Apply"
+ // << " -- UID0 : " << face_lite.m_node0->m_uid_new_node
+ // << " -- UID1 : " << face_lite.m_node1->m_uid_new_node
+ // << " -- New UID : " << face_lite.m_uid_new_face;
+ }
+ else {
+ Int64 pos = 0;
+ while (pos < additionnal_answer.size()) {
+ Int64& uid0 = additionnal_answer[pos++];
+ Int64 uid1 = additionnal_answer[pos++];
+ Int64 decal = additionnal_answer[pos++];
+ if (uid0 == face_lite.m_node0->m_uid_new_node && uid1 == face_lite.m_node1->m_uid_new_node) {
+ uid0 = answer;
+ break;
+ }
+ pos += decal;
+ }
+ if (pos >= additionnal_answer.size()) {
+ ARCANE_FATAL("Unknown face -- UID0 : {0} -- UID1 : {1}", face_lite.m_node0->m_uid_new_node, face_lite.m_node1->m_uid_new_node);
+ }
+ }
+ }
+
+ Int64 pos = 0;
+ Span sub_additionnal_answer;
+
+ while (pos < additionnal_answer.size()) {
+ Int64 pos_node_in_array = additionnal_answer[pos++];
+ pos++;
+ Int64 decal = additionnal_answer[pos++];
+
+ FaceLite& face_lite = new_faces[requested_faces[sr][pos_node_in_array]];
+ sub_additionnal_answer = additionnal_answer.subSpan(pos, decal);
+
+ pos += decal;
+
+
+ // debug() << "[Face][" << my_proc << " <- " << sr << "] Sub"
+ // << " -- sub_additionnal_answer : " << sub_additionnal_answer;
+
+ Node node00 = face_lite.m_node0->m_node0;
+ Node node01 = face_lite.m_node0->m_node1;
+ Node node10 = face_lite.m_node1->m_node0;
+ Node node11 = face_lite.m_node1->m_node1;
+
+ Int64 min_uid = INT64_MAX;
+ Int32 owner_min = -1;
+
+ // TODO AH : C'est quand même TURBO moche
+ for (Cell cell00 : node00.cells()) {
+ for (Cell cell01 : node01.cells()) {
+ if (cell00 == cell01) {
+ for (Cell cell10 : node10.cells()) {
+ if (cell01 == cell10) {
+ for (Cell cell11 : node11.cells()) {
+ if (cell10 == cell11) {
+ if (cell11.uniqueId() < min_uid && sub_additionnal_answer.contains(cell11.owner())) {
+ min_uid = cell11.uniqueId();
+ owner_min = cell11.owner();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // S'il l'on est le proprio, on peut définir le uid du noeud.
+ if (owner_min == subDomain()->subDomainId()) {
+ face_lite.m_owner_new_face = subDomain()->subDomainId();
+ face_lite.m_uid_new_face = sd_nb_face++;
+
+ // On enregistre le UID à envoyer.
+ if (sub_additionnal_answer.size() > 1) {
+ need_more_comm = true;
+
+ // debug() << "[Face][" << my_proc << "] Send"
+ // << " -- UID : " << face_lite.m_uid_new_face
+ // << " -- for Node UID0 : " << face_lite.m_node0->m_uid_new_node
+ // << " -- UID1 : " << face_lite.m_node1->m_uid_new_node
+ // << " -- to : " << sub_additionnal_answer;
+
+ for (auto proc : sub_additionnal_answer) {
+ if (proc == my_proc)
+ continue;
+ request_uid[proc].add(face_lite.m_uid_new_face);
+ }
+ }
+ }
+
+ // Sinon, on doit aller demander le uid au proprio.
+ else {
+ need_more_comm = true;
+ face_lite.m_owner_new_face = owner_min;
+ face_lite.m_uid_new_face = -2;
+
+ if (request_uid[my_proc].empty()) {
+ request_uid[my_proc].resize(subDomain()->nbSubDomain(), 0);
+ }
+
+ request_uid[my_proc][owner_min]++;
+
+ requested_faces2[owner_min].add(requested_faces[sr][pos_node_in_array]);
+
+ // debug() << "[Face][" << my_proc << " -> " << face_lite.m_owner_new_face << "] Recv UID for Node"
+ // << " -- UID0 : " << face_lite.m_node0->m_uid_new_node
+ // << " -- UID1 : " << face_lite.m_node1->m_uid_new_node;
+ }
+ }
+ }
+
+ if (need_more_comm) {
+ {
+ UniqueArray requests(subDomain()->nbSubDomain());
+
+ // On envoie les UID complémentaires.
+ debug() << "[Face][" << my_proc << "] Step 3.2";
+
+ for (Int32 sr = 0; sr < subDomain()->nbSubDomain(); ++sr) {
+ if (sr == subDomain()->subDomainId()) {
+ // debug() << "[Face][" << my_proc << " -> " << sr << "] My RequestsC : " << request_uid[sr];
+ continue;
+ }
+ if (request_uid[sr].empty()) continue;
+ requests[sr] = pm->send(request_uid[sr], sr, false);
+ // debug() << "[Face][" << my_proc << " -> " << sr << "] RequestsC : " << request_uid[sr];
+ }
+
+ pm->waitAllRequests(requests);
+ }
+
+ // On reçoit et traite les UID complémentaires.
+ debug() << "[Face][" << my_proc << "] Step 7";
+
+ if (!request_uid[my_proc].empty()) {
+ for (Int32 sr = 0; sr < subDomain()->nbSubDomain(); ++sr) {
+ if (sr == subDomain()->subDomainId()) {
+ continue;
+ }
+
+ Int64 size = request_uid[my_proc][sr];
+ if (size == 0) continue;
+
+ // debug() << "[Face][" << my_proc << " -> " << sr << "] Size recv : " << size;
+
+
+ UniqueArray requested_uid(size);
+ pm->recv(requested_uid, sr);
+
+ for (Int32 i = 0; i < size; ++i) {
+ FaceLite& face_lite = new_faces[requested_faces2[sr][i]];
+ face_lite.m_uid_new_face = requested_uid[i];
+ // debug() << "[Face][" << my_proc << "] Apply2"
+ // << " -- UID0 : " << face_lite.m_node0->m_uid_new_node
+ // << " -- UID1 : " << face_lite.m_node1->m_uid_new_node
+ // << " -- New UID : " << face_lite.m_uid_new_face;
+ }
+ }
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+Int32 MeshCutService::
+_makeUniqueFaceUID(Int32 sd_nb_face, UniqueArray& new_faces)
+{
+ IParallelMng* pm = subDomain()->parallelMng();
+
+ UniqueArray all_nb_node(pm->commSize());
+ pm->allGather(ArrayView{ 1, &sd_nb_face }, all_nb_node);
+
+ Int32 sum = 0;
+ for (auto& elem : all_nb_node) {
+ const Int32 old = elem;
+ elem = sum;
+ sum += old;
+ }
+
+ // info() << "[" << pm->commRank() << "] Gather result (rectified) : " << all_nb_node;
+
+ for (auto& elem : new_faces) {
+ elem.m_uid_new_face += all_nb_node[elem.m_owner_new_face];
+ }
+ return sum;
+}
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+void MeshCutService::
+_compute()
+{
+ Int32 nb_cell = 0;
+ Int32 g_nb_node = 0;
+ Int32 g_nb_face = 0;
+
+ UniqueArray new_cells;
+
+ UniqueArray new_nodes;
+ UniqueArray new_faces;
+
+ for (Int32 i = 0; i < m_plans.size(); ++i) {
+
+ debug() << "Plan : " << m_plans[i].first << ", " << m_plans[i].second;
+
+ UniqueArray plan_new_nodes;
+ UniqueArray plan_new_faces;
+
+ Int32 ajust = new_nodes.size();
+
+ Int32 previous_g_nb_node = g_nb_node;
+ Int32 previous_g_nb_face = g_nb_face;
+
+ Int32 nb_node_for_this_plan = g_nb_node;
+ Int32 nb_face_for_this_plan = g_nb_face;
+
+ debug() << "[" << subDomain()->parallelMng()->commRank() << "] _createNodesAndCells";
+ _createNodesAndCells(i, g_nb_node, plan_new_nodes, ajust, nb_cell, new_cells, g_nb_face, plan_new_faces);
+
+ // for (auto& elem : plan_new_nodes) {
+ // debug() << "New node"
+ // << " -- UID : " << elem.m_new_node->m_uid_new_node
+ // << " -- Owner : " << elem.m_new_node->m_owner_new_node
+ // << " -- Pos : " << elem.m_intersection_pos
+ // << " -- Edge node0 : " << elem.m_new_node->m_uid_node0
+ // << " -- Edge node1 : " << elem.m_new_node->m_uid_node1;
+ // }
+
+ debug() << "[" << subDomain()->parallelMng()->commRank() << "] _fillNodeUID";
+ _fillNodeUID(g_nb_node, plan_new_nodes);
+ nb_node_for_this_plan = g_nb_node - nb_node_for_this_plan;
+
+ nb_node_for_this_plan = _makeUniqueNodeUID(nb_node_for_this_plan, plan_new_nodes);
+ g_nb_node = previous_g_nb_node + nb_node_for_this_plan;
+
+ // for (auto& elem : plan_new_nodes) {
+ // debug() << "Fix node"
+ // << " -- UID : " << elem.m_new_node->m_uid_new_node
+ // << " -- Owner : " << elem.m_new_node->m_owner_new_node
+ // << " -- Pos : " << elem.m_intersection_pos
+ // << " -- Edge node0 : " << elem.m_new_node->m_uid_node0
+ // << " -- Edge node1 : " << elem.m_new_node->m_uid_node1;
+ // }
+ //
+ // for (auto& elem : plan_new_faces) {
+ // debug() << "New face"
+ // << " -- UID : " << elem.m_uid_new_face
+ // << " -- Owner : " << elem.m_owner_new_face
+ // << " -- Node0 : " << elem.m_node0->m_uid_new_node
+ // << " -- Node00 : " << elem.m_node0->m_uid_node0
+ // << " -- Node01 : " << elem.m_node0->m_uid_node1
+ // << " -- Node1 : " << elem.m_node1->m_uid_new_node
+ // << " -- Node10 : " << elem.m_node1->m_uid_node0
+ // << " -- Node11 : " << elem.m_node1->m_uid_node1;
+ // }
+ _fillFaceUID(g_nb_face, plan_new_faces);
+ nb_face_for_this_plan = g_nb_face - nb_face_for_this_plan;
+
+ nb_face_for_this_plan = _makeUniqueFaceUID(nb_face_for_this_plan, plan_new_faces);
+ g_nb_face = previous_g_nb_face + nb_face_for_this_plan;
+
+ // for (auto& elem : plan_new_faces) {
+ // debug() << "Fix face"
+ // << " -- UID : " << elem.m_uid_new_face
+ // << " -- Owner : " << elem.m_owner_new_face
+ // << " -- Node0 : " << elem.m_node0->m_uid_new_node
+ // << " -- Node00 : " << elem.m_node0->m_uid_node0
+ // << " -- Node01 : " << elem.m_node0->m_uid_node1
+ // << " -- Node1 : " << elem.m_node1->m_uid_new_node
+ // << " -- Node10 : " << elem.m_node1->m_uid_node0
+ // << " -- Node11 : " << elem.m_node1->m_uid_node1;
+ // }
+
+ new_nodes.addRange(plan_new_nodes);
+ new_faces.addRange(plan_new_faces);
+ }
+
+ _makeUniqueCellUID(nb_cell, new_cells, new_nodes);
+
+ {
+ // Int32 pos0 = 0;
+ // while (pos0 < new_cells.size()) {
+ // StringBuilder logs;
+ // logs += "New cell -- Type : ";
+ // Int64 type = new_cells[pos0++];
+ // logs += type;
+ // logs += " -- UID : ";
+ // logs += new_cells[pos0++];
+ //
+ // for (Int32 i = 0; i < type; ++i) {
+ // logs += " -- Node";
+ // logs += i;
+ // logs += " : ";
+ // logs += new_cells[pos0++];
+ // }
+ // debug() << logs;
+ // }
+ }
+
+ _addFaces(new_faces);
+ _addCells(new_cells);
+ m_cloned_mesh->modifier()->endUpdate();
+ _setCoordNodesAndOwner(new_nodes);
+ _setFacesOwner(new_faces);
+
+ m_cloned_mesh->nodeFamily()->notifyItemsOwnerChanged();
+ m_cloned_mesh->faceFamily()->notifyItemsOwnerChanged();
+
+ info() << "New mesh : " << m_cloned_mesh->name() << " -- NbNode : " << m_cloned_mesh->nbNode() << " -- NbCells : " << m_cloned_mesh->nbCell();
+}
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+std::optional MeshCutService::
+_find(Span new_faces, Int64 node_uid0, Int64 node_uid1)
+{
+ for (Int32 i = 0; i < new_faces.size(); ++i) {
+ if (new_faces[i].m_node0->m_uid_new_node == node_uid0 && new_faces[i].m_node1->m_uid_new_node == node_uid1) {
+ return i;
+ }
+ }
+ return std::nullopt;
+}
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+void MeshCutService::
+_addFaces(UniqueArray& new_faces) const
+{
+ UniqueArray faces_infos;
+ faces_infos.reserve(new_faces.size() * 4);
+
+ Int32 nb_faces = 0;
+
+ for (auto& elem : new_faces) {
+ faces_infos.add(ITI_Line2);
+ faces_infos.add(elem.m_uid_new_face);
+ faces_infos.add(elem.m_node0->m_uid_new_node);
+ faces_infos.add(elem.m_node1->m_uid_new_node);
+ nb_faces++;
+ }
+
+ m_cloned_mesh->modifier()->addFaces(nb_faces, faces_infos);
+}
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+void MeshCutService::
+_addCells(UniqueArray& new_cells)
+{
+ Int32 nb_cells = 0;
+
+ Int32 pos0 = 0;
+ while (pos0 < new_cells.size()) {
+ const Int64 type = new_cells[pos0];
+
+ if (type == 3)
+ new_cells[pos0++] = ITI_Triangle3;
+ else if (type == 4)
+ new_cells[pos0++] = ITI_Quad4;
+ else if (type == 5)
+ new_cells[pos0++] = ITI_Pentagon5;
+ else if (type == 6)
+ new_cells[pos0++] = ITI_Hexagon6;
+ else
+ ARCANE_FATAL("Pas implem : {0}", type);
+
+ pos0 += 1 + static_cast(type); // (1)=UID + (type)=UIDNodes
+
+ nb_cells++;
+ }
+
+ m_cloned_mesh->modifier()->addCells(nb_cells, new_cells);
+}
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+void MeshCutService::
+_setCoordNodesAndOwner(UniqueArray& new_nodes)
+{
+ VariableNodeReal3& node_coords(m_cloned_mesh->nodesCoordinates());
+ ENUMERATE_ (Node, inode, m_cloned_mesh->allNodes()) {
+ const Int64 uid = inode->uniqueId();
+ for (const auto& elem : new_nodes) {
+ if (elem.m_new_node->m_uid_new_node == uid) {
+ node_coords[inode] = elem.m_intersection_pos;
+ inode->mutableItemBase().setOwner(elem.m_new_node->m_owner_new_node, subDomain()->subDomainId());
+ // debug() << "NodeUID : " << uid << " -- Coord : " << node_coords[inode];
+ }
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+void MeshCutService::
+_setFacesOwner(UniqueArray& new_faces)
+{
+ ENUMERATE_ (Face, iface, m_cloned_mesh->allFaces()) {
+ const Int64 uid = iface->uniqueId();
+ for (const auto& elem : new_faces) {
+ if (elem.m_uid_new_face == uid) {
+ iface->mutableItemBase().setOwner(elem.m_owner_new_face, subDomain()->subDomainId());
+ }
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+} // namespace Arcane
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
diff --git a/arcane/src/arcane/std/MeshSection.axl b/arcane/src/arcane/std/MeshSection.axl
new file mode 100644
index 000000000..7e2f6628e
--- /dev/null
+++ b/arcane/src/arcane/std/MeshSection.axl
@@ -0,0 +1,21 @@
+
+
+
+
+ Service allowing the creation of a mesh with a section of another mesh.
+
+
+
+
+
+
+
+
+
+
+ UniqueID of service mesh. Useful if you need to create multiple instances of this service for a mesh.
+
+
+
+
+
diff --git a/arcane/src/arcane/std/MeshSectionService.cc b/arcane/src/arcane/std/MeshSectionService.cc
new file mode 100644
index 000000000..5ac1cb8d6
--- /dev/null
+++ b/arcane/src/arcane/std/MeshSectionService.cc
@@ -0,0 +1,274 @@
+// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
+//-----------------------------------------------------------------------------
+// Copyright 2000-2026 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com)
+// See the top-level COPYRIGHT file for details.
+// SPDX-License-Identifier: Apache-2.0
+//-----------------------------------------------------------------------------
+/*---------------------------------------------------------------------------*/
+/* MeshSectionService.cc (C) 2000-2026 */
+/* */
+/* Service allowing the creation of a mesh with a section of another mesh. */
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+#include "arcane/core/IMesh.h"
+#include "arcane/core/IMeshFactoryMng.h"
+#include "arcane/core/IMeshMng.h"
+#include "arcane/core/IMeshModifier.h"
+#include "arcane/core/IMeshSection.h"
+#include "arcane/core/IPrimaryMesh.h"
+#include "arcane/core/MeshBuildInfo.h"
+
+#include "arcane/std/MeshSection_axl.h"
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+namespace Arcane
+{
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+/*!
+ * \brief Service allowing the creation of a mesh with a section of another
+ * mesh.
+ *
+ * You can define a section of mesh with the method \a addPlan(). All cells
+ * (their barycenter) between these planes will be copied in a second mesh
+ * created by this service. You can get this mesh with the method
+ * \a meshSection().
+ */
+class MeshSectionService
+: public ArcaneMeshSectionObject
+{
+ public:
+
+ explicit MeshSectionService(const ServiceBuildInfo& sbi)
+ : ArcaneMeshSectionObject(sbi)
+ , m_creation_type(sbi.creationType())
+ {}
+
+ ~MeshSectionService() override = default;
+
+ public:
+
+ void addPlane(const Real3& p0, const Real3& normal) override;
+
+ void setVariables(VariableCollection variables) override;
+ VariableCollection variables() override { return {}; }
+ void setServiceMeshUniqueId(Int32 unique_id) override;
+ void updateSection() override;
+
+ MeshHandle meshSection() override
+ {
+ return m_cloned_mesh->handle();
+ }
+
+ private:
+
+ void _createMesh();
+ void _createCells(Int32& nb_cell, UniqueArray& cells_infos, Int32& nb_face, UniqueArray& faces_infos, std::unordered_map& pos_node);
+ void _compute();
+
+ private:
+
+ // VariableCollection m_variables_ori;
+ IPrimaryMesh* m_cloned_mesh = nullptr;
+ UniqueArray> m_plans;
+ Int32 m_mesh_uid = -1;
+ eServiceType m_creation_type;
+};
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+ARCANE_REGISTER_SERVICE_MESHSECTION(MeshSection, MeshSectionService);
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+void MeshSectionService::
+addPlane(const Real3& p0, const Real3& normal)
+{
+ m_plans.add({ p0, math::normalizeReal3(normal) });
+}
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+void MeshSectionService::
+setVariables(VariableCollection variables)
+{
+ ARCANE_UNUSED(variables);
+ ARCANE_NOT_YET_IMPLEMENTED("Not supported yet");
+ // m_variables_ori = variables;
+}
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+void MeshSectionService::
+setServiceMeshUniqueId(Int32 unique_id)
+{
+ m_mesh_uid = unique_id;
+}
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+void MeshSectionService::
+updateSection()
+{
+ _createMesh();
+ _compute();
+}
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+void MeshSectionService::
+_createMesh()
+{
+ if (m_cloned_mesh != nullptr) {
+ m_cloned_mesh->modifier()->clearItems();
+ }
+
+ IMeshMng* mm = subDomain()->meshMng();
+
+ if (m_mesh_uid == -1) {
+ if (m_creation_type == ST_CaseOption) {
+ m_mesh_uid = options()->getUniqueIdServiceMesh();
+ }
+ else {
+ m_mesh_uid = 0;
+ }
+ }
+
+ String service_mesh_name = mesh()->name() + "_MeshSection" + m_mesh_uid;
+
+ MeshHandle* mesh_handle = mm->findMeshHandle(service_mesh_name, false);
+
+ if (mesh_handle == nullptr) {
+ IParallelMng* pm = subDomain()->parallelMng();
+ MeshBuildInfo mbi(service_mesh_name);
+ mbi.addParallelMng(makeRef(pm));
+ m_cloned_mesh = mm->meshFactoryMng()->createMesh(mbi);
+ m_cloned_mesh->modifier()->setDynamic(true);
+ m_cloned_mesh->setDimension(mesh()->dimension());
+ m_cloned_mesh->endAllocate();
+ }
+ else {
+ m_cloned_mesh = mesh_handle->mesh()->toPrimaryMesh();
+ m_cloned_mesh->modifier()->clearItems();
+ }
+}
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+void MeshSectionService::
+_createCells(Int32& sd_nb_cell, UniqueArray& cells_infos, Int32& sd_nb_face, UniqueArray& faces_infos, std::unordered_map& pos_node)
+{
+ VariableNodeReal3& node_coord = mesh()->nodesCoordinates();
+
+ VariableFaceBool is_added(VariableBuildInfo(mesh(), "IsAdded"));
+ is_added.fill(false);
+
+ ENUMERATE_ (Cell, icell, ownCells()) {
+ {
+ Real3 b{ 0 };
+ for (Node node : icell->nodes()) {
+ b += node_coord[node];
+ }
+ b /= icell->nbNode();
+
+ bool in_plan = true;
+ for (auto& [p0, normal] : m_plans) {
+ const Real dist = math::dot({ b - p0 }, normal);
+ if (dist < 0) {
+ in_plan = false;
+ break;
+ }
+ }
+
+ if (!in_plan)
+ continue;
+ }
+
+ Int16 cell_type = icell->itemTypeId();
+ cells_infos.add(cell_type);
+
+ Int64 cell_uid = icell->uniqueId().asInt64();
+ cells_infos.add(cell_uid);
+
+ for (Node node : icell->nodes()) {
+ Int64 node_uid = node.uniqueId().asInt64();
+ cells_infos.add(node_uid);
+ pos_node[node.uniqueId()] = node_coord[node];
+ }
+ ++sd_nb_cell;
+
+ for (Face face : icell->faces()) {
+ if (is_added[face]) continue;
+ is_added[face] = true;
+
+ Int16 face_type = face.itemTypeId();
+ faces_infos.add(face_type);
+
+ Int64 face_uid = face.uniqueId().asInt64();
+ faces_infos.add(face_uid);
+
+ for (Node node : face.nodes()) {
+ Int64 node_uid = node.uniqueId().asInt64();
+ faces_infos.add(node_uid);
+ pos_node[node.uniqueId()] = node_coord[node];
+ }
+ ++sd_nb_face;
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+void MeshSectionService::
+_compute()
+{
+ UniqueArray cells_infos;
+ cells_infos.reserve(10000);
+
+ UniqueArray faces_infos;
+ faces_infos.reserve(10000);
+
+ std::unordered_map coord_map;
+
+ Int32 nb_cell = 0;
+ Int32 nb_face = 0;
+
+ _createCells(nb_cell, cells_infos, nb_face, faces_infos, coord_map);
+
+ m_cloned_mesh->modifier()->addFaces(nb_face, faces_infos);
+ m_cloned_mesh->modifier()->addCells(nb_cell, cells_infos);
+ m_cloned_mesh->modifier()->endUpdate();
+
+ {
+ VariableNodeReal3& node_coords(m_cloned_mesh->nodesCoordinates());
+ ENUMERATE_ (Node, inode, m_cloned_mesh->allNodes()) {
+ node_coords[inode] = coord_map[inode->uniqueId()];
+ }
+ }
+
+ info() << "New mesh -- NbNode : " << m_cloned_mesh->nbNode() << " -- NbCells : " << m_cloned_mesh->nbCell();
+}
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+} // namespace Arcane
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
diff --git a/arcane/src/arcane/std/srcs.cmake b/arcane/src/arcane/std/srcs.cmake
index db40e67d1..ae250b4eb 100644
--- a/arcane/src/arcane/std/srcs.cmake
+++ b/arcane/src/arcane/std/srcs.cmake
@@ -56,6 +56,8 @@
ProfilingInfo.cc
ProfilingInfo.h
MasterModule.cc
+ MeshCutService.cc
+ MeshSectionService.cc
UnitTestModule.cc
ArcaneStdRegisterer.h
TextReader.h
@@ -155,4 +157,6 @@ set(AXL_FILES
SplitSDMeshPartitioner
KdiPostProcessor
VtkPolyhedralMeshIO
+ MeshCut
+ MeshSection
)
diff --git a/arcane/src/arcane/tests/CMakeLists.txt b/arcane/src/arcane/tests/CMakeLists.txt
index 746135d3d..fc4f48cf3 100644
--- a/arcane/src/arcane/tests/CMakeLists.txt
+++ b/arcane/src/arcane/tests/CMakeLists.txt
@@ -1422,6 +1422,12 @@ endif ()
# ----------------------------------------------------------------------------
+arcane_add_test(meshcut1 testMeshCut-1.arc "-m 1")
+arcane_add_test(meshcut2 testMeshCut-2.arc "-m 20")
+arcane_add_test(meshsection1 testMeshSection-1.arc)
+
+# ----------------------------------------------------------------------------
+
include(accelerator/tests.cmake)
include(cartesianmesh/tests.cmake)
include(geometry/tests.cmake)
diff --git a/arcane/src/arcane/tests/MeshCutTest.axl b/arcane/src/arcane/tests/MeshCutTest.axl
new file mode 100644
index 000000000..23d2976c9
--- /dev/null
+++ b/arcane/src/arcane/tests/MeshCutTest.axl
@@ -0,0 +1,26 @@
+
+
+
+ Test module
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/arcane/src/arcane/tests/MeshCutTest.cc b/arcane/src/arcane/tests/MeshCutTest.cc
new file mode 100644
index 000000000..a7aad16f3
--- /dev/null
+++ b/arcane/src/arcane/tests/MeshCutTest.cc
@@ -0,0 +1,220 @@
+// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
+//-----------------------------------------------------------------------------
+// Copyright 2000-2026 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com)
+// See the top-level COPYRIGHT file for details.
+// SPDX-License-Identifier: Apache-2.0
+//-----------------------------------------------------------------------------
+/*---------------------------------------------------------------------------*/
+/* MeshCutTest.cc (C) 2000-2026 */
+/* */
+/* MeshCut test service. */
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+#include "arcane/utils/List.h"
+#include "arcane/utils/Real3.h"
+
+#include "arcane/core/Directory.h"
+#include "arcane/core/IMeshSection.h"
+#include "arcane/core/IPostProcessorWriter.h"
+#include "arcane/core/ITimeLoop.h"
+#include "arcane/core/ITimeLoopMng.h"
+#include "arcane/core/IVariableMng.h"
+#include "arcane/core/ServiceBuilder.h"
+#include "arcane/core/TimeLoopEntryPointInfo.h"
+
+#include "arcane/tests/MeshCutTest_axl.h"
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+namespace ArcaneTest
+{
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+using namespace Arcane;
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+class MeshCutTest
+: public ArcaneMeshCutTestObject
+{
+ public:
+
+ explicit MeshCutTest(const ModuleBuildInfo& mbi);
+ ~MeshCutTest() override = default;
+
+public:
+
+ static void staticInitialize(ISubDomain* sd);
+
+ public:
+
+ void compute() override;
+
+ private:
+
+ UniqueArray times;
+};
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+ARCANE_REGISTER_MODULE_MESHCUTTEST(MeshCutTest);
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+MeshCutTest::
+MeshCutTest(const ModuleBuildInfo& mbi)
+: ArcaneMeshCutTestObject(mbi)
+{}
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+void MeshCutTest::
+staticInitialize(ISubDomain* sd)
+{
+ String time_loop_name("MeshCutTestLoop");
+
+ ITimeLoopMng* tlm = sd->timeLoopMng();
+ ITimeLoop* time_loop = tlm->createTimeLoop(time_loop_name);
+
+ {
+ List clist;
+ clist.add(TimeLoopEntryPointInfo("MeshCutTest.Compute"));
+ time_loop->setEntryPoints(ITimeLoop::WComputeLoop, clist);
+ }
+
+ {
+ StringList clist;
+ clist.add("MeshCutTest");
+ time_loop->setRequiredModulesName(clist);
+ clist.clear();
+ clist.add("ArcanePostProcessing");
+ clist.add("ArcaneCheckpoint");
+ time_loop->setOptionalModulesName(clist);
+ }
+
+ tlm->registerTimeLoop(time_loop);
+}
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+void MeshCutTest::
+compute()
+{
+ {
+ m_global_time = globalIteration();
+ times.add(m_global_time());
+ }
+
+ //
+ // MeshHandle meshhsection;
+ // {
+ // ServiceBuilder spp0(mesh()->handle());
+ // Ref pp0 = spp0.createReference("MeshSectionService");
+ //
+ // pp0->addPlane({0.95, 0, 0}, {1, 0, 0});
+ // pp0->addPlane({0.97, 0, 0}, {-1, 0, 0});
+ //
+ // pp0->updateSection();
+ // meshhsection = pp0->meshSection();
+ // }
+ // IMesh* meshsection = meshhsection.mesh();
+ //
+ // if (options()->enablePostProcessing())
+ // {
+ // ServiceBuilder spp(meshhsection);
+ // Ref pp = spp.createReference("VtkHdfV2PostProcessor");
+ // Directory output_directory = Directory(subDomain()->exportDirectory(), "amrtestpost1");
+ // pp->setBaseDirectoryName(output_directory.path());
+ // IPostProcessorWriter* post_processor = pp.get();
+ // post_processor->setTimes(times);
+ //
+ // VariableList variables;
+ // variables.add(meshsection->nodesCoordinates().variable());
+ // post_processor->setVariables(variables);
+ //
+ // ItemGroupList groups;
+ // groups.add(meshsection->allNodes());
+ // post_processor->setGroups(groups);
+ //
+ // IVariableMng* vm = meshsection->variableMng();
+ // vm->writePostProcessing(post_processor);
+ // }
+
+
+ MeshHandle meshhcut;
+ {
+ ServiceBuilder spp0(mesh()->handle());
+ // ServiceBuilder spp0(meshhsection);
+ Ref pp0 = spp0.createReference("MeshCutService");
+
+ for (auto plane : options()->plane()) {
+ pp0->addPlane(plane->p0() + (plane->p0Velocity() * globalIteration()), plane->normal());
+ }
+
+ pp0->updateSection();
+ meshhcut = pp0->meshSection();
+ }
+ IMesh* meshcut = meshhcut.mesh();
+
+
+ if (options()->enablePostProcessing())
+ {
+ ServiceBuilder spp(meshhcut);
+ Ref pp = spp.createReference("VtkHdfV2PostProcessor");
+ Directory output_directory = Directory(subDomain()->exportDirectory(), "amrtestpost1");
+ output_directory.createDirectory();
+ pp->setBaseDirectoryName(output_directory.path());
+ IPostProcessorWriter* post_processor = pp.get();
+ post_processor->setTimes(times);
+
+ VariableList variables;
+ variables.add(meshcut->nodesCoordinates().variable());
+ post_processor->setVariables(variables);
+
+ ItemGroupList groups;
+ groups.add(meshcut->allNodes());
+ post_processor->setGroups(groups);
+
+ IVariableMng* vm = meshcut->variableMng();
+ vm->writePostProcessing(post_processor);
+ }
+
+ if (options()->enablePostProcessing())
+ {
+ ServiceBuilder spp(mesh()->handle());
+ Ref pp = spp.createReference("VtkHdfV2PostProcessor");
+ Directory output_directory = Directory(subDomain()->exportDirectory(), "amrtestpost1");
+ output_directory.createDirectory();
+ pp->setBaseDirectoryName(output_directory.path());
+ IPostProcessorWriter* post_processor = pp.get();
+ post_processor->setTimes(times);
+
+ VariableList variables;
+ variables.add(mesh()->nodesCoordinates().variable());
+ post_processor->setVariables(variables);
+
+ ItemGroupList groups;
+ groups.add(mesh()->allNodes());
+ post_processor->setGroups(groups);
+
+ IVariableMng* vm = mesh()->variableMng();
+ vm->writePostProcessing(post_processor);
+ }
+}
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+} // namespace ArcaneTest
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
diff --git a/arcane/src/arcane/tests/MeshSectionTest.axl b/arcane/src/arcane/tests/MeshSectionTest.axl
new file mode 100644
index 000000000..95386ad38
--- /dev/null
+++ b/arcane/src/arcane/tests/MeshSectionTest.axl
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/arcane/src/arcane/tests/MeshSectionTest.cc b/arcane/src/arcane/tests/MeshSectionTest.cc
new file mode 100644
index 000000000..46421e522
--- /dev/null
+++ b/arcane/src/arcane/tests/MeshSectionTest.cc
@@ -0,0 +1,151 @@
+// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
+//-----------------------------------------------------------------------------
+// Copyright 2000-2026 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com)
+// See the top-level COPYRIGHT file for details.
+// SPDX-License-Identifier: Apache-2.0
+//-----------------------------------------------------------------------------
+/*---------------------------------------------------------------------------*/
+/* MeshSectionTest.cc (C) 2000-2026 */
+/* */
+/* MeshSection test service. */
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+#include "arcane/utils/List.h"
+
+#include "arcane/core/BasicUnitTest.h"
+#include "arcane/core/Directory.h"
+#include "arcane/core/IMeshSection.h"
+#include "arcane/core/IPostProcessorWriter.h"
+#include "arcane/core/IVariableMng.h"
+#include "arcane/core/ServiceBuilder.h"
+
+#include "arcane/tests/MeshSectionTest_axl.h"
+
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+namespace ArcaneTest
+{
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+using namespace Arcane;
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+class MeshSectionTest
+: public ArcaneMeshSectionTestObject
+{
+ public:
+
+ explicit MeshSectionTest(const ServiceBuildInfo& sbi);
+ ~MeshSectionTest() override = default;
+
+ public:
+
+ void initializeTest() override;
+ void executeTest() override;
+
+private:
+
+ UniqueArray times;
+};
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+ARCANE_REGISTER_SERVICE_MESHSECTIONTEST(MeshSectionTest, MeshSectionTest);
+
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+MeshSectionTest::
+MeshSectionTest(const ServiceBuildInfo& sbi)
+: ArcaneMeshSectionTestObject(sbi)
+{}
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+void MeshSectionTest::
+initializeTest()
+{
+}
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+void MeshSectionTest::
+executeTest()
+{
+ times.add(m_global_time());
+
+ ServiceBuilder spp0(mesh()->handle());
+ Ref pp0 = spp0.createReference("MeshSectionService");
+
+ for (auto plane : options()->plane()) {
+ pp0->addPlane(plane->p0(), plane->normal());
+ }
+
+ pp0->updateSection();
+ MeshHandle meshsh = pp0->meshSection();
+ IMesh* meshs = meshsh.mesh();
+
+
+ if (options()->enablePostProcessing())
+ {
+ ServiceBuilder spp(meshsh);
+ Ref pp = spp.createReference("VtkHdfV2PostProcessor");
+ Directory output_directory = Directory(subDomain()->exportDirectory(), "amrtestpost1");
+ output_directory.createDirectory();
+ pp->setBaseDirectoryName(output_directory.path());
+ IPostProcessorWriter* post_processor = pp.get();
+ post_processor->setTimes(times);
+
+ VariableList variables;
+ variables.add(meshs->nodesCoordinates().variable());
+ post_processor->setVariables(variables);
+
+ ItemGroupList groups;
+ groups.add(meshs->allNodes());
+ post_processor->setGroups(groups);
+
+ IVariableMng* vm = meshs->variableMng();
+ vm->writePostProcessing(post_processor);
+ }
+
+ if (options()->enablePostProcessing())
+ {
+ ServiceBuilder spp(mesh()->handle());
+ Ref pp = spp.createReference("VtkHdfV2PostProcessor");
+ Directory output_directory = Directory(subDomain()->exportDirectory(), "amrtestpost1");
+ output_directory.createDirectory();
+ pp->setBaseDirectoryName(output_directory.path());
+ IPostProcessorWriter* post_processor = pp.get();
+ post_processor->setTimes(times);
+
+ VariableList variables;
+ variables.add(mesh()->nodesCoordinates().variable());
+ post_processor->setVariables(variables);
+
+ ItemGroupList groups;
+ groups.add(mesh()->allNodes());
+ post_processor->setGroups(groups);
+
+ IVariableMng* vm = mesh()->variableMng();
+ vm->writePostProcessing(post_processor);
+ }
+}
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+
+} // namespace ArcaneTest
+
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
diff --git a/arcane/src/arcane/tests/srcs.cmake b/arcane/src/arcane/tests/srcs.cmake
index 457b707dd..1dae6cf5a 100644
--- a/arcane/src/arcane/tests/srcs.cmake
+++ b/arcane/src/arcane/tests/srcs.cmake
@@ -89,6 +89,8 @@ set(ARCANE_SOURCES
HyodaMixedCellsUnitTest.cc
HllcSchemeTypes.h
HllcSchemeModule.cc
+ MeshCutTest.cc
+ MeshSectionTest.cc
)
set(ARCANE_ACCELERATOR_SOURCES
ArcaneTestStandaloneSubDomain.cc
@@ -146,4 +148,6 @@ set(AXL_FILES
RayMeshIntersectionUnitTest
HyodaMixedCellsUnitTest
HllcScheme
+ MeshCutTest
+ MeshSectionTest
)
diff --git a/arcane/tests/testMeshCut-1.arc b/arcane/tests/testMeshCut-1.arc
new file mode 100644
index 000000000..9434d6330
--- /dev/null
+++ b/arcane/tests/testMeshCut-1.arc
@@ -0,0 +1,38 @@
+
+
+
+ Test MeshCut 1
+ Test MeshCut 1
+ MeshCutTestLoop
+
+
+
+
+
+ 2
+ 2
+ 1
+ 0.0 0.0 0.0
+ 22.0
+ 22.0
+ 22.0
+
+
+
+
+
+ false
+
+ 1 1 1
+ 1 0 0
+
+
+ 1 1 1
+ 1 0.5 0
+
+
+ 1.6 0 0
+ 1 0.5 0.1
+
+
+
diff --git a/arcane/tests/testMeshCut-2.arc b/arcane/tests/testMeshCut-2.arc
new file mode 100644
index 000000000..154b5c418
--- /dev/null
+++ b/arcane/tests/testMeshCut-2.arc
@@ -0,0 +1,27 @@
+
+
+
+ Test MeshCut 1
+ Test MeshCut 1
+ MeshCutTestLoop
+
+
+
+
+ sod3d-misc.msh
+
+
+
+
+ false
+
+ 0 0.05 0
+ 0 1 0
+
+
+ 0.98 0 0
+ 0 -0.1 0
+ 1 0.5 0.1
+
+
+
diff --git a/arcane/tests/testMeshSection-1.arc b/arcane/tests/testMeshSection-1.arc
new file mode 100644
index 000000000..89f7af02c
--- /dev/null
+++ b/arcane/tests/testMeshSection-1.arc
@@ -0,0 +1,28 @@
+
+
+
+ Test MeshCut 1
+ Test MeshCut 1
+ UnitTest
+
+
+
+
+ sod3d-misc.msh
+
+
+
+
+
+
+ 0.4 0 0
+ 1 0 0
+
+
+ 0.6 0 0
+ -1 0 0
+
+
+
+
+