Salome HOME
[EDF27860] : MEDCouplingUMesh.getCellsContainingPoints eps parameter specifies a...
[tools/medcoupling.git] / src / MEDCoupling / MEDCouplingUMesh.cxx
index a9181f3e7dcf75f00bab788a8e1c127594efd5a5..6d2ad7a88fe6025c15336e6cb6086beb81449664 100755 (executable)
@@ -1,4 +1,4 @@
-// Copyright (C) 2007-2021  CEA/DEN, EDF R&D
+// Copyright (C) 2007-2023  CEA, EDF
 //
 // This library is free software; you can redistribute it and/or
 // modify it under the terms of the GNU Lesser General Public
@@ -697,6 +697,44 @@ MEDCouplingUMesh *MEDCouplingUMesh::buildDescendingConnectivity(DataArrayIdType
   return buildDescendingConnectivityGen<MinusOneSonsGenerator>(desc,descIndx,revDesc,revDescIndx,MEDCouplingFastNbrer);
 }
 
+/*!
+ * This method is a generalization of buildDescendingConnectivity method. This method return mesh containing subcells of this at level specified by \a targetDeltaLevel
+ * and return descending and reverse descending correspondances to this.
+ *
+ * \param [in] targetDeltaLevel target delta level compared to \a this mesh dimension. This parameter is expected to be lower than zero.
+ * 
+ * \throw If targetDeltaLevel is greater or equal to zero
+ * \throw If targetDeltaLevel is lower than -meshDim
+ * \sa MEDCouplingUMesh::buildDescendingConnectivity, MEDCouplingUMesh::explode3DMeshTo1D
+ */
+MCAuto<MEDCouplingUMesh> MEDCouplingUMesh::explodeMeshTo(int targetDeltaLevel, MCAuto<DataArrayIdType>& desc, MCAuto<DataArrayIdType>& descIndx, MCAuto<DataArrayIdType>& revDesc, MCAuto<DataArrayIdType>& revDescIndx) const
+{
+  this->checkConsistencyLight();
+  if( targetDeltaLevel >= 0 )
+    THROW_IK_EXCEPTION("Input parameter targetDeltaLevel is expected to be lower than zero !");
+  if( targetDeltaLevel == -1 )
+  {
+    desc = DataArrayIdType::New(); descIndx = DataArrayIdType::New(); revDesc = DataArrayIdType::New(); revDescIndx = DataArrayIdType::New();
+    MCAuto<MEDCouplingUMesh> ret( this->buildDescendingConnectivity(desc,descIndx,revDesc,revDescIndx) );
+    return ret;
+  }
+  if( targetDeltaLevel == -2 && this->getMeshDimension() == 3 )
+  {
+    desc = DataArrayIdType::New(); descIndx = DataArrayIdType::New(); revDesc = DataArrayIdType::New(); revDescIndx = DataArrayIdType::New();
+    MCAuto<MEDCouplingUMesh> ret( this->explode3DMeshTo1D(desc,descIndx,revDesc,revDescIndx) );
+    return ret;
+  }
+  if( targetDeltaLevel == -this->getMeshDimension() )
+  {
+    MCAuto<MEDCouplingUMesh> ret = MEDCouplingUMesh::Build0DMeshFromCoords( const_cast<DataArrayDouble *>( this->getCoords() ) );
+    MEDCouplingUMesh::DeleteCellTypeInIndexedArray(getNodalConnectivity(),getNodalConnectivityIndex(),desc,descIndx);
+    revDesc = DataArrayIdType::New(); revDescIndx = DataArrayIdType::New();
+    this->getReverseNodalConnectivity(revDesc,revDescIndx);
+    return ret;
+  }
+  THROW_IK_EXCEPTION("Not valid input targetDeltaLevel regarding mesh dimension");
+}
+
 /*!
  * \a this has to have a mesh dimension equal to 3. If it is not the case an INTERP_KERNEL::Exception will be thrown.
  * This behaves exactly as MEDCouplingUMesh::buildDescendingConnectivity does except that this method compute directly the transition from mesh dimension 3 to sub edges (dimension 1)
@@ -1340,6 +1378,8 @@ bool MEDCouplingUMesh::unPolyze()
  * This method performs operation only on polyhedrons in \b this. If no polyhedrons exists in \b this, \b this remains unchanged.
  * This method allows to merge if any coplanar 3DSurf cells that may appear in some polyhedrons cells.
  *
+ * \b WARNING: this method will not modify edges connectivity! Take a look at colinearizeEdges for that.
+ *
  * \param [in] eps is a relative precision that allows to establish if some 3D plane are coplanar or not. This epsilon is used to recenter around origin to have maximal
  *             precision.
  */
@@ -1376,6 +1416,84 @@ void MEDCouplingUMesh::simplifyPolyhedra(double eps)
     setConnectivity(connNew,connINew,false);
 }
 
+/*!
+ * This method expects that spaceDimension is equal to 3 and meshDimension equal to 3.
+ * This method performs operation only on polyhedrons in \b this. If no polyhedrons exists in \b this, \b this remains unchanged.
+ * This method allows to simplify edges of polyhedron cells so that consecutive colinear segments (with intermediate points
+ * not used by any other cell) are merged together.
+ *
+ * \param [in] eps is a relative precision that allows to establish if two consecutive 3D segments are colinear or not.
+ *
+ * \sa simplifyPolyhedra
+ */
+void MEDCouplingUMesh::colinearizeEdges(double eps)
+{
+  //
+  // Thanks to Antoine Gerschenfeld (CEA) for contributing this method!
+  //
+  using DAI = MCAuto<DataArrayIdType>;
+  checkFullyDefined();
+  if(getMeshDimension()!=3 || getSpaceDimension()!=3)
+    throw INTERP_KERNEL::Exception("MEDCouplingUMesh::colinearizeEdges() : works with meshdim=3 and spaceDim=3!");
+  double seps = sqrt(1-eps);
+  // Computing connectivities and correspondances : elements -> segments -> points
+  DAI E_Fi(DataArrayIdType::New()), E_F(DataArrayIdType::New()), F_Ei(DataArrayIdType::New()), F_E(DataArrayIdType::New()),
+         F_Si(DataArrayIdType::New()), F_S(DataArrayIdType::New()), S_Fi(DataArrayIdType::New()), S_F(DataArrayIdType::New()),
+         S_Pi(DataArrayIdType::New()), S_P(DataArrayIdType::New()), P_Si(DataArrayIdType::New()), P_S(DataArrayIdType::New());
+  MCAuto<MEDCouplingUMesh> m_f(buildDescendingConnectivity(E_F, E_Fi, F_E, F_Ei)),
+         m_s(m_f->buildDescendingConnectivity(F_S, F_Si, S_F, S_Fi)),
+         m_p(m_s->buildDescendingConnectivity(S_P, S_Pi, P_S, P_Si)); // E: elem, F: faces, S: segments (edges), P: points (vertices)
+  const mcIdType *S_Pp(S_P->begin()), *S_Pip(S_Pi->begin()), *P_Sp(P_S->begin()), *P_Sip(P_Si->begin());
+  std::set<mcIdType> pt_rem;
+  const mcIdType *m_pi = m_p->getNodalConnectivityIndex()->begin(),
+                 *m_pc = m_p->getNodalConnectivity()->begin();
+  double (*coord)[3] = (double (*)[3]) getCoords()->begin();
+  // Find all points only connected to exaclty 2 segments - they are the candidates for elimination
+  // Note that in 3D this can only happen for polyhedrons (when this happens at all)
+  DAI dsi = P_Si->deltaShiftIndex();
+  DAI cand = dsi->findIdsEqual(2);
+  for (const mcIdType& i: *cand)  // i is a point to be potentially eliminated, shared by 2 segs only
+    {
+      double n2[2] = {0., 0.}, scal = 0.; // n2 is a squared norm, scal is a scalar product
+      mcIdType p[2][2];                   // p[j][k] is the ID (in the coord array) of the k-th point of the j-th segment
+      for (mcIdType j = 0; j < 2; j++)
+        for (mcIdType k = 0; k < 2; k++)
+          {
+            mcIdType off1 = P_Sip[i] + j;   // offset to get ID of the j-th seg (around the i-th point) in the point->seg correspondance
+            mcIdType pt_id = P_Sp[off1] + k; // ID of the k-th point of the j-th seg in the point->seg correspondance
+            mcIdType pt_id2 = S_Pp[S_Pip[pt_id]]; // ID of the point in the point mesh
+            p[j][k] = m_pc[m_pi[pt_id2] + 1];  // Absolute ID, as read from the connectvity (+1 to skip type: NORM_POINT1)
+            // Just for fun, as initially written by Antoine :-)
+            // p[j][k] = m_pc[m_pi[S_P->getIJ(S_Pi->getIJ(P_S->getIJ(P_Si->getIJ(i, 0) + j, 0), 0) + k, 0)] + 1];
+          }
+      // Geometric test on scalar product
+      for (int d = 0; d < 3; d++) // dimension
+        {
+          for (int j = 0; j < 2; j++)
+            n2[j] += std::pow(coord[p[j][1]][d] - coord[p[j][0]][d], 2);
+          scal += (coord[p[1][1]][d] - coord[p[1][0]][d]) * (coord[p[0][1]][d] - coord[p[0][0]][d]);
+        }
+      if (scal * scal > seps * n2[0] * n2[1]) // seps is a sqrt for homogeneity
+        pt_rem.insert(m_pc[m_pi[i] + 1]);  // point should be removed
+    }
+  // Clean connectivity by filtering points to be removed:
+  DataArrayIdType *old_index = getNodalConnectivityIndex(), *old_conn = getNodalConnectivity();
+  DAI new_index(DataArrayIdType::New()), new_conn(DataArrayIdType::New());
+  const mcIdType *old_index_p(old_index->begin()), *old_conn_p(old_conn->begin());
+  for (mcIdType i = 0; i < getNumberOfCells(); i++)
+    {
+      new_index->pushBackSilent(new_conn->getNbOfElems());
+      for (mcIdType j = old_index_p[i]; j < old_index_p[i + 1]; j++)
+        {
+          // Keep point if it is not to be removed, or if is first in connectivity (TODO this last check could be removed?)
+          if (std::find(pt_rem.begin(), pt_rem.end(), old_conn_p[j]) == pt_rem.end() || j == old_index_p[i])
+            new_conn->pushBackSilent(old_conn_p[j]);
+        }
+    }
+  new_index->pushBackSilent(new_conn->getNbOfElems());
+  setConnectivity(new_conn, new_index);
+}
+
 /*!
  * This method returns all node ids used in the connectivity of \b this. The data array returned has to be dealt by the caller.
  * The returned node ids are sorted ascendingly. This method is close to MEDCouplingUMesh::getNodeIdsInUse except
@@ -2229,10 +2347,15 @@ MEDCouplingUMesh *MEDCouplingUMesh::buildBoundaryMesh(bool keepCoords) const
 DataArrayIdType *MEDCouplingUMesh::findCellIdsOnBoundary() const
 {
   checkFullyDefined();
-  MCAuto<DataArrayIdType> desc=DataArrayIdType::New();
-  MCAuto<DataArrayIdType> descIndx=DataArrayIdType::New();
-  MCAuto<DataArrayIdType> revDesc=DataArrayIdType::New();
-  MCAuto<DataArrayIdType> revDescIndx=DataArrayIdType::New();
+  MCAuto<DataArrayIdType> ret2(DataArrayIdType::New());
+
+  if (getNumberOfCells() == 0)
+    {
+      ret2->alloc(0,1);
+      return ret2.retn();
+    }
+
+  MCAuto<DataArrayIdType> desc(DataArrayIdType::New()), descIndx(DataArrayIdType::New()), revDesc(DataArrayIdType::New()), revDescIndx(DataArrayIdType::New());
   //
   buildDescendingConnectivity(desc,descIndx,revDesc,revDescIndx)->decrRef();
   desc=(DataArrayIdType*)0; descIndx=(DataArrayIdType*)0;
@@ -2248,7 +2371,6 @@ DataArrayIdType *MEDCouplingUMesh::findCellIdsOnBoundary() const
     if(!ret1[revDescPtr[revDescIndxPtr[*pt]]])
       { ret1[revDescPtr[revDescIndxPtr[*pt]]]=true; sz++; }
   //
-  DataArrayIdType *ret2=DataArrayIdType::New();
   ret2->alloc(sz,1);
   mcIdType *ret2Ptr=ret2->getPointer();
   sz=0;
@@ -2256,7 +2378,7 @@ DataArrayIdType *MEDCouplingUMesh::findCellIdsOnBoundary() const
     if(*it)
       *ret2Ptr++=sz;
   ret2->setName("BoundaryCells");
-  return ret2;
+  return ret2.retn();
 }
 
 /*!
@@ -2354,16 +2476,16 @@ MEDCouplingUMesh *MEDCouplingUMesh::buildUnstructured() const
 /*!
  * This method expects that \b this and \b otherDimM1OnSameCoords share the same coordinates array.
  * otherDimM1OnSameCoords->getMeshDimension() is expected to be equal to this->getMeshDimension()-1.
- * This method searches for nodes needed to be duplicated. These nodes are nodes fetched by \b otherDimM1OnSameCoords which are not part of the boundary of \b otherDimM1OnSameCoords.
- * If a node is in the boundary of \b this \b and in the boundary of \b otherDimM1OnSameCoords this node is considered as needed to be duplicated.
+ * This method searches for nodes needed to be duplicated. Those are the nodes fetched by \b otherDimM1OnSameCoords which are not part of the boundary of \b otherDimM1OnSameCoords.
+ * If a node is in the boundary of \b this \b and in the boundary of \b otherDimM1OnSameCoords, it will be duplicated.
  * When the set of node ids \b nodeIdsToDuplicate is computed, cell ids in \b this is searched so that their connectivity includes at least 1 node in \b nodeIdsToDuplicate.
  *
- * \param [in] otherDimM1OnSameCoords a mesh lying on the same coords than \b this and with a mesh dimension equal to those of \b this minus 1. WARNING this input
+ * \param [in] crackingMesh a mesh lying on the same coords than \b this and with a mesh dimension equal to those of \b this minus 1. WARNING this input
  *             parameter is altered during the call.
  * \return node ids which need to be duplicated following the algorithm explained above.
  *
  */
-DataArrayIdType* MEDCouplingUMesh::findNodesToDuplicate(const MEDCouplingUMesh& otherDimM1OnSameCoords) const
+DataArrayIdType* MEDCouplingUMesh::findNodesToDuplicate(const MEDCouplingUMesh& crackingMesh) const
 {
   // DEBUG NOTE: in case of issue with the algorithm in this method, see Python script in resources/dev
   // which mimicks the C++
@@ -2371,15 +2493,26 @@ DataArrayIdType* MEDCouplingUMesh::findNodesToDuplicate(const MEDCouplingUMesh&
   using MCUMesh = MCAuto<MEDCouplingUMesh>;
 
   checkFullyDefined();
-  otherDimM1OnSameCoords.checkFullyDefined();
-  if(getCoords()!=otherDimM1OnSameCoords.getCoords())
+  crackingMesh.checkFullyDefined();
+  if(getCoords()!=crackingMesh.getCoords())
     throw INTERP_KERNEL::Exception("MEDCouplingUMesh::findNodesToDuplicate : meshes do not share the same coords array !");
-  if(otherDimM1OnSameCoords.getMeshDimension()!=getMeshDimension()-1)
+  if(crackingMesh.getMeshDimension()!=getMeshDimension()-1)
     throw INTERP_KERNEL::Exception("MEDCouplingUMesh::findNodesToDuplicate : the mesh given in other parameter must have this->getMeshDimension()-1 !");
 
+  // Clean the M1 group (cracking mesh): the M1 cells which are part of M0 boundary are irrelevant (we can't create a crack on the boundary of M0!)
+  MCUMesh m0skin = computeSkin();
+  DataArrayIdType *idsToKeepP;
+  m0skin->areCellsIncludedIn(&crackingMesh,2, idsToKeepP);
+  DAInt idsToKeep(idsToKeepP);
+  DAInt ids2 = idsToKeep->findIdsNotInRange(0, m0skin->getNumberOfCells());  // discard cells on the skin of M0
+  MCUMesh otherDimM1OnSameCoords =static_cast<MEDCouplingUMesh *>(crackingMesh.buildPartOfMySelf(ids2->begin(), ids2->end(), true));
+
+  if (!otherDimM1OnSameCoords->getNumberOfCells())
+    return MCAuto<DataArrayIdType>(DataArrayIdType::New()).retn();
+
   // Checking star-shaped M1 group:
   DAInt dt0=DataArrayIdType::New(),dit0=DataArrayIdType::New(),rdt0=DataArrayIdType::New(),rdit0=DataArrayIdType::New();
-  MCUMesh meshM2 = otherDimM1OnSameCoords.buildDescendingConnectivity(dt0, dit0, rdt0, rdit0); // 2D: a mesh of points, 3D: a mesh of segs
+  MCUMesh meshM2 = otherDimM1OnSameCoords->buildDescendingConnectivity(dt0, dit0, rdt0, rdit0); // 2D: a mesh of points, 3D: a mesh of segs
   DAInt dsi = rdit0->deltaShiftIndex();
   DAInt idsTmp0 = dsi->findIdsNotInRange(-1, 3);  // for 2D: if a point is connected to more than 2 segs. For 3D: if a seg is connected to more than two faces.
   if(idsTmp0->getNumberOfTuples())
@@ -2391,33 +2524,28 @@ DataArrayIdType* MEDCouplingUMesh::findNodesToDuplicate(const MEDCouplingUMesh&
   DAInt xtremIdsM2 = dsi->findIdsEqual(1); dsi = 0;
   MCUMesh meshM2Part = static_cast<MEDCouplingUMesh *>(meshM2->buildPartOfMySelf(xtremIdsM2->begin(), xtremIdsM2->end(),true));
   DAInt xtrem = meshM2Part->computeFetchedNodeIds();
-  // Remove from the list points on the boundary of the M0 mesh (those need duplication!)
-  dt0=DataArrayIdType::New(),dit0=DataArrayIdType::New(),rdt0=DataArrayIdType::New(),rdit0=DataArrayIdType::New();
-  MCUMesh m0desc = buildDescendingConnectivity(dt0, dit0, rdt0, rdit0); dt0=0; dit0=0; rdt0=0;
-  dsi = rdit0->deltaShiftIndex();  rdit0=0;
-  DAInt boundSegs = dsi->findIdsEqual(1);  dsi = 0; // boundary segs/faces of the M0 mesh
-  MCUMesh m0descSkin = static_cast<MEDCouplingUMesh *>(m0desc->buildPartOfMySelf(boundSegs->begin(),boundSegs->end(), true));
-  DAInt fNodes = m0descSkin->computeFetchedNodeIds();
-  // In 3D, some points on the boundary of M0 will NOT be duplicated (where as in 2D, points on the boundary of M0 are always duplicated)
-  // Think of a partial (plane) crack in a cube: the points at the tip of the crack and not located inside the volume of the cube are not duplicated
-  // although they are technically on the skin of the cube.
+  // Remove from the list points on the boundary of the M0 mesh (those need duplication!).
+  //    In 3D, some points on the boundary of M0 will NOT be duplicated (where as in 2D, points on the boundary of M0 are always duplicated)
+  //    Think of a partial (plane) crack in a cube: the points at the tip of the crack and not located inside the volume of the cube are not duplicated
+  //    although they are technically on the skin of the cube.
+  DAInt fNodes = m0skin->computeFetchedNodeIds();
   DAInt notDup = 0;
   if (getMeshDimension() == 3)
     {
       DAInt dnu1=DataArrayIdType::New(), dnu2=DataArrayIdType::New(), dnu3=DataArrayIdType::New(), dnu4=DataArrayIdType::New();
-      MCUMesh m0descSkinDesc = m0descSkin->buildDescendingConnectivity(dnu1, dnu2, dnu3, dnu4); // all segments of the skin of the 3D (M0) mesh
+      MCUMesh m0skinDesc = m0skin->buildDescendingConnectivity(dnu1, dnu2, dnu3, dnu4); // all segments of the skin of the 3D (M0) mesh
       dnu1=0;dnu2=0;dnu3=0;dnu4=0;
       DataArrayIdType * corresp=0;
-      meshM2->areCellsIncludedIn(m0descSkinDesc,2,corresp);
+      meshM2->areCellsIncludedIn(m0skinDesc,2,corresp);
       // validIds is the list of segments which are on both the skin of *this*, and in the segments of the M1 group
-      // In the cube example above, this is a U shape polyline.
+      // In the cube example above, this is a U-shaped polyline.
       DAInt validIds = corresp->findIdsInRange(0, meshM2->getNumberOfCells());
       corresp->decrRef();
       if (validIds->getNumberOfTuples())
         {
           // Build the set of segments which are: in the desc mesh of the skin of the 3D mesh (M0) **and** in the desc mesh of the M1 group:
           // (the U-shaped polyline described above)
-          MCUMesh m1IntersecSkin = static_cast<MEDCouplingUMesh *>(m0descSkinDesc->buildPartOfMySelf(validIds->begin(), validIds->end(), true));
+          MCUMesh m1IntersecSkin = static_cast<MEDCouplingUMesh *>(m0skinDesc->buildPartOfMySelf(validIds->begin(), validIds->end(), true));
           // Its boundary nodes should no be duplicated (this is for example the tip of the crack inside the cube described above)
           DAInt notDuplSkin = m1IntersecSkin->findBoundaryNodes();
           DAInt fNodes1 = fNodes->buildSubstraction(notDuplSkin);
@@ -2444,7 +2572,7 @@ DataArrayIdType* MEDCouplingUMesh::findNodesToDuplicate(const MEDCouplingUMesh&
               MCUMesh mAroundBNDesc = mAroundBN->buildDescendingConnectivity(descBN,descIBN,revDescBN,revDescIBN);
               //    2. Identify cells in sub-mesh mAroundBN which have a face in common with M1
               DataArrayIdType *idsOfM1BNt;
-              mAroundBNDesc->areCellsIncludedIn(&otherDimM1OnSameCoords,2, idsOfM1BNt);
+              mAroundBNDesc->areCellsIncludedIn(otherDimM1OnSameCoords,2, idsOfM1BNt);
               DAInt idsOfM1BN(idsOfM1BNt);
               mcIdType nCells=mAroundBN->getNumberOfCells(), nCellsDesc=mAroundBNDesc->getNumberOfCells();
               DAInt idsTouch=DataArrayIdType::New(); idsTouch->alloc(0,1);
@@ -2473,7 +2601,7 @@ DataArrayIdType* MEDCouplingUMesh::findNodesToDuplicate(const MEDCouplingUMesh&
   else  // if (3D ...)
     notDup = xtrem->buildSubstraction(fNodes);
 
-  DAInt m1Nodes = otherDimM1OnSameCoords.computeFetchedNodeIds();
+  DAInt m1Nodes = otherDimM1OnSameCoords->computeFetchedNodeIds();
   DAInt dupl = m1Nodes->buildSubstraction(notDup);
   return dupl.retn();
 }
@@ -2486,8 +2614,7 @@ DataArrayIdType* MEDCouplingUMesh::findNodesToDuplicate(const MEDCouplingUMesh&
  * Given a set of nodes to duplicate, this method identifies which cells should have their connectivity modified
  * to produce the inner boundary. It is typically called after findNodesToDuplicate().
  *
- * \param [in] otherDimM1OnSameCoords a mesh lying on the same coords than \b this and with a mesh dimension equal to those of \b this minus 1. WARNING this input
- *             parameter is altered during the call.
+ * \param [in] otherDimM1OnSameCoords a mesh lying on the same coords than \b this and with a mesh dimension equal to those of \b this minus 1.
  * \param [in] nodeIdsToDuplicateBg node ids needed to be duplicated, as returned by findNodesToDuplicate.
  * \param [in] nodeIdsToDuplicateEnd node ids needed to be duplicated, as returned by findNodesToDuplicate.
  * \param [out] cellIdsNeededToBeRenum cell ids in \b this in which the renumber of nodes should be performed.
@@ -2497,8 +2624,6 @@ DataArrayIdType* MEDCouplingUMesh::findNodesToDuplicate(const MEDCouplingUMesh&
 void MEDCouplingUMesh::findCellsToRenumber(const MEDCouplingUMesh& otherDimM1OnSameCoords, const mcIdType *nodeIdsToDuplicateBg, const mcIdType *nodeIdsToDuplicateEnd,
                                            DataArrayIdType *& cellIdsNeededToBeRenum, DataArrayIdType *& cellIdsNotModified) const
 {
-  // DEBUG NOTE: in case of issue with the algorithm in this method, see Python script in resources/dev
-  // which mimicks the C++
   using DAInt = MCAuto<DataArrayIdType>;
   using MCUMesh = MCAuto<MEDCouplingUMesh>;
 
@@ -2509,192 +2634,87 @@ void MEDCouplingUMesh::findCellsToRenumber(const MEDCouplingUMesh& otherDimM1OnS
   if(otherDimM1OnSameCoords.getMeshDimension()!=getMeshDimension()-1)
     throw INTERP_KERNEL::Exception("MEDCouplingUMesh::findCellsToRenumber: the mesh given in other parameter must have this->getMeshDimension()-1 !");
 
-  DAInt cellsAroundGroupLarge = getCellIdsLyingOnNodes(nodeIdsToDuplicateBg, nodeIdsToDuplicateEnd, false);  // false= take cell in, even if not all nodes are in dupl
+  // Degenerated case - no nodes to duplicate
+  if (nodeIdsToDuplicateBg == nodeIdsToDuplicateEnd)
+    {
+      cellIdsNeededToBeRenum = DataArrayIdType::New(); cellIdsNeededToBeRenum->alloc(0,1);
+      cellIdsNotModified = DataArrayIdType::New(); cellIdsNotModified->alloc(0,1);
+      return;
+    }
 
-  //
+  // Compute cell IDs of the mesh with cells that touch the M1 group with a least one node:
+  DAInt cellsAroundGroupLarge = getCellIdsLyingOnNodes(nodeIdsToDuplicateBg, nodeIdsToDuplicateEnd, false);  // false= take cell in, even if not all nodes are in dupl
   MCUMesh mAroundGrpLarge=static_cast<MEDCouplingUMesh *>(buildPartOfMySelf(cellsAroundGroupLarge->begin(),cellsAroundGroupLarge->end(),true));
+  mcIdType nCellsLarge=cellsAroundGroupLarge->getNumberOfTuples();
   DAInt descL=DataArrayIdType::New(),descIL=DataArrayIdType::New(),revDescL=DataArrayIdType::New(),revDescIL=DataArrayIdType::New();
   MCUMesh mArGrpLargeDesc=mAroundGrpLarge->buildDescendingConnectivity(descL,descIL,revDescL,revDescIL);
   const mcIdType *descILP=descIL->begin(), *descLP=descL->begin();
-
-  // Extract now all N D cells which have a complete face in touch with the group:
-  //   1. Identify cells of M1 group in sub-mesh mAroundGrp
   DataArrayIdType *idsOfM1t;
   mArGrpLargeDesc->areCellsIncludedIn(&otherDimM1OnSameCoords,2, idsOfM1t);
   DAInt idsOfM1Large(idsOfM1t);
   mcIdType nL = mArGrpLargeDesc->getNumberOfCells();
-  DAInt idsStrict = DataArrayIdType::New(); idsStrict->alloc(0,1);
-  //  2. Build map giving for each cell ID in mAroundGrp (not in mAroundGrpLarge) the corresponding cell
-  //     ID on the other side of the crack:
-  std::map<mcIdType, mcIdType> toOtherSide, pos;
-  mcIdType cnt = 0;
+
+  // Computation of the neighbor information of the mesh WITH the crack (some neighbor links are removed):
+  //     In the neighbor information remove the connection between high dimension cells and its low level constituents which are part
+  //     of the frontier given in parameter (i.e. the cells of low dimension from the group delimiting the crack):
+  DAInt descLTrunc = descL->deepCopy(), descILTrunc = descIL->deepCopy();
+  DataArrayIdType::RemoveIdsFromIndexedArrays(idsOfM1Large->begin(), idsOfM1Large->end(),descLTrunc,descILTrunc);
+  DataArrayIdType *neight=0, *neighIt=0;
+  MEDCouplingUMesh::ComputeNeighborsOfCellsAdv(descLTrunc,descILTrunc,revDescL,revDescIL, neight, neighIt);
+  DAInt neighL(neight), neighIL(neighIt);
+
+  DAInt hitCellsLarge = DataArrayIdType::New(); hitCellsLarge->alloc(nCellsLarge,1);
+  hitCellsLarge->fillWithValue(0);  // 0 : not hit, +1: one side of the crack, -1: other side of the crack,
+  mcIdType* hitCellsLargeP = hitCellsLarge->rwBegin();
+
+  // Now loop on the faces of the M1 group and fill spread zones on either side of the crack:
   const mcIdType *revDescILP=revDescIL->begin(), *revDescLP=revDescL->begin();
   for(const auto& v: *idsOfM1Large)
     {
-      if (v >= nL)    // Keep valid match only
-        continue;
+      if (v >= nL) continue;   // Keep valid match only - see doc of areCellsIncludedIn()
       mcIdType idx0 = revDescILP[v];
-      // Keep the two cells on either side of the face v of M1:
+      // Retrieve the two cells on either side of the face v of M1:
       mcIdType c1=revDescLP[idx0], c2=revDescLP[idx0+1];
-      DAInt t1=idsStrict->findIdsEqual(c1), t2=idsStrict->findIdsEqual(c2);
-
-      if (!t1->getNumberOfTuples())
-        {  pos[c1] = cnt++; idsStrict->pushBackSilent(c1);   }
-      if (!t2->getNumberOfTuples())
-        {  pos[c2] = cnt++; idsStrict->pushBackSilent(c2);   }
-
-      mcIdType k1 = pos[c1], k2=pos[c2];
-      toOtherSide[k1] = k2;
-      toOtherSide[k2] = k1;
-    }
-
-  DAInt cellsAroundGroup = cellsAroundGroupLarge->selectByTupleId(idsStrict->begin(), idsStrict->end());
-  MCUMesh mAroundGrp = static_cast<MEDCouplingUMesh *>(buildPartOfMySelf(cellsAroundGroup->begin(), cellsAroundGroup->end(), true));
-  mcIdType nCells=cellsAroundGroup->getNumberOfTuples(), nCellsLarge=cellsAroundGroupLarge->getNumberOfTuples();
-  DAInt desc=DataArrayIdType::New(),descI=DataArrayIdType::New(),revDesc=DataArrayIdType::New(),revDescI=DataArrayIdType::New();  
-  MCUMesh mArGrpDesc=mAroundGrp->buildDescendingConnectivity(desc,descI,revDesc,revDescI);
-  DataArrayIdType *idsOfM1t2;
-  mArGrpDesc->areCellsIncludedIn(&otherDimM1OnSameCoords,2, idsOfM1t2);  // TODO can we avoid recomputation here?
-  DAInt idsOfM1(idsOfM1t2);
-
-  // Neighbor information of the mesh WITH the crack (some neighbors are removed):
-  //     In the neighbor information remove the connection between high dimension cells and its low level constituents which are part
-  //     of the frontier given in parameter (i.e. the cells of low dimension from the group delimiting the crack):
-  DataArrayIdType::RemoveIdsFromIndexedArrays(idsOfM1->begin(), idsOfM1->end(),desc,descI);
-  //     Compute the neighbor of each cell in mAroundGrp, taking into account the broken link above. Two
-  //     cells on either side of the crack (defined by the mesh of low dimension) are not neighbor anymore.
-  DataArrayIdType *neight=0, *neighIt=0;
-  MEDCouplingUMesh::ComputeNeighborsOfCellsAdv(desc,descI,revDesc,revDescI, neight, neighIt);
-  DAInt neigh(neight), neighI(neighIt);
-
-  // For each initial connex part of the M1 mesh (or said differently for each independent crack):
-  mcIdType seed=0, nIter=0;
-  mcIdType nIterMax = nCells+1; // Safety net for the loop
-  DAInt hitCells = DataArrayIdType::New(); hitCells->alloc(nCells,1);
-  mcIdType* hitCellsP = hitCells->rwBegin();
-  hitCells->fillWithValue(0);  // 0 : not hit, +x: one side of the crack, -x: other side of the crack, with 'x' the index of the connex component
-  mcIdType PING_FULL, PONG_FULL;
-  mcIdType MAX_CP = 10000;  // the choices below assume we won't have more than 10000 different connex parts ...
-  mcIdType PING_FULL_init = 0, PING_PART = MAX_CP;
-  mcIdType PONG_FULL_init = 0, PONG_PART = -MAX_CP;
-  cnt=0;
-  while (nIter < nIterMax)
-    {
-      DAInt t = hitCells->findIdsEqual(0);
-      if(!t->getNumberOfTuples())
-        break;
-      mcIdType seed = t->getIJ(0,0);
-      bool done = false;
-      cnt++;
-      PING_FULL = PING_FULL_init+cnt;
-      PONG_FULL = PONG_FULL_init-cnt;
-      // while the connex bits in correspondance on either side of the crack are not fully covered
-      while(!done && nIter < nIterMax)  // Start of the ping-pong
+      std::map<mcIdType, mcIdType> toOther = {{c1, c2}, {c2, c1}};
+      // Handle the spread zones on the two sides of the crack:
+      for (const auto c: {c1, c2})
         {
-          nIter++;
-          // Identify connex zone around the seed - this zone corresponds to some cells on the other side
-          // of the crack that might extend further away. So we will need to compute spread zone on the other side
-          // too ... and this process can repeat, hence the "ping-pong" logic.
+          if (hitCellsLargeP[c]) continue;
+          // Identify connex zone around this cell - if we find a value already assigned there, use it.
           mcIdType dnu;
-          DAInt spreadZone = MEDCouplingUMesh::ComputeSpreadZoneGraduallyFromSeed(&seed, &seed+1, neigh,neighI, -1, dnu);
-          done = true;
-          for(const mcIdType& s: *spreadZone)
-            {
-              hitCellsP[s] = PING_FULL;
-              const auto& it = toOtherSide.find(s);
-              if (it != toOtherSide.end())
-                {
-                  mcIdType other = it->second;
-                  if (hitCellsP[other] != PONG_FULL)
-                    {
-                      // On the other side of the crack we hit a cell which was not fully covered previously by the 
-                      // ComputeSpreadZone process, so we are not done yet, ComputeSreadZone will need to be applied there
-                      done = false;
-                      hitCellsP[other] = PONG_PART;
-                      //  Compute next seed, i.e. a cell on the other side of the crack
-                      seed = other;
-                    }
-                }
-            }
-          if (done)
-            {
-              // we might have several disjoint PONG parts in front of a single PING connex part:
-              DAInt idsPong = hitCells->findIdsEqual(PONG_PART);
-              if (idsPong->getNumberOfTuples())
-                {
-                  seed = idsPong->getIJ(0,0);
-                  done = false;
-                }
-              continue;  // continue without switching side (or break if 'done' remains false)
-            }
-          else
+          DAInt spreadZone = MEDCouplingUMesh::ComputeSpreadZoneGraduallyFromSeed(&c, &c+1, neighL,neighIL, -1, dnu);
+          std::set<mcIdType> sv;
+          for (const mcIdType& s: *spreadZone)
+            if (hitCellsLargeP[s]) sv.insert(hitCellsLargeP[s]);
+          if (sv.size() > 1)
+            // Strange: we find in the same spread zone a +1 and -1 !
+            throw INTERP_KERNEL::Exception("MEDCouplingUMesh::findCellsToRenumber: internal error #0 - conflicting values - should not happen!");
+          // If a valid value was found, use it:
+          mcIdType val = sv.size()==1 ? *sv.begin() : 0;
+          // Hopefully this does not conflict with an potential value on the other side:
+          mcIdType other = toOther[c];
+          if (hitCellsLargeP[other])
             {
-              // Go to the other side
-              std::swap(PING_FULL, PONG_FULL);
-              std::swap(PING_PART, PONG_PART);
-            }
-        } // while (!done ...)
-      DAInt nonHitCells = hitCells->findIdsEqual(0);
-      if (nonHitCells->getNumberOfTuples())
-        seed = nonHitCells->getIJ(0,0);
-      else
-        break;
-    } // while (nIter < nIterMax ...
-  if (nIter >= nIterMax)
-    throw INTERP_KERNEL::Exception("MEDCouplingUMesh::findCellsToRenumber: Too many iterations - should not happen");
-
-  // Now we have handled all N D cells which have a face touching the M1 group. It remains the cells
-  // which are just touching the group by one (or several) node(s) (see for example testBuildInnerBoundaryAlongM1Group4)
-  // All those cells are in direct contact with a cell which is either PING_FULL or PONG_FULL
-  // So first reproject the PING/PONG info onto mAroundGrpLarge:
-  DAInt hitCellsLarge = DataArrayIdType::New(); hitCellsLarge->alloc(nCellsLarge,1);
-  hitCellsLarge->fillWithValue(0);
-  mcIdType *hitCellsLargeP=hitCellsLarge->rwBegin(), tt=0;
-  for(const auto &i: *idsStrict)
-    { hitCellsLargeP[i] = hitCellsP[tt++]; }
-  DAInt nonHitCells = hitCellsLarge->findIdsEqual(0);
-  // Neighbor information in mAroundGrpLarge:
-  DataArrayIdType *neighLt=0, *neighILt=0;
-  MEDCouplingUMesh::ComputeNeighborsOfCellsAdv(descL,descIL,revDescL,revDescIL, neighLt, neighILt);
-  DAInt neighL(neighLt), neighIL(neighILt);
-  const mcIdType *neighILP=neighIL->begin(), *neighLP=neighL->begin();
-  for(const auto& c : *nonHitCells)
-    {
-      mcIdType cnt00 = neighILP[c];
-      for (const mcIdType *n=neighLP+cnt00; cnt00 < neighILP[c+1]; n++, cnt00++)
-        {
-          mcIdType neighVal = hitCellsLargeP[*n];
-          if (neighVal != 0 && std::abs(neighVal) < MAX_CP)  // (@test_T0) second part of the test to skip cells being assigned and target only cells assigned in the first part of the algo above
-            {
-              mcIdType currVal = hitCellsLargeP[c];
-              if (currVal != 0)   // Several neighbors have a candidate number
-                {
-                  // Unfortunately in some weird cases (see testBuildInnerBoundary8) a cell in mAroundGrpLarge
-                  // might have as neighbor two conflicting spread zone ...
-                  if (currVal*neighVal < 0)
-                    {
-                      // If we arrive here, the cell was already assigned a number and we found a neighbor with
-                      // a different sign ... we must swap the whole spread zone!!
-                      DAInt ids1 = hitCellsLarge->findIdsEqual(neighVal), ids1b = hitCellsLarge->findIdsEqual(-neighVal);
-                      DAInt ids2 = hitCellsLarge->findIdsEqual(MAX_CP*neighVal), ids2b = hitCellsLarge->findIdsEqual(-MAX_CP*neighVal);
-                      // A nice little lambda to multiply part of a DAInt by -1 ...
-                      auto mul_part_min1 = [hitCellsLargeP](const DAInt& ids) { for(const auto& i: *ids) hitCellsLargeP[i] *= -1; };
-                      mul_part_min1(ids1);
-                      mul_part_min1(ids1b);
-                      mul_part_min1(ids2);
-                      mul_part_min1(ids2b);
-                    }
-                }
-              else  // First assignation
-                hitCellsLargeP[c] = MAX_CP*neighVal;  // Same sign, but different value to preserve PING_FULL and PONG_FULL
+              if(val && hitCellsLargeP[other] != -val)
+                throw INTERP_KERNEL::Exception("MEDCouplingUMesh::findCellsToRenumber: internal error #1 - conflicting values - should not happen!");;
+              // We do not yet have a value, but other side has one. Use it!
+              if(!val) val = -hitCellsLargeP[other];
             }
+          // Cover first initialisation:
+          if (!val) val = 1;
+          // And finally, fill the current spread zone:
+          for(const mcIdType& s: *spreadZone) hitCellsLargeP[s] = val;
         }
     }
-  DAInt cellsRet1 = hitCellsLarge->findIdsInRange(1,MAX_CP*MAX_CP);   // Positive spread zone number
-  DAInt cellsRet2 = hitCellsLarge->findIdsInRange(-MAX_CP*MAX_CP, 0); // Negative spread zone number
+
+  DAInt cellsRet1 = hitCellsLarge->findIdsEqual(1);
+  DAInt cellsRet2 = hitCellsLarge->findIdsEqual(-1);
 
   if (cellsRet1->getNumberOfTuples() + cellsRet2->getNumberOfTuples() != cellsAroundGroupLarge->getNumberOfTuples())
-    throw INTERP_KERNEL::Exception("MEDCouplingUMesh::findCellsToRenumber: Some cells not hit - Internal error should not happen");
+    {
+      DAInt nonHitCells = hitCellsLarge->findIdsEqual(0); // variable kept for debug ...
+      throw INTERP_KERNEL::Exception("MEDCouplingUMesh::findCellsToRenumber: Some cells not hit - Internal error should not happen");
+    }
   cellsRet1->transformWithIndArr(cellsAroundGroupLarge->begin(),cellsAroundGroupLarge->end());
   cellsRet2->transformWithIndArr(cellsAroundGroupLarge->begin(),cellsAroundGroupLarge->end());
   //
@@ -3838,7 +3858,11 @@ MEDCouplingUMesh *MEDCouplingUMesh::buildSlice3D(const double *origin, const dou
   revDesc2=0; revDescIndx2=0;
   MCAuto<MEDCouplingUMesh> mDesc1=mDesc2->buildDescendingConnectivity(desc1,descIndx1,revDesc1,revDescIndx1);//meshDim==1 spaceDim==3
   revDesc1=0; revDescIndx1=0;
-  mDesc1->fillCellIdsToKeepFromNodeIds(&nodes[0],&nodes[0]+nodes.size(),true,cellIds1D);
+  //Marking all 1D cells that contained at least one node located on the plane
+  //the intersection between those cells and the plane, which consist of the nodes previously tagged, thus don't need to be computed afterwards
+  //(if said intersection is computed in MEDCouplingUMesh::split3DCurveWithPlane, then we might create additional nodes
+  //due to accuracy errors when the needed nodes already exist)
+  mDesc1->fillCellIdsToKeepFromNodeIds(&nodes[0],&nodes[0]+nodes.size(),false,cellIds1D);
   MCAuto<DataArrayIdType> cellIds1DTmp(cellIds1D);
   //
   std::vector<mcIdType> cut3DCurve(mDesc1->getNumberOfCells(),-2);
@@ -4332,7 +4356,7 @@ mcIdType MEDCouplingUMesh::getCellContainingPoint(const double *pos, double eps)
  *          point, for more points getCellsContainingPoints() is recommended as it is
  *          faster.
  *  \param [in] pos - array of coordinates of the ball central point.
- *  \param [in] eps - ball radius.
+ *  \param [in] eps - ball radius (i.e. the precision) relative to max dimension along X, Y or Z of the the considered cell bounding box.
  *  \param [out] elts - vector returning ids of the found cells. It is cleared
  *         before inserting ids.
  *  \throw If the coordinates array is not set.
@@ -4399,7 +4423,7 @@ void MEDCouplingUMesh::getCellsContainingPointsZeAlg(const double *pos, mcIdType
  *         X0,Y0,Z0,X1,Y1,Z1,... Size of the array must be \a
  *         this->getSpaceDimension() * \a nbOfPoints
  *  \param [in] nbOfPoints - number of points to locate within \a this mesh.
- *  \param [in] eps - radius of balls (i.e. the precision).
+ *  \param [in] eps - ball radius (i.e. the precision) relative to max dimension along X, Y or Z of the the considered cell bounding box.
  *  \param [out] elts - vector returning ids of found cells.
  *  \param [out] eltsIndex - an array, of length \a nbOfPoints + 1,
  *         dividing cell ids in \a elts into groups each referring to one
@@ -7975,9 +7999,15 @@ DataArrayIdType *MEDCouplingUMesh::orderConsecutiveCells1D() const
               ptId = direction ? (ptId1 == prevPointId ? ptId2 : ptId1) : (ptId2 == prevPointId ? ptId1 : ptId2);
               if (dsi[ptId] == 1) // hitting the end of the line
                 break;
+
               prevPointId = ptId;
               mcIdType seg1 = rD[rDI[ptId]], seg2 = rD[rDI[ptId]+1];
               activeSeg = (seg1 == activeSeg) ? seg2 : seg1;
+
+              //for piecewise meshes made up of closed parts
+              bool segmentAlreadyTreated = (std::find(linePiece.begin(), linePiece.end(), activeSeg) != linePiece.end());
+              if(segmentAlreadyTreated)
+                break;
             }
         }
       // Done, save final piece into DA:
@@ -7987,6 +8017,7 @@ DataArrayIdType *MEDCouplingUMesh::orderConsecutiveCells1D() const
       // identify next valid start segment (one which is not consumed)
       if(!edgeSet.empty())
         startSeg = *(edgeSet.begin());
+
     }
   while (!edgeSet.empty());
   return result.retn();