Salome HOME
0021542: EDF 1699 SMESH: Reorient a group of faces
[modules/smesh.git] / src / SMESH / SMESH_MeshEditor.cxx
index 90be95edf8d12a9917c4f8dd171db5cdd5f0e26f..888aee883e34ef899ffa2ee8d3fd24c339f15502 100644 (file)
@@ -120,6 +120,19 @@ SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
 {
 }
 
+//================================================================================
+/*!
+ * \brief Clears myLastCreatedNodes and myLastCreatedElems
+ */
+//================================================================================
+
+void SMESH_MeshEditor::CrearLastCreated()
+{
+  myLastCreatedNodes.Clear();
+  myLastCreatedElems.Clear();
+}
+
+
 //=======================================================================
 /*!
  * \brief Add element
@@ -389,6 +402,44 @@ int SMESH_MeshEditor::Remove (const list< int >& theIDs,
   return removed;
 }
 
+//================================================================================
+/*!
+ * \brief Create 0D elements on all nodes of the given object except those
+ *        nodes on which a 0D element already exists.
+ *  \param elements - Elements on whose nodes to create 0D elements; if empty, 
+ *                    the all mesh is treated
+ *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
+ */
+//================================================================================
+
+void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
+                                                   TIDSortedElemSet&       all0DElems )
+{
+  typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::const_iterator> TSetIterator;
+  SMDS_ElemIteratorPtr elemIt;
+  if ( elements.empty() )
+    elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
+  else
+    elemIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
+
+  while ( elemIt->more() )
+  {
+    const SMDS_MeshElement* e = elemIt->next();
+    SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
+    while ( nodeIt->more() )
+    {
+      const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
+      SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
+      if ( it0D->more() )
+        all0DElems.insert( it0D->next() );
+      else {
+        myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
+        all0DElems.insert( myLastCreatedElems.Last() );
+      }
+    }
+  }
+}
+
 //=======================================================================
 //function : FindShape
 //purpose  : Return an index of the shape theElem is on
@@ -1065,7 +1116,7 @@ bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
  * \brief Reorient faces.
  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
  * \param theDirection - desired direction of normal of \a theFace
- * \param theFace - one of \a theFaces that sould be orientated according to
+ * \param theFace - one of \a theFaces that sould be oriented according to
  *        \a theDirection and whose orientation defines orientation of other faces
  * \return number of reoriented faces.
  */
@@ -1102,6 +1153,11 @@ int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
     theFaces.erase( theFace );
   startFaces.insert( theFace );
 
+  int nodeInd1, nodeInd2;
+  const SMDS_MeshElement*           otherFace;
+  vector< const SMDS_MeshElement* > facesNearLink;
+  vector< std::pair< int, int > >   nodeIndsOfFace;
+
   set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
   while ( startFace != startFaces.end() )
   {
@@ -1124,13 +1180,36 @@ int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
       }
       else
       {
-        int nodeInd1, nodeInd2;
-        const SMDS_MeshElement* otherFace = FindFaceInSet( link.first, link.second,
-                                                           theFaces, avoidSet,
-                                                           & nodeInd1, & nodeInd2);
+        facesNearLink.clear();
+        nodeIndsOfFace.clear();
+        while (( otherFace = FindFaceInSet( link.first, link.second,
+                                            theFaces, avoidSet, &nodeInd1, &nodeInd2 )))
+          if ( otherFace != theFace)
+          {
+            facesNearLink.push_back( otherFace );
+            nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
+            avoidSet.insert( otherFace );
+          }
+        if ( facesNearLink.size() > 1 )
+        {
+          // select a face most co-directed with theFace,
+          // other faces won't be visited this time
+          gp_XYZ NF, NOF;
+          SMESH_Algo::FaceNormal( theFace, NF, /*normalized=*/false );
+          double proj, maxProj = 0;
+          for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
+            SMESH_Algo::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
+            if (( proj = Abs( NF * NOF )) > maxProj ) {
+              maxProj = proj;
+              otherFace = facesNearLink[i];
+              nodeInd1  = nodeIndsOfFace[i].first;
+              nodeInd2  = nodeIndsOfFace[i].second;
+            }
+          }
+        }
         if ( otherFace && otherFace != theFace)
         {
-          // link must be reversed in otherFace if orientation ot otherFace
+          // link must be reverse in otherFace if orientation ot otherFace
           // is same as that of theFace
           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
           {
@@ -1143,7 +1222,7 @@ int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
             theFaces.erase( otherFace );
         }
       }
-      std::swap( link.first, link.second );
+      std::swap( link.first, link.second ); // reverse the link
     }
     startFaces.erase( startFace );
     startFace = startFaces.begin();
@@ -1216,7 +1295,8 @@ bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
     if( !elem->IsQuadratic() ) {
 
       // split liner quadrangle
-
+      // for MaxElementLength2D functor we return minimum diagonal for splitting,
+      // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
       if ( aBadRate1 <= aBadRate2 ) {
         // tr1 + tr2 is better
         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
@@ -1350,7 +1430,8 @@ int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
-
+    // for MaxElementLength2D functor we return minimum diagonal for splitting,
+    // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
       return 1; // diagonal 1-3
 
@@ -3062,7 +3143,7 @@ void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
     while ( fIt->more() ) {
       const SMDS_MeshElement* face = fIt->next();
-      theElems.insert( face );
+      theElems.insert( theElems.end(), face );
     }
   }
   // get all face ids theElems are on
@@ -5709,7 +5790,7 @@ SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
 
     SMDSAbs_GeometryType geomType = elem->GetGeomType();
     int                  nbNodes  = elem->NbNodes();
-    if ( geomType == SMDSGeom_POINT ) continue; // node
+    if ( geomType == SMDSGeom_NONE ) continue; // node
 
     switch ( geomType ) {
 
@@ -5891,7 +5972,7 @@ SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
   // group names
   set< string > groupNames;
-  
+
   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
   if ( !groupIt->more() ) return newGroupIDs;
 
@@ -6103,7 +6184,7 @@ struct SMESH_NodeSearcherImpl: public SMESH_NodeSearcher
         }
         else if ( tree->NbNodes() ) // put a tree to the treeMap
         {
-          const Bnd_B3d& box = tree->getBox();
+          const Bnd_B3d& box = *tree->getBox();
           double sqDist = thePnt.SquareDistance( 0.5 * ( box.CornerMin() + box.CornerMax() ));
           pair<TDistTreeMap::iterator,bool> it_in = treeMap.insert( make_pair( sqDist, tree ));
           if ( !it_in.second ) // not unique distance to box center
@@ -6115,7 +6196,7 @@ struct SMESH_NodeSearcherImpl: public SMESH_NodeSearcher
       TDistTreeMap::iterator sqDist_tree = treeMap.begin();
       if ( treeMap.size() > 5 ) {
         SMESH_OctreeNode* closestTree = sqDist_tree->second;
-        const Bnd_B3d& box = closestTree->getBox();
+        const Bnd_B3d& box = *closestTree->getBox();
         double limit = sqrt( sqDist_tree->first ) + sqrt ( box.SquareExtent() );
         sqLimit = limit * limit;
       }
@@ -6200,7 +6281,7 @@ namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint()
 
   protected:
     ElementBndBoxTree():_size(0) {}
-    SMESH_Octree* allocateOctreeChild() const { return new ElementBndBoxTree; }
+    SMESH_Octree* newChild() const { return new ElementBndBoxTree; }
     void          buildChildrenData();
     Bnd_B3d*      buildRootBox();
   private:
@@ -6222,7 +6303,7 @@ namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint()
   //================================================================================
 
   ElementBndBoxTree::ElementBndBoxTree(const SMDS_Mesh& mesh, SMDSAbs_ElementType elemType, SMDS_ElemIteratorPtr theElemIt, double tolerance)
-    :SMESH_Octree( new SMESH_Octree::Limit( MaxLevel, /*minSize=*/0. ))
+    :SMESH_Octree( new SMESH_TreeLimit( MaxLevel, /*minSize=*/0. ))
   {
     int nbElems = mesh.GetMeshInfo().NbElements( elemType );
     _elements.reserve( nbElems );
@@ -6273,7 +6354,7 @@ namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint()
     {
       for (int j = 0; j < 8; j++)
       {
-        if ( !_elements[i]->IsOut( myChildren[j]->getBox() ))
+        if ( !_elements[i]->IsOut( *myChildren[j]->getBox() ))
         {
           _elements[i]->_refCount++;
           ((ElementBndBoxTree*)myChildren[j])->_elements.push_back( _elements[i]);
@@ -6304,7 +6385,7 @@ namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint()
   void ElementBndBoxTree::getElementsNearPoint( const gp_Pnt&     point,
                                                 TIDSortedElemSet& foundElems)
   {
-    if ( getBox().IsOut( point.XYZ() ))
+    if ( getBox()->IsOut( point.XYZ() ))
       return;
 
     if ( isLeaf() )
@@ -6329,7 +6410,7 @@ namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint()
   void ElementBndBoxTree::getElementsNearLine( const gp_Ax1&     line,
                                                TIDSortedElemSet& foundElems)
   {
-    if ( getBox().IsOut( line ))
+    if ( getBox()->IsOut( line ))
       return;
 
     if ( isLeaf() )
@@ -6355,7 +6436,7 @@ namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint()
                                                 const double      radius,
                                                 TIDSortedElemSet& foundElems)
   {
-    if ( getBox().IsOut( center, radius ))
+    if ( getBox()->IsOut( center, radius ))
       return;
 
     if ( isLeaf() )
@@ -6657,7 +6738,7 @@ void SMESH_ElementSearcherImpl::findOuterBoundary(const SMDS_MeshElement* outerF
  * \brief Find elements of given type where the given point is IN or ON.
  *        Returns nb of found elements and elements them-selves.
  *
- * 'ALL' type means elements of any type excluding nodes, balls and 0D elements 
+ * 'ALL' type means elements of any type excluding nodes, balls and 0D elements
  */
 //=======================================================================
 
@@ -6734,13 +6815,13 @@ SMESH_ElementSearcherImpl::FindClosestTo( const gp_Pnt&       point,
     }
     TIDSortedElemSet suspectElems;
     _ebbTree->getElementsNearPoint( point, suspectElems );
-    
+
     if ( suspectElems.empty() && _ebbTree->maxSize() > 0 )
     {
-      gp_Pnt boxCenter = 0.5 * ( _ebbTree->getBox().CornerMin() +
-                                 _ebbTree->getBox().CornerMax() );
+      gp_Pnt boxCenter = 0.5 * ( _ebbTree->getBox()->CornerMin() +
+                                 _ebbTree->getBox()->CornerMax() );
       double radius;
-      if ( _ebbTree->getBox().IsOut( point.XYZ() ))
+      if ( _ebbTree->getBox()->IsOut( point.XYZ() ))
         radius = point.Distance( boxCenter ) - 0.5 * _ebbTree->maxSize();
       else
         radius = _ebbTree->maxSize() / pow( 2., _ebbTree->getHeight()) / 2;
@@ -7242,7 +7323,7 @@ namespace
                       POS_ALL = POS_LEFT | POS_RIGHT | POS_VERTEX };
   struct PointPos
   {
-    PositionName _name; 
+    PositionName _name;
     int          _index; // index of vertex or segment
 
     PointPos( PositionName n, int i=-1 ): _name(n), _index(i) {}
@@ -8156,32 +8237,29 @@ private:
 //purpose  : Return list of group of elements built on the same nodes.
 //           Search among theElements or in the whole mesh if theElements is empty
 //=======================================================================
-void SMESH_MeshEditor::FindEqualElements(set<const SMDS_MeshElement*> & theElements,
-                                         TListOfListOfElementsID &      theGroupsOfElementsID)
+
+void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet &        theElements,
+                                         TListOfListOfElementsID & theGroupsOfElementsID)
 {
   myLastCreatedElems.Clear();
   myLastCreatedNodes.Clear();
 
-  typedef set<const SMDS_MeshElement*> TElemsSet;
   typedef map< SortableElement, int > TMapOfNodeSet;
   typedef list<int> TGroupOfElems;
 
-  TElemsSet elems;
   if ( theElements.empty() )
   { // get all elements in the mesh
     SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
     while ( eIt->more() )
-      elems.insert( elems.end(), eIt->next());
+      theElements.insert( theElements.end(), eIt->next());
   }
-  else
-    elems = theElements;
 
   vector< TGroupOfElems > arrayOfGroups;
   TGroupOfElems groupOfElems;
   TMapOfNodeSet mapOfNodeSet;
 
-  TElemsSet::iterator elemIt = elems.begin();
-  for ( int i = 0, j=0; elemIt != elems.end(); ++elemIt, ++j ) {
+  TIDSortedElemSet::iterator elemIt = theElements.begin();
+  for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
     const SMDS_MeshElement* curElem = *elemIt;
     SortableElement SE(curElem);
     int ind = -1;
@@ -8254,8 +8332,8 @@ void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElemen
 
 void SMESH_MeshEditor::MergeEqualElements()
 {
-  set<const SMDS_MeshElement*> aMeshElements; /* empty input -
-                                                 to merge equal elements in the whole mesh */
+  TIDSortedElemSet aMeshElements; /* empty input ==
+                                     to merge equal elements in the whole mesh */
   TListOfListOfElementsID aGroupsOfElementsID;
   FindEqualElements(aMeshElements, aGroupsOfElementsID);
   MergeElements(aGroupsOfElementsID);
@@ -9635,7 +9713,7 @@ void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d)
   if ( !theForce3d )
   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
     aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
-    aHelper.FixQuadraticElements();
+    aHelper.FixQuadraticElements(myError);
   }
 }
 
@@ -9775,7 +9853,7 @@ void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
   if ( !theForce3d  && !getenv("NO_FixQuadraticElements"))
   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
     helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
-    helper.FixQuadraticElements();
+    helper.FixQuadraticElements( myError );
   }
 }
 
@@ -11088,7 +11166,7 @@ bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSorted
       // --- build a map (face to duplicate --> volume to modify)
       //     with all the faces shared by 2 domains (group of elements)
       //     and corresponding volume of this domain, for each shared face.
-      //     a volume has a face shared by 2 domains if it has a neighbor which is not in is domain.
+      //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
 
       //MESSAGE("Domain " << idom);
       const TIDSortedElemSet& domain = theElems[idom];
@@ -11151,8 +11229,6 @@ bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSorted
             {
               int oldId = *itn;
               //MESSAGE("     node " << oldId);
-              std::set<int> cells;
-              cells.clear();
               vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
               for (int i=0; i<l.ncells; i++)
                 {
@@ -11169,8 +11245,8 @@ bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSorted
                                 //no cells created after BuildDownWardConnectivity
                     }
                   DownIdType aCell(downId, vtkType);
-                  if (celldom.count(vtkId))
-                    continue;
+                  if (!cellDomains.count(aCell))
+                    cellDomains[aCell] = emptyMap; // create an empty entry for cell
                   cellDomains[aCell][idomain] = vtkId;
                   celldom[vtkId] = idomain;
                   //MESSAGE("       cell " << vtkId << " domain " << idomain);
@@ -11204,16 +11280,18 @@ bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSorted
           std::set<int> oldNodes;
           oldNodes.clear();
           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
-          bool isMultipleDetected = false;
           std::set<int>::iterator itn = oldNodes.begin();
           for (; itn != oldNodes.end(); ++itn)
             {
               int oldId = *itn;
-              //MESSAGE("     node " << oldId);
+              //MESSAGE("-+-+-a node " << oldId);
               if (!nodeDomains.count(oldId))
                 nodeDomains[oldId] = emptyMap; // create an empty entry for node
               if (nodeDomains[oldId].empty())
-                nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
+                {
+                  nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
+                  //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
+                }
               std::map<int, int>::iterator itdom = domvol.begin();
               for (; itdom != domvol.end(); ++itdom)
                 {
@@ -11225,7 +11303,6 @@ bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSorted
                         {
                           vector<int> orderedDoms;
                           //MESSAGE("multiple node " << oldId);
-                          isMultipleDetected =true;
                           if (mutipleNodes.count(oldId))
                             orderedDoms = mutipleNodes[oldId];
                           else
@@ -11245,16 +11322,35 @@ bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSorted
                       SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
                       int newId = newNode->getVtkId();
                       nodeDomains[oldId][idom] = newId; // cloned node for other domains
-                      //MESSAGE("   newNode " << newId << " oldNode " << oldId << " size=" <<nodeDomains[oldId].size());
-                    }
-                  if (nodeDomains[oldId].size() >= 3)
-                    {
-                      //MESSAGE("confirm multiple node " << oldId);
-                      isMultipleDetected =true;
+                      //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
                     }
                 }
             }
-          if (isMultipleDetected) // check if an edge of the face is shared between 3 or more domains
+        }
+    }
+
+  for (int idomain = 0; idomain < theElems.size(); idomain++)
+    {
+      itface = faceDomains.begin();
+      for (; itface != faceDomains.end(); ++itface)
+        {
+          std::map<int, int> domvol = itface->second;
+          if (!domvol.count(idomain))
+            continue;
+          DownIdType face = itface->first;
+          //MESSAGE(" --- face " << face.cellId);
+          std::set<int> oldNodes;
+          oldNodes.clear();
+          grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
+          int nbMultipleNodes = 0;
+          std::set<int>::iterator itn = oldNodes.begin();
+          for (; itn != oldNodes.end(); ++itn)
+            {
+              int oldId = *itn;
+              if (mutipleNodes.count(oldId))
+                nbMultipleNodes++;
+            }
+          if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
             {
               //MESSAGE("multiple Nodes detected on a shared face");
               int downId = itface->first.cellId;
@@ -11268,7 +11364,7 @@ bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSorted
                     if (mutipleNodes.count(nodes[i]))
                       if (!mutipleNodesToFace.count(nodes[i]))
                         mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
-               }
+                }
               else // shared face (between two volumes)
                 {
                   int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
@@ -11282,9 +11378,12 @@ bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSorted
                         {
                           vector<int> vn0 = mutipleNodes[nodes[0]];
                           vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
-                          sort( vn0.begin(), vn0.end() );
-                          sort( vn1.begin(), vn1.end() );
-                          if (vn0 == vn1)
+                          vector<int> doms;
+                          for (int i0 = 0; i0 < vn0.size(); i0++)
+                            for (int i1 = 0; i1 < vn1.size(); i1++)
+                              if (vn0[i0] == vn1[i1])
+                                doms.push_back(vn0[i0]);
+                          if (doms.size() >2)
                             {
                               //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
                               double *coords = grid->GetPoint(nodes[0]);
@@ -11296,9 +11395,9 @@ bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSorted
                               map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
                               map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
                               int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
-                              for (int id=0; id < vn0.size(); id++)
+                              for (int id=0; id < doms.size(); id++)
                                 {
-                                  int idom = vn0[id];
+                                  int idom = doms[id];
                                   for (int ivol=0; ivol<nbvol; ivol++)
                                     {
                                       int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
@@ -11361,6 +11460,14 @@ bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSorted
 
   if (createJointElems)
     {
+      int idg;
+      string joints2DName = "joints2D";
+      mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
+      SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
+      string joints3DName = "joints3D";
+      mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
+      SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
+
       itface = faceDomains.begin();
       for (; itface != faceDomains.end(); ++itface)
         {
@@ -11384,13 +11491,16 @@ bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSorted
             grpname << dom1 << "_" << dom2;
           else
             grpname << dom2 << "_" << dom1;
-          int idg;
           string namegrp = grpname.str();
           if (!mapOfJunctionGroups.count(namegrp))
             mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
           SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
           if (sgrp)
             sgrp->Add(vol->GetID());
+          if (vol->GetType() == SMDSAbs_Volume)
+            joints3DGrp->Add(vol->GetID());
+          else if (vol->GetType() == SMDSAbs_Face)
+            joints2DGrp->Add(vol->GetID());
         }
     }
 
@@ -11444,11 +11554,8 @@ bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSorted
                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
               SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
 
-              stringstream grpname;
-              grpname << "mj_";
-              grpname << 0 << "_" << 0;
               int idg;
-              string namegrp = grpname.str();
+              string namegrp = "jointsMultiples";
               if (!mapOfJunctionGroups.count(namegrp))
                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
@@ -11457,7 +11564,7 @@ bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSorted
             }
           else
             {
-              MESSAGE("Quadratic multiple joints not implemented");
+              INFOS("Quadratic multiple joints not implemented");
               // TODO quadratic nodes
             }
         }
@@ -11750,8 +11857,11 @@ void SMESH_MeshEditor::CreateHoleSkin(double radius,
       groupDS = group->GetGroupDS();
       if ( !groupDS || groupDS->IsEmpty() ) continue;
       std::string grpName = group->GetName();
+      //MESSAGE("grpName=" << grpName);
       if (grpName == groupName)
         break;
+      else
+        groupDS = 0;
     }
 
   bool isNodeGroup = false;
@@ -11765,6 +11875,7 @@ void SMESH_MeshEditor::CreateHoleSkin(double radius,
 
   if (nodesCoords.size() > 0)
     isNodeCoords = true; // a list o nodes given by their coordinates
+  //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
 
   // --- define groups to build