Salome HOME
#19887 [CEA] Body fitting missing some faces and generates not-wanted splitted elements
[modules/smesh.git] / src / StdMeshers / StdMeshers_Cartesian_3D.cxx
index 96bbf63f5df11020d8f425ac787ec319b1c34064..7e444be893672c8d92438e4a8bc527f2f06ccf0d 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2007-2019  CEA/DEN, EDF R&D, OPEN CASCADE
+// Copyright (C) 2007-2020  CEA/DEN, EDF R&D, OPEN CASCADE
 //
 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
@@ -362,6 +362,9 @@ namespace
     gp_XYZ             _origin;
     gp_Mat             _invB; // inverted basis of _axes
 
+    // index shift within _nodes of nodes of a cell from the 1st node
+    int                _nodeShift[8];
+
     vector< const SMDS_MeshNode* >    _nodes; // mesh nodes at grid nodes
     vector< const F_IntersectPoint* > _gridIntP; // grid node intersection with geometry
     ObjectPool< E_IntersectPoint >    _edgeIntPool; // intersections with EDGEs
@@ -375,7 +378,6 @@ namespace
     bool                              _toUseThresholdForInternalFaces;
     double                            _sizeThreshold;
 
-    vector< TGeomID >                 _shapeIDs; // returned by Hexahedron::getSolids()
     SMESH_MesherHelper*               _helper;
 
     size_t CellIndex( size_t i, size_t j, size_t k ) const
@@ -428,6 +430,7 @@ namespace
     bool IsBoundaryFace( TGeomID face ) const { return _geometry._boundaryFaces.Contains( face ); }
     void SetOnShape( const SMDS_MeshNode* n, const F_IntersectPoint& ip, bool unset=false );
     bool IsToCheckNodePos() const { return !_toAddEdges && _toCreateFaces; }
+    bool IsToRemoveExcessEntities() const { return !_toAddEdges; }
 
     void SetCoordinates(const vector<double>& xCoords,
                         const vector<double>& yCoords,
@@ -443,12 +446,14 @@ namespace
    */
   struct CellsAroundLink
   {
+    int    _iDir;
     int    _dInd[4][3];
     size_t _nbCells[3];
     int    _i,_j,_k;
     Grid*  _grid;
 
     CellsAroundLink( Grid* grid, int iDir ):
+      _iDir( iDir ),
       _dInd{ {0,0,0}, {0,0,0}, {0,0,0}, {0,0,0} },
       _nbCells{ grid->_coords[0].size() - 1,
           grid->_coords[1].size() - 1,
@@ -467,7 +472,7 @@ namespace
       _j = j - _dInd[iL][1];
       _k = k - _dInd[iL][2];
     }
-    bool GetCell( int iL, int& i, int& j, int& k, int& cellIndex )
+    bool GetCell( int iL, int& i, int& j, int& k, int& cellIndex, int& linkIndex )
     {
       i =  _i + _dInd[iL][0];
       j =  _j + _dInd[iL][1];
@@ -477,6 +482,7 @@ namespace
            k < 0 || k >= (int)_nbCells[2] )
         return false;
       cellIndex = _grid->CellIndex( i,j,k );
+      linkIndex = iL + _iDir * 4;
       return true;
     }
   };
@@ -760,9 +766,13 @@ namespace
     // --------------------------------------------------------------------------------
     struct _Face
     {
+      SMESH_Block::TShapeID   _name;
       vector< _OrientedLink > _links;       // links on GridLine's
       vector< _Link >         _polyLinks;   // links added to close a polygonal face
       vector< _Node* >        _eIntNodes;   // nodes at intersection with EDGEs
+
+      _Face():_name( SMESH_Block::ID_NONE )
+      {}
       bool IsPolyLink( const _OrientedLink& ol )
       {
         return _polyLinks.empty() ? false :
@@ -790,41 +800,79 @@ namespace
     // --------------------------------------------------------------------------------
     struct _volumeDef // holder of nodes of a volume mesh element
     {
+      typedef void* _ptr;
+
       struct _nodeDef
       {
         const SMDS_MeshNode*    _node; // mesh node at hexahedron corner
         const B_IntersectPoint* _intPoint;
 
+        _nodeDef(): _node(0), _intPoint(0) {}
         _nodeDef( _Node* n ): _node( n->_node), _intPoint( n->_intPoint ) {}
         const SMDS_MeshNode*    Node() const
         { return ( _intPoint && _intPoint->_node ) ? _intPoint->_node : _node; }
         const E_IntersectPoint* EdgeIntPnt() const
         { return static_cast< const E_IntersectPoint* >( _intPoint ); }
+        _ptr Ptr() const { return Node() ? (_ptr) Node() : (_ptr) EdgeIntPnt(); }
+        bool operator==(const _nodeDef& other ) const { return Ptr() == other.Ptr(); }
       };
+
       vector< _nodeDef >      _nodes;
       vector< int >           _quantities;
       _volumeDef*             _next; // to store several _volumeDefs in a chain
       TGeomID                 _solidID;
       const SMDS_MeshElement* _volume; // new volume
 
+      vector< SMESH_Block::TShapeID > _names; // name of side a polygon originates from
+
       _volumeDef(): _next(0), _solidID(0), _volume(0) {}
       ~_volumeDef() { delete _next; }
       _volumeDef( _volumeDef& other ):
         _next(0), _solidID( other._solidID ), _volume( other._volume )
-      { _nodes.swap( other._nodes ); _quantities.swap( other._quantities ); other._volume = 0; }
+      { _nodes.swap( other._nodes ); _quantities.swap( other._quantities ); other._volume = 0;
+        _names.swap( other._names ); }
 
-      void Set( const vector< _Node* >& nodes, const vector< int >& quant = vector< int >() )
-      { _nodes.assign( nodes.begin(), nodes.end() ); _quantities = quant; }
+      size_t size() const { return 1 + ( _next ? _next->size() : 0 ); }
+      _volumeDef* at(int index)
+      { return index == 0 ? this : ( _next ? _next->at(index-1) : _next ); }
 
       void Set( _Node** nodes, int nb )
       { _nodes.assign( nodes, nodes + nb ); }
 
       void SetNext( _volumeDef* vd )
       { if ( _next ) { _next->SetNext( vd ); } else { _next = vd; }}
+
+      bool IsEmpty() const { return (( _nodes.empty() ) &&
+                                     ( !_next || _next->IsEmpty() )); }
+      bool IsPolyhedron() const { return ( !_quantities.empty() ||
+                                           ( _next && !_next->_quantities.empty() )); }
+
+
+      struct _linkDef: public std::pair<_ptr,_ptr> // to join polygons in removeExcessSideDivision()
+      {
+        _nodeDef _node1;//, _node2;
+        mutable /*const */_linkDef *_prev, *_next;
+        size_t _loopIndex;
+
+        _linkDef():_prev(0), _next(0) {}
+
+        void init( const _nodeDef& n1, const _nodeDef& n2, size_t iLoop )
+        {
+          _node1     = n1; //_node2 = n2;
+          _loopIndex = iLoop;
+          first      = n1.Ptr();
+          second     = n2.Ptr();
+          if ( first > second ) std::swap( first, second );
+        }
+        void setNext( _linkDef* next )
+        {
+          _next = next;
+          next->_prev = this;
+        }
+      };
     };
 
     // topology of a hexahedron
-    int   _nodeShift[8];
     _Node _hexNodes [8];
     _Link _hexLinks [12];
     _Face _hexQuads [6];
@@ -838,7 +886,7 @@ namespace
     // additional nodes created at intersection points
     vector< _Node > _intNodes;
 
-    // nodes inside the hexahedron (at VERTEXes)
+    // nodes inside the hexahedron (at VERTEXes) refer to _intNodes
     vector< _Node* > _vIntNodes;
 
     // computed volume elements
@@ -859,7 +907,7 @@ namespace
     Hexahedron(Grid* grid);
     int MakeElements(SMESH_MesherHelper&                      helper,
                      const map< TGeomID, vector< TGeomID > >& edge2faceIDsMap);
-    void ComputeElements( const Solid* solid = 0, int solidIndex = -1 );
+    void computeElements( const Solid* solid = 0, int solidIndex = -1 );
 
   private:
     Hexahedron(const Hexahedron& other, size_t i, size_t j, size_t k, int cellID );
@@ -867,7 +915,7 @@ namespace
     void init( size_t i );
     void setIJK( size_t i );
     bool compute( const Solid* solid, const IsInternalFlag intFlag );
-    const vector< TGeomID >& getSolids();
+    size_t getSolids( TGeomID ids[] );
     bool isCutByInternalFace( IsInternalFlag & maxFlag );
     void addEdges(SMESH_MesherHelper&                      helper,
                   vector< Hexahedron* >&                   intersectedHex,
@@ -894,6 +942,8 @@ namespace
                       const map< TGeomID, vector< TGeomID > >& edge2faceIDsMap );
     void getVolumes( vector< const SMDS_MeshElement* > & volumes );
     void getBoundaryElems( vector< const SMDS_MeshElement* > & boundaryVolumes );
+    void removeExcessSideDivision(const vector< Hexahedron* >& allHexa);
+    void removeExcessNodes(vector< Hexahedron* >& allHexa);
     TGeomID getAnyFace() const;
     void cutByExtendedInternal( std::vector< Hexahedron* >& hexes,
                                 const TColStd_MapOfInteger& intEdgeIDs );
@@ -928,7 +978,7 @@ namespace
       TID2Nb::iterator id2nb = id2nbMap.insert( s0 ).first;
       id2nb->second++;
     }
-  };
+  }; // class Hexahedron
 
 #ifdef WITH_TBB
   // --------------------------------------------------------------------------
@@ -943,7 +993,7 @@ namespace
     {
       for ( size_t i = r.begin(); i != r.end(); ++i )
         if ( Hexahedron* hex = _hexVec[ i ] )
-          hex->ComputeElements();
+          hex->computeElements();
     }
   };
   // --------------------------------------------------------------------------
@@ -2090,14 +2140,14 @@ namespace
     size_t i101 = i100 + dz;
     size_t i011 = i010 + dz;
     size_t i111 = i110 + dz;
-    _nodeShift[ SMESH_Block::ShapeIndex( SMESH_Block::ID_V000 )] = i000;
-    _nodeShift[ SMESH_Block::ShapeIndex( SMESH_Block::ID_V100 )] = i100;
-    _nodeShift[ SMESH_Block::ShapeIndex( SMESH_Block::ID_V010 )] = i010;
-    _nodeShift[ SMESH_Block::ShapeIndex( SMESH_Block::ID_V110 )] = i110;
-    _nodeShift[ SMESH_Block::ShapeIndex( SMESH_Block::ID_V001 )] = i001;
-    _nodeShift[ SMESH_Block::ShapeIndex( SMESH_Block::ID_V101 )] = i101;
-    _nodeShift[ SMESH_Block::ShapeIndex( SMESH_Block::ID_V011 )] = i011;
-    _nodeShift[ SMESH_Block::ShapeIndex( SMESH_Block::ID_V111 )] = i111;
+    grid->_nodeShift[ SMESH_Block::ShapeIndex( SMESH_Block::ID_V000 )] = i000;
+    grid->_nodeShift[ SMESH_Block::ShapeIndex( SMESH_Block::ID_V100 )] = i100;
+    grid->_nodeShift[ SMESH_Block::ShapeIndex( SMESH_Block::ID_V010 )] = i010;
+    grid->_nodeShift[ SMESH_Block::ShapeIndex( SMESH_Block::ID_V110 )] = i110;
+    grid->_nodeShift[ SMESH_Block::ShapeIndex( SMESH_Block::ID_V001 )] = i001;
+    grid->_nodeShift[ SMESH_Block::ShapeIndex( SMESH_Block::ID_V101 )] = i101;
+    grid->_nodeShift[ SMESH_Block::ShapeIndex( SMESH_Block::ID_V011 )] = i011;
+    grid->_nodeShift[ SMESH_Block::ShapeIndex( SMESH_Block::ID_V111 )] = i111;
 
     vector< int > idVec;
     // set nodes to links
@@ -2113,8 +2163,10 @@ namespace
     int interlace[4] = { 0, 3, 1, 2 }; // to walk by links around a face: { u0, 1v, u1, 0v }
     for ( int faceID = SMESH_Block::ID_Fxy0; faceID <= SMESH_Block::ID_F1yz; ++faceID )
     {
-      SMESH_Block::GetFaceEdgesIDs( faceID, idVec );
       _Face& quad = _hexQuads[ SMESH_Block::ShapeIndex( faceID )];
+      quad._name = (SMESH_Block::TShapeID) faceID;
+
+      SMESH_Block::GetFaceEdgesIDs( faceID, idVec );
       bool revFace = ( faceID == SMESH_Block::ID_Fxy0 ||
                        faceID == SMESH_Block::ID_Fx1z ||
                        faceID == SMESH_Block::ID_F0yz );
@@ -2142,9 +2194,6 @@ namespace
     _polygons.reserve(100); // to avoid reallocation;
 
     // copy topology
-    for ( int i = 0; i < 8; ++i )
-      _nodeShift[i] = other._nodeShift[i];
-
     for ( int i = 0; i < 12; ++i )
     {
       const _Link& srcLink = other._hexLinks[ i ];
@@ -2157,6 +2206,7 @@ namespace
     {
       const _Face& srcQuad = other._hexQuads[ i ];
       _Face&       tgtQuad = this->_hexQuads[ i ];
+      tgtQuad._name = srcQuad._name;
       tgtQuad._links.resize(4);
       for ( int j = 0; j < 4; ++j )
       {
@@ -2175,13 +2225,12 @@ namespace
   /*!
    * \brief Return IDs of SOLIDs interfering with this Hexahedron
    */
-  const vector< TGeomID >& Hexahedron::getSolids()
+  size_t Hexahedron::getSolids( TGeomID ids[] )
   {
-    _grid->_shapeIDs.clear();
     if ( _grid->_geometry.IsOneSolid() )
     {
-      _grid->_shapeIDs.push_back( _grid->GetSolid()->ID() );
-      return _grid->_shapeIDs;
+      ids[0] = _grid->GetSolid()->ID();
+      return 1;
     }
     // count intersection points belonging to each SOLID
     TID2Nb id2NbPoints;
@@ -2190,8 +2239,8 @@ namespace
     _origNodeInd = _grid->NodeIndex( _i,_j,_k );
     for ( int iN = 0; iN < 8; ++iN )
     {
-      _hexNodes[iN]._node     = _grid->_nodes   [ _origNodeInd + _nodeShift[iN] ];
-      _hexNodes[iN]._intPoint = _grid->_gridIntP[ _origNodeInd + _nodeShift[iN] ];
+      _hexNodes[iN]._node     = _grid->_nodes   [ _origNodeInd + _grid->_nodeShift[iN] ];
+      _hexNodes[iN]._intPoint = _grid->_gridIntP[ _origNodeInd + _grid->_nodeShift[iN] ];
 
       if ( _hexNodes[iN]._intPoint ) // intersection with a FACE
       {
@@ -2231,12 +2280,12 @@ namespace
         insertAndIncrement( solidIDs[i], id2NbPoints );
     }
 
-    _grid->_shapeIDs.reserve( id2NbPoints.size() );
+    size_t nbSolids = 0;
     for ( TID2Nb::iterator id2nb = id2NbPoints.begin(); id2nb != id2NbPoints.end(); ++id2nb )
       if ( id2nb->second >= 3 )
-        _grid->_shapeIDs.push_back( id2nb->first );
+        ids[ nbSolids++ ] = id2nb->first;
 
-    return _grid->_shapeIDs;
+    return nbSolids;
   }
 
   //================================================================================
@@ -2350,8 +2399,8 @@ namespace
     {
       _hexNodes[iN]._isInternalFlags = 0;
 
-      _hexNodes[iN]._node     = _grid->_nodes   [ _origNodeInd + _nodeShift[iN] ];
-      _hexNodes[iN]._intPoint = _grid->_gridIntP[ _origNodeInd + _nodeShift[iN] ];
+      _hexNodes[iN]._node     = _grid->_nodes   [ _origNodeInd + _grid->_nodeShift[iN] ];
+      _hexNodes[iN]._intPoint = _grid->_gridIntP[ _origNodeInd + _grid->_nodeShift[iN] ];
 
       if ( _hexNodes[iN]._node && !solid->Contains( _hexNodes[iN]._node->GetShapeID() ))
         _hexNodes[iN]._node = 0;
@@ -2571,21 +2620,22 @@ namespace
   /*!
    * \brief Compute mesh volumes resulted from intersection of the Hexahedron
    */
-  void Hexahedron::ComputeElements( const Solid* solid, int solidIndex )
+  void Hexahedron::computeElements( const Solid* solid, int solidIndex )
   {
     if ( !solid )
     {
       solid = _grid->GetSolid();
       if ( !_grid->_geometry.IsOneSolid() )
       {
-        const vector< TGeomID >& solidIDs = getSolids();
-        if ( solidIDs.size() > 1 )
+        TGeomID solidIDs[20];
+        size_t nbSolids = getSolids( solidIDs );
+        if ( nbSolids > 1 )
         {
-          for ( size_t i = 0; i < solidIDs.size(); ++i )
+          for ( size_t i = 0; i < nbSolids; ++i )
           {
             solid = _grid->GetSolid( solidIDs[i] );
-            ComputeElements( solid, i );
-            if ( !_volumeDefs._nodes.empty() && i < solidIDs.size() - 1 )
+            computeElements( solid, i );
+            if ( !_volumeDefs._nodes.empty() && i < nbSolids - 1 )
               _volumeDefs.SetNext( new _volumeDef( _volumeDefs ));
           }
           return;
@@ -2649,6 +2699,7 @@ namespace
       _polygons.resize( _polygons.size() + 1 );
       _Face* polygon = &_polygons.back();
       polygon->_polyLinks.reserve( 20 );
+      polygon->_name = quad._name;
 
       splits.clear();
       for ( int iE = 0; iE < 4; ++iE ) // loop on 4 sides of a quadrangle
@@ -2683,6 +2734,7 @@ namespace
           _polygons.resize( _polygons.size() + 1 );
           polygon = &_polygons.back();
           polygon->_polyLinks.reserve( 20 );
+          polygon->_name = quad._name;
         }
         polygon->_links.push_back( splits[ iS ] );
         splits[ iS++ ]._link = 0;
@@ -3149,6 +3201,40 @@ namespace
     if ( _hasTooSmall )
       return false; // too small volume
 
+
+    // Try to find out names of no-name polygons (issue # 19887)
+    if ( _grid->IsToRemoveExcessEntities() && _polygons.back()._name == SMESH_Block::ID_NONE )
+    {
+      gp_XYZ uvwCenter =
+        0.5 * ( _grid->_coords[0][_i] + _grid->_coords[0][_i+1] ) * _grid->_axes[0] +
+        0.5 * ( _grid->_coords[1][_j] + _grid->_coords[1][_j+1] ) * _grid->_axes[1] +
+        0.5 * ( _grid->_coords[2][_k] + _grid->_coords[2][_k+1] ) * _grid->_axes[2];
+      for ( size_t i = _polygons.size() - 1; _polygons[i]._name == SMESH_Block::ID_NONE; --i )
+      {
+        _Face& face = _polygons[ i ];
+        Bnd_Box bb;
+        gp_Pnt uvw;
+        for ( size_t iL = 0; iL < face._links.size(); ++iL )
+        {
+          _Node* n = face._links[ iL ].FirstNode();
+          gp_XYZ p = SMESH_NodeXYZ( n->Node() );
+          _grid->ComputeUVW( p, uvw.ChangeCoord().ChangeData() );
+          bb.Add( uvw );
+        }
+        gp_Pnt pMin = bb.CornerMin();
+        if ( bb.IsXThin( _grid->_tol ))
+          face._name = pMin.X() < uvwCenter.X() ? SMESH_Block::ID_F0yz : SMESH_Block::ID_F1yz;
+        else if ( bb.IsYThin( _grid->_tol ))
+          face._name = pMin.Y() < uvwCenter.Y() ? SMESH_Block::ID_Fx0z : SMESH_Block::ID_Fx1z;
+        else if ( bb.IsZThin( _grid->_tol ))
+          face._name = pMin.Z() < uvwCenter.Z() ? SMESH_Block::ID_Fxy0 : SMESH_Block::ID_Fxy1;
+      }
+    }
+
+    _volumeDefs._nodes.clear();
+    _volumeDefs._quantities.clear();
+    _volumeDefs._names.clear();
+
     // create a classic cell if possible
 
     int nbPolygons = 0;
@@ -3169,14 +3255,12 @@ namespace
     else if ( nbNodes == 5 && nbPolygons == 5 ) isClassicElem = addPyra ();
     if ( !isClassicElem )
     {
-      _volumeDefs._nodes.clear();
-      _volumeDefs._quantities.clear();
-
       for ( size_t iF = 0; iF < _polygons.size(); ++iF )
       {
         const size_t nbLinks = _polygons[ iF ]._links.size();
         if ( nbLinks == 0 ) continue;
         _volumeDefs._quantities.push_back( nbLinks );
+        _volumeDefs._names.push_back( _polygons[ iF ]._name );
         for ( size_t iL = 0; iL < nbLinks; ++iL )
           _volumeDefs._nodes.push_back( _polygons[ iF ]._links[ iL ].FirstNode() );
       }
@@ -3200,7 +3284,7 @@ namespace
     int nbIntHex = 0;
 
     // set intersection nodes from GridLine's to links of allHexa
-    int i,j,k, cellIndex;
+    int i,j,k, cellIndex, iLink;
     for ( int iDir = 0; iDir < 3; ++iDir )
     {
       // loop on GridLine's parallel to iDir
@@ -3217,7 +3301,7 @@ namespace
           fourCells.Init( lineInd.I(), lineInd.J(), lineInd.K() );
           for ( int iL = 0; iL < 4; ++iL ) // loop on 4 cells sharing a link
           {
-            if ( !fourCells.GetCell( iL, i,j,k, cellIndex ))
+            if ( !fourCells.GetCell( iL, i,j,k, cellIndex, iLink ))
               continue;
             Hexahedron *& hex = allHexa[ cellIndex ];
             if ( !hex)
@@ -3225,7 +3309,6 @@ namespace
               hex = new Hexahedron( *this, i, j, k, cellIndex );
               ++nbIntHex;
             }
-            const int iLink = iL + iDir * 4;
             hex->_hexLinks[iLink]._fIntPoints.push_back( &(*ip) );
             hex->_nbFaceIntNodes += bool( ip->_node );
           }
@@ -3238,6 +3321,7 @@ namespace
 
     // add not split hexahedra to the mesh
     int nbAdded = 0;
+    TGeomID solidIDs[20];
     vector< Hexahedron* > intHexa; intHexa.reserve( nbIntHex );
     vector< const SMDS_MeshElement* > boundaryVolumes; boundaryVolumes.reserve( nbIntHex * 1.1 );
     for ( size_t i = 0; i < allHexa.size(); ++i )
@@ -3273,7 +3357,8 @@ namespace
         }
         else
         {
-          solidID = getSolids()[0];
+          getSolids( solidIDs );
+          solidID = solidIDs[0];
         }
         mesh->SetMeshElementOnShape( el, solidID );
         ++nbAdded;
@@ -3293,22 +3378,33 @@ namespace
       }
     }
 
-    // add elements resulted from hexadron intersection
+    // compute definitions of volumes resulted from hexadron intersection
 #ifdef WITH_TBB
     tbb::parallel_for ( tbb::blocked_range<size_t>( 0, intHexa.size() ),
                         ParallelHexahedron( intHexa ),
-                        tbb::simple_partitioner()); // ComputeElements() is called here
+                        tbb::simple_partitioner()); // computeElements() is called here
+#else
     for ( size_t i = 0; i < intHexa.size(); ++i )
       if ( Hexahedron * hex = intHexa[ i ] )
-        nbAdded += hex->addVolumes( helper );
-#else
+        hex->computeElements();
+#endif
+
+    // simplify polyhedrons
+    if ( _grid->IsToRemoveExcessEntities() )
+    {
+      for ( size_t i = 0; i < intHexa.size(); ++i )
+        if ( Hexahedron * hex = intHexa[ i ] )
+          hex->removeExcessSideDivision( allHexa );
+
+      for ( size_t i = 0; i < intHexa.size(); ++i )
+        if ( Hexahedron * hex = intHexa[ i ] )
+          hex->removeExcessNodes( allHexa );
+    }
+
+    // add volumes
     for ( size_t i = 0; i < intHexa.size(); ++i )
       if ( Hexahedron * hex = intHexa[ i ] )
-      {
-        hex->ComputeElements();
         nbAdded += hex->addVolumes( helper );
-      }
-#endif
 
     // fill boundaryVolumes with volumes neighboring too small skipped volumes
     if ( _grid->_toCreateFaces )
@@ -3603,7 +3699,8 @@ namespace
           int  i = ! ( u < _grid->_tol ); // [0,1]
           int iN = link._nodes[ i ] - hex->_hexNodes; // [0-7]
 
-          const F_IntersectPoint * & ip = _grid->_gridIntP[ hex->_origNodeInd + _nodeShift[iN] ];
+          const F_IntersectPoint * & ip = _grid->_gridIntP[ hex->_origNodeInd +
+                                                            _grid->_nodeShift[iN] ];
           if ( !ip )
           {
             ip = _grid->_extIntPool.getNew();
@@ -3631,13 +3728,12 @@ namespace
           int i,j,k, cellIndex;
           for ( int iC = 0; iC < 4; ++iC ) // loop on 4 cells sharing the link
           {
-            if ( !fourCells.GetCell( iC, i,j,k, cellIndex ))
+            if ( !fourCells.GetCell( iC, i,j,k, cellIndex, iLink ))
               continue;
             Hexahedron * h = hexes[ cellIndex ];
             if ( !h )
               h = hexes[ cellIndex ] = new Hexahedron( *this, i, j, k, cellIndex );
-            const int iL = iC + iDir * 4;
-            h->_hexLinks[iL]._fIntPoints.push_back( ip );
+            h->_hexLinks[iLink]._fIntPoints.push_back( ip );
             h->_nbFaceIntNodes++;
             //isCut = true;
           }
@@ -3697,8 +3793,8 @@ namespace
 
     for ( size_t iN = 0; iN < 8; ++iN ) // check corners
     {
-      _hexNodes[iN]._node     = _grid->_nodes   [ _origNodeInd + _nodeShift[iN] ];
-      _hexNodes[iN]._intPoint = _grid->_gridIntP[ _origNodeInd + _nodeShift[iN] ];
+      _hexNodes[iN]._node     = _grid->_nodes   [ _origNodeInd + _grid->_nodeShift[iN] ];
+      _hexNodes[iN]._intPoint = _grid->_gridIntP[ _origNodeInd + _grid->_nodeShift[iN] ];
       if ( _hexNodes[iN]._intPoint )
         for ( size_t iF = 0; iF < _hexNodes[iN]._intPoint->_faceIDs.size(); ++iF )
         {
@@ -4957,7 +5053,7 @@ namespace
   /*!
    * \brief Return created volumes and volumes that can have free facet because of
    *        skipped small volume. Also create mesh faces on free facets
-   *        of adjacent not-cut volumes id the result volume is too small.
+   *        of adjacent not-cut volumes if the result volume is too small.
    */
   void Hexahedron::getBoundaryElems( vector< const SMDS_MeshElement* > & boundaryElems )
   {
@@ -5004,8 +5100,9 @@ namespace
           if ( !faceID )
             break;
           if ( _grid->IsInternal( faceID ) ||
-               _grid->IsShared( faceID ) ||
-               _grid->IsBoundaryFace( faceID ))
+               _grid->IsShared( faceID ) //||
+               //_grid->IsBoundaryFace( faceID ) -- commented for #19887
+               ) 
             break; // create only if a new face will be used by other 3D algo
         }
 
@@ -5034,6 +5131,337 @@ namespace
     }
   }
 
+  //================================================================================
+  /*!
+   * \brief Remove edges and nodes dividing a hexa side in the case if an adjacent
+   *        volume also sharing the dividing edge is missing due to its small side.
+   *        Issue #19887.
+   */
+  //================================================================================
+
+  void Hexahedron::removeExcessSideDivision(const vector< Hexahedron* >& allHexa)
+  {
+    if ( ! _volumeDefs.IsPolyhedron() )
+      return; // not a polyhedron
+      
+    // look for a divided side adjacent to a small hexahedron
+
+    int di[6] = { 0, 0, 0, 0,-1, 1 };
+    int dj[6] = { 0, 0,-1, 1, 0, 0 };
+    int dk[6] = {-1, 1, 0, 0, 0, 0 };
+
+    for ( int iF = 0; iF < 6; ++iF ) // loop on 6 sides of a hexahedron
+    {
+      size_t neighborIndex = _grid->CellIndex( _i + di[iF],
+                                               _j + dj[iF],
+                                               _k + dk[iF] );
+      if ( neighborIndex >= allHexa.size() ||
+           !allHexa[ neighborIndex ]       ||
+           !allHexa[ neighborIndex ]->_hasTooSmall )
+        continue;
+
+      // check if a side is divided into several polygons
+      for ( _volumeDef* volDef = &_volumeDefs; volDef; volDef = volDef->_next )
+      {
+        int nbPolygons = 0, nbNodes = 0;
+        for ( size_t i = 0; i < volDef->_names.size(); ++i )
+          if ( volDef->_names[ i ] == _hexQuads[ iF ]._name )
+          {
+            ++nbPolygons;
+            nbNodes += volDef->_quantities[ i ];
+          }
+        if ( nbPolygons < 2 )
+          continue;
+
+        // construct loops from polygons
+        typedef _volumeDef::_linkDef TLinkDef;
+        std::vector< TLinkDef* > loops;
+        std::vector< TLinkDef > links( nbNodes );
+        for ( size_t i = 0, iN = 0, iLoop = 0; iLoop < volDef->_quantities.size(); ++iLoop )
+        {
+          size_t nbLinks = volDef->_quantities[ iLoop ];
+          if ( volDef->_names[ iLoop ] != _hexQuads[ iF ]._name )
+          {
+            iN += nbLinks;
+            continue;
+          }
+          loops.push_back( & links[i] );
+          for ( size_t n = 0; n < nbLinks-1; ++n, ++i, ++iN )
+          {
+            links[i].init( volDef->_nodes[iN], volDef->_nodes[iN+1], iLoop );
+            links[i].setNext( &links[i+1] );
+          }
+          links[i].init( volDef->_nodes[iN], volDef->_nodes[iN-nbLinks+1], iLoop );
+          links[i].setNext( &links[i-nbLinks+1] );
+          ++i; ++iN;
+        }
+
+        // look for equal links in different loops and join such loops
+        bool loopsJoined = false;
+        std::set< TLinkDef > linkSet;
+        for ( size_t iLoop = 0; iLoop < loops.size(); ++iLoop )
+        {
+          TLinkDef* beg = 0;
+          for ( TLinkDef* l = loops[ iLoop ]; l != beg; l = l->_next ) // walk around the iLoop
+          {
+            std::pair< std::set< TLinkDef >::iterator, bool > it2new = linkSet.insert( *l );
+            if ( !it2new.second ) // equal found, join loops
+            {
+              const TLinkDef* equal = &(*it2new.first);
+              if ( equal->_loopIndex == l->_loopIndex )
+                continue; // error?
+
+              loopsJoined = true;
+
+              for ( size_t i = iLoop - 1; i < loops.size(); --i )
+                if ( loops[ i ] && loops[ i ]->_loopIndex == equal->_loopIndex )
+                  loops[ i ] = 0;
+
+              // exclude l and equal and join two loops
+              if ( l->_prev != equal )
+                l->_prev->setNext( equal->_next );
+              if ( equal->_prev != l )
+                equal->_prev->setNext( l->_next );
+
+              if ( volDef->_quantities[ l->_loopIndex ] > 0 )
+                volDef->_quantities[ l->_loopIndex     ] *= -1;
+              if ( volDef->_quantities[ equal->_loopIndex ] > 0 )
+                volDef->_quantities[ equal->_loopIndex ] *= -1;
+
+              if ( loops[ iLoop ] == l )
+                loops[ iLoop ] = l->_prev->_next;
+            }
+            beg = loops[ iLoop ];
+          }
+        }
+        // update volDef
+        if ( loopsJoined )
+        {
+          // set unchanged polygons
+          std::vector< int >                  newQuantities;
+          std::vector< _volumeDef::_nodeDef > newNodes;
+          vector< SMESH_Block::TShapeID >     newNames;
+          newQuantities.reserve( volDef->_quantities.size() );
+          newNodes.reserve     ( volDef->_nodes.size() );
+          newNames.reserve     ( volDef->_names.size() );
+          for ( size_t i = 0, iLoop = 0; iLoop < volDef->_quantities.size(); ++iLoop )
+          {
+            if ( volDef->_quantities[ iLoop ] < 0 )
+            {
+              i -= volDef->_quantities[ iLoop ];
+              continue;
+            }
+            newQuantities.push_back( volDef->_quantities[ iLoop ]);
+            newNodes.insert( newNodes.end(),
+                             volDef->_nodes.begin() + i,
+                             volDef->_nodes.begin() + i + newQuantities.back() );
+            newNames.push_back( volDef->_names[ iLoop ]);
+            i += volDef->_quantities[ iLoop ];
+          }
+
+          // set joined loops
+          for ( size_t iLoop = 0; iLoop < loops.size(); ++iLoop )
+          {
+            if ( !loops[ iLoop ] )
+              continue;
+            newQuantities.push_back( 0 );
+            TLinkDef* beg = 0;
+            for ( TLinkDef* l = loops[ iLoop ]; l != beg; l = l->_next, ++newQuantities.back() )
+            {
+              newNodes.push_back( l->_node1 );
+              beg = loops[ iLoop ];
+            }
+            newNames.push_back( _hexQuads[ iF ]._name );
+          }
+          volDef->_quantities.swap( newQuantities );
+          volDef->_nodes.swap( newNodes );
+          volDef->_names.swap( newNames );
+        }
+      } // loop on volDef's
+    } // loop on hex sides
+
+    return;
+  } // removeExcessSideDivision()
+
+
+  //================================================================================
+  /*!
+   * \brief Remove nodes splitting Cartesian cell edges in the case if a node
+   *        is used in every cells only by two polygons sharing the edge
+   *        Issue #19887.
+   */
+  //================================================================================
+
+  void Hexahedron::removeExcessNodes(vector< Hexahedron* >& allHexa)
+  {
+    if ( ! _volumeDefs.IsPolyhedron() )
+      return; // not a polyhedron
+
+    typedef vector< _volumeDef::_nodeDef >::iterator TNodeIt;
+    vector< int > nodesInPoly[ 4 ]; // node index in _volumeDefs._nodes
+    vector< int > volDefInd  [ 4 ]; // index of a _volumeDefs
+    Hexahedron*   hexa       [ 4 ];
+    int i,j,k, cellIndex, iLink = 0, iCellLink;
+    for ( int iDir = 0; iDir < 3; ++iDir )
+    {
+      CellsAroundLink fourCells( _grid, iDir );
+      for ( int iL = 0; iL < 4; ++iL, ++iLink ) // 4 links in a direction
+      {
+        _Link& link = _hexLinks[ iLink ];
+        fourCells.Init( _i, _j, _k, iLink );
+
+        for ( size_t iP = 0; iP < link._fIntPoints.size(); ++iP ) // loop on nodes on the link
+        {
+          bool nodeRemoved = true;
+          _volumeDef::_nodeDef node; node._intPoint = link._fIntPoints[iP];
+
+          for ( size_t i = 0, nb = _volumeDefs.size(); i < nb &&  nodeRemoved; ++i )
+            if ( _volumeDef* vol = _volumeDefs.at( i ))
+              nodeRemoved =
+                ( std::find( vol->_nodes.begin(), vol->_nodes.end(), node ) == vol->_nodes.end() );
+          if ( nodeRemoved )
+            continue; // node already removed
+
+          // check if a node encounters zero or two times in 4 cells sharing iLink
+          // if so, the node can be removed from the cells
+          bool       nodeIsOnEdge = true;
+          int nbPolyhedraWithNode = 0;
+          for ( int iC = 0; iC < 4; ++iC ) // loop on 4 cells sharing a link
+          {
+            nodesInPoly[ iC ].clear();
+            volDefInd  [ iC ].clear();
+            hexa       [ iC ] = 0;
+            if ( !fourCells.GetCell( iC, i,j,k, cellIndex, iCellLink ))
+              continue;
+            hexa[ iC ] = allHexa[ cellIndex ];
+            if ( !hexa[ iC ])
+              continue;
+            for ( size_t i = 0, nb = hexa[ iC ]->_volumeDefs.size(); i < nb; ++i )
+              if ( _volumeDef* vol = hexa[ iC ]->_volumeDefs.at( i ))
+              {
+                for ( TNodeIt nIt = vol->_nodes.begin(); nIt != vol->_nodes.end(); ++nIt )
+                {
+                  nIt = std::find( nIt, vol->_nodes.end(), node );
+                  if ( nIt != vol->_nodes.end() )
+                  {
+                    nodesInPoly[ iC ].push_back( std::distance( vol->_nodes.begin(), nIt ));
+                    volDefInd  [ iC ].push_back( i );
+                  }
+                  else
+                    break;
+                }
+                nbPolyhedraWithNode += ( !nodesInPoly[ iC ].empty() );
+              }
+            if ( nodesInPoly[ iC ].size() != 0 &&
+                 nodesInPoly[ iC ].size() != 2 )
+            {
+              nodeIsOnEdge = false;
+              break;
+            }
+          } // loop  on 4 cells
+
+          // remove nodes from polyhedra
+          if ( nbPolyhedraWithNode > 0 && nodeIsOnEdge )
+          {
+            for ( int iC = 0; iC < 4; ++iC ) // loop on 4 cells sharing the link
+            {
+              if ( nodesInPoly[ iC ].empty() )
+                continue;
+              for ( int i = volDefInd[ iC ].size() - 1; i >= 0; --i )
+              {
+                _volumeDef* vol = hexa[ iC ]->_volumeDefs.at( volDefInd[ iC ][ i ]);
+                int nIndex = nodesInPoly[ iC ][ i ];
+                // decrement _quantities
+                for ( size_t iQ = 0; iQ < vol->_quantities.size(); ++iQ )
+                  if ( nIndex < vol->_quantities[ iQ ])
+                  {
+                    vol->_quantities[ iQ ]--;
+                    break;
+                  }
+                  else
+                  {
+                    nIndex -= vol->_quantities[ iQ ];
+                  }
+                vol->_nodes.erase( vol->_nodes.begin() + nodesInPoly[ iC ][ i ]);
+
+                if ( i == 0 &&
+                     vol->_nodes.size() == 6 * 4 &&
+                     vol->_quantities.size() == 6 ) // polyhedron becomes hexahedron?
+                {
+                  bool allQuads = true;
+                  for ( size_t iQ = 0; iQ < vol->_quantities.size() &&  allQuads; ++iQ )
+                    allQuads = ( vol->_quantities[ iQ ] == 4 );
+                  if ( allQuads )
+                  {
+                    // set side nodes as this: bottom, top, top, ...
+                    int iTop, iBot; // side indices
+                    for ( int iS = 0; iS < 6; ++iS )
+                    {
+                      if ( vol->_names[ iS ] == SMESH_Block::ID_Fxy0 )
+                        iBot = iS;
+                      if ( vol->_names[ iS ] == SMESH_Block::ID_Fxy1 )
+                        iTop = iS;
+                    }
+                    if ( iBot != 0 )
+                    {
+                      if ( iTop == 0 )
+                      {
+                        std::copy( vol->_nodes.begin(),
+                                   vol->_nodes.begin() + 4,
+                                   vol->_nodes.begin() + 4 );
+                        iTop = 1;
+                      }
+                      std::copy( vol->_nodes.begin() + 4 * iBot,
+                                 vol->_nodes.begin() + 4 * ( iBot + 1),
+                                 vol->_nodes.begin() );
+                    }
+                    if ( iTop != 1 )
+                      std::copy( vol->_nodes.begin() + 4 * iTop,
+                                 vol->_nodes.begin() + 4 * ( iTop + 1),
+                                 vol->_nodes.begin() + 4 );
+
+                    std::copy( vol->_nodes.begin() + 4,
+                               vol->_nodes.begin() + 8,
+                               vol->_nodes.begin() + 8 );
+                    // set up top facet nodes by comparing their uvw with bottom nodes
+                    E_IntersectPoint ip[8];
+                    for ( int iN = 0; iN < 8; ++iN )
+                    {
+                      SMESH_NodeXYZ p = vol->_nodes[ iN ].Node();
+                      _grid->ComputeUVW( p, ip[ iN ]._uvw );
+                    }
+                    const double tol2 = _grid->_tol * _grid->_tol;
+                    for ( int iN = 0; iN < 4; ++iN )
+                    {
+                      gp_Pnt2d pBot( ip[ iN ]._uvw[0], ip[ iN ]._uvw[1] );
+                      for ( int iT = 4; iT < 8; ++iT )
+                      {
+                        gp_Pnt2d pTop( ip[ iT ]._uvw[0], ip[ iT ]._uvw[1] );
+                        if ( pBot.SquareDistance( pTop ) < tol2 )
+                        {
+                          // vol->_nodes[ iN + 4 ]._node = ip[ iT ]._node;
+                          // vol->_nodes[ iN + 4 ]._intPoint = 0;
+                          vol->_nodes[ iN + 4 ] = vol->_nodes[ iT + 4 ];
+                          break;
+                        }
+                      }
+                    }
+                    vol->_nodes.resize( 8 );
+                    vol->_quantities.clear();
+                    //vol->_names.clear();
+                  }
+                }
+              } // loop on _volumeDefs
+            } // loop on 4 cell abound a link
+          } // if ( nodeIsOnEdge )
+        } // loop on intersection points of a link
+      } // loop on 4 links of a direction
+    } // loop on 3 directions
+
+    return;
+
+  } // removeExcessNodes()
+
   //================================================================================
   /*!
    * \brief Set to _hexLinks a next portion of splits located on one side of INTERNAL FACEs