Salome HOME
StructuredCGNS - Write FamilyName info to be able to get the original group name...
[modules/smesh.git] / src / StdMeshers / StdMeshers_Cartesian_3D.cxx
index 62c3abba05c00aaad215dc1572a3f7ce0ff5c424..c392deeeaa41d3cc75f64671002a8a94f0b1842a 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2007-2021  CEA/DEN, EDF R&D, OPEN CASCADE
+// Copyright (C) 2007-2024  CEA, EDF, OPEN CASCADE
 //
 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
 //
 #include "StdMeshers_Cartesian_3D.hxx"
 #include "StdMeshers_CartesianParameters3D.hxx"
-
-#include "ObjectPool.hxx"
-#include "SMDS_MeshNode.hxx"
-#include "SMDS_VolumeTool.hxx"
-#include "SMESHDS_Mesh.hxx"
-#include "SMESH_Block.hxx"
-#include "SMESH_Comment.hxx"
-#include "SMESH_ControlsDef.hxx"
-#include "SMESH_Mesh.hxx"
-#include "SMESH_MeshAlgos.hxx"
-#include "SMESH_MeshEditor.hxx"
-#include "SMESH_MesherHelper.hxx"
-#include "SMESH_subMesh.hxx"
-#include "SMESH_subMeshEventListener.hxx"
+#include "StdMeshers_Cartesian_VL.hxx"
 #include "StdMeshers_FaceSide.hxx"
+#include "StdMeshers_ViscousLayers.hxx"
+
+#include <ObjectPool.hxx>
+#include <SMDS_LinearEdge.hxx>
+#include <SMDS_MeshNode.hxx>
+#include <SMDS_VolumeOfNodes.hxx>
+#include <SMDS_VolumeTool.hxx>
+#include <SMESHDS_Mesh.hxx>
+#include <SMESH_Block.hxx>
+#include <SMESH_Comment.hxx>
+#include <SMESH_ControlsDef.hxx>
+#include <SMESH_Mesh.hxx>
+#include <SMESH_MeshAlgos.hxx>
+#include <SMESH_MeshEditor.hxx>
+#include <SMESH_MesherHelper.hxx>
+#include <SMESH_subMesh.hxx>
+#include <SMESH_subMeshEventListener.hxx>
 
 #include <utilities.h>
 #include <Utils_ExceptHandlers.hxx>
 #include <gp_Sphere.hxx>
 #include <gp_Torus.hxx>
 
+//STD
 #include <limits>
+#include <mutex>
+#include <thread>
 
 #include <boost/container/flat_map.hpp>
 
 #define WINVER 0x0A00
 #define _WIN32_WINNT 0x0A00
 #endif
-
+#include <algorithm>
 #include <tbb/parallel_for.h>
-//#include <tbb/enumerable_thread_specific.h>
 #endif
 
 using namespace std;
 using namespace SMESH;
+std::mutex _eMutex;
+std::mutex _bMutex;
 
 //=============================================================================
 /*!
@@ -130,7 +138,8 @@ StdMeshers_Cartesian_3D::StdMeshers_Cartesian_3D(int hypId, SMESH_Gen * gen)
 {
   _name = "Cartesian_3D";
   _shapeType = (1 << TopAbs_SOLID);       // 1 bit /shape type
-  _compatibleHypothesis.push_back("CartesianParameters3D");
+  _compatibleHypothesis.push_back( "CartesianParameters3D" );
+  _compatibleHypothesis.push_back( StdMeshers_ViscousLayers::GetHypType() );
 
   _onlyUnaryInput = false;          // to mesh all SOLIDs at once
   _requireDiscreteBoundary = false; // 2D mesh not needed
@@ -149,19 +158,26 @@ bool StdMeshers_Cartesian_3D::CheckHypothesis (SMESH_Mesh&          aMesh,
 {
   aStatus = SMESH_Hypothesis::HYP_MISSING;
 
-  const list<const SMESHDS_Hypothesis*>& hyps = GetUsedHypothesis(aMesh, aShape);
+  const list<const SMESHDS_Hypothesis*>& hyps = GetUsedHypothesis(aMesh, aShape, /*skipAux=*/false);
   list <const SMESHDS_Hypothesis* >::const_iterator h = hyps.begin();
   if ( h == hyps.end())
   {
     return false;
   }
 
+  _hyp = nullptr;
+  _hypViscousLayers = nullptr;
+  _isComputeOffset = false;
+
   for ( ; h != hyps.end(); ++h )
   {
-    if (( _hyp = dynamic_cast<const StdMeshers_CartesianParameters3D*>( *h )))
+    if ( !_hyp && ( _hyp = dynamic_cast<const StdMeshers_CartesianParameters3D*>( *h )))
     {
       aStatus = _hyp->IsDefined() ? HYP_OK : HYP_BAD_PARAMETER;
-      break;
+    }
+    else
+    {
+      _hypViscousLayers = dynamic_cast<const StdMeshers_ViscousLayers*>( *h );
     }
   }
 
@@ -170,7 +186,20 @@ bool StdMeshers_Cartesian_3D::CheckHypothesis (SMESH_Mesh&          aMesh,
 
 namespace
 {
-  typedef int TGeomID; // IDs of sub-shapes
+  /*!
+   * \brief Temporary mesh to hold 
+   */
+  struct TmpMesh: public SMESH_Mesh
+  {
+    TmpMesh() {
+      _isShapeToMesh = (_id = 0);
+      _meshDS  = new SMESHDS_Mesh( _id, true );
+    }
+  };
+
+  typedef int                     TGeomID; // IDs of sub-shapes
+  typedef TopTools_ShapeMapHasher TShapeHasher; // non-oriented shape hasher
+  typedef std::array< int, 3 >    TIJK;
 
   const TGeomID theUndefID = 1e+9;
 
@@ -326,7 +355,7 @@ namespace
     mutable vector< TGeomID >    _faceIDs;
 
     B_IntersectPoint(): _node(NULL) {}
-    bool Add( const vector< TGeomID >& fIDs, const SMDS_MeshNode* n=0 ) const;
+    bool Add( const vector< TGeomID >& fIDs, const SMDS_MeshNode* n=NULL ) const;
     TGeomID HasCommonFace( const B_IntersectPoint * other, TGeomID avoidFace=-1 ) const;
     size_t GetCommonFaces( const B_IntersectPoint * other, TGeomID * commonFaces ) const;
     bool IsOnFace( TGeomID faceID ) const;
@@ -408,6 +437,11 @@ namespace
     {
       _curInd[0] = i; _curInd[1] = j; _curInd[2] = k;
     }
+    void SetLineIndex(size_t i)
+    {
+      _curInd[_iVar2] = i / _size[_iVar1];
+      _curInd[_iVar1] = i % _size[_iVar1];
+    }
     void operator++()
     {
       if ( ++_curInd[_iVar1] == _size[_iVar1] )
@@ -419,8 +453,10 @@ namespace
     size_t LineIndex01 () const { return _curInd[_iVar1] + (_curInd[_iVar2] + 1 )* _size[_iVar1]; }
     size_t LineIndex11 () const { return (_curInd[_iVar1] + 1 ) + (_curInd[_iVar2] + 1 )* _size[_iVar1]; }
     void SetIndexOnLine (size_t i)  { _curInd[ _iConst ] = i; }
+    bool IsValidIndexOnLine (size_t i) const { return  i < _size[ _iConst ]; }
     size_t NbLines() const { return _size[_iVar1] * _size[_iVar2]; }
   };
+  struct FaceGridIntersector;
   // --------------------------------------------------------------------------
   /*!
    * \brief Container of GridLine's
@@ -437,7 +473,9 @@ namespace
     // 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 SMDS_MeshNode* >    _nodes;          // mesh nodes at grid nodes
+    vector< const SMDS_MeshNode* >    _allBorderNodes; // mesh nodes between the bounding box and the geometry boundary
+
     vector< const F_IntersectPoint* > _gridIntP; // grid node intersection with geometry
     ObjectPool< E_IntersectPoint >    _edgeIntPool; // intersections with EDGEs
     ObjectPool< F_IntersectPoint >    _extIntPool; // intersections with extended INTERNAL FACEs
@@ -449,6 +487,8 @@ namespace
     bool                              _toConsiderInternalFaces;
     bool                              _toUseThresholdForInternalFaces;
     double                            _sizeThreshold;
+    bool                              _toUseQuanta;
+    double                            _quanta;
 
     SMESH_MesherHelper*               _helper;
 
@@ -460,11 +500,16 @@ namespace
     {
       return i + j*_coords[0].size() + k*_coords[0].size()*_coords[1].size();
     }
+    size_t NodeIndex( const TIJK& ijk ) const
+    {
+      return NodeIndex( ijk[0], ijk[1], ijk[2] );
+    }
     size_t NodeIndexDX() const { return 1; }
     size_t NodeIndexDY() const { return _coords[0].size(); }
     size_t NodeIndexDZ() const { return _coords[0].size() * _coords[1].size(); }
 
     LineIndexer GetLineIndexer(size_t iDir) const;
+    size_t GetLineDir( const GridLine* line, size_t & index ) const;
 
     E_IntersectPoint* Add( const E_IntersectPoint& ip )
     {
@@ -654,7 +699,8 @@ namespace
     // --------------------------------------------------------------------------------
     struct _Node //!< node either at a hexahedron corner or at intersection
     {
-      const SMDS_MeshNode*    _node; // mesh node at hexahedron corner
+      const SMDS_MeshNode*    _node;        // mesh node at hexahedron corner
+      const SMDS_MeshNode*    _boundaryCornerNode; // missing mesh node due to hex truncation on the boundary
       const B_IntersectPoint* _intPoint;
       const _Face*            _usedInFace;
       char                    _isInternalFlags;
@@ -663,6 +709,8 @@ namespace
         :_node(n), _intPoint(ip), _usedInFace(0), _isInternalFlags(0) {} 
       const SMDS_MeshNode*    Node() const
       { return ( _intPoint && _intPoint->_node ) ? _intPoint->_node : _node; }
+      const SMDS_MeshNode*    BoundaryNode() const
+      { return _node ? _node : _boundaryCornerNode; }
       const E_IntersectPoint* EdgeIntPnt() const
       { return static_cast< const E_IntersectPoint* >( _intPoint ); }
       const F_IntersectPoint* FaceIntPnt() const
@@ -705,6 +753,7 @@ namespace
       }
       void Add( const E_IntersectPoint* ip )
       {
+        const std::lock_guard<std::mutex> lock(_eMutex);
         // Possible cases before Add(ip):
         ///  1) _node != 0 --> _Node at hex corner ( _intPoint == 0 || _intPoint._node == 0 )
         ///  2) _node == 0 && _intPoint._node != 0  -->  link intersected by FACE
@@ -901,6 +950,7 @@ namespace
       TGeomID                 _solidID;
       double                  _size;
       const SMDS_MeshElement* _volume; // new volume
+      std::vector<const SMDS_MeshElement*> _brotherVolume; // produced due to poly split 
 
       vector< SMESH_Block::TShapeID > _names; // name of side a polygon originates from
 
@@ -977,10 +1027,7 @@ namespace
     int         _origNodeInd; // index of _hexNodes[0] node within the _grid
     size_t      _i,_j,_k;
     bool        _hasTooSmall;
-
-#ifdef _DEBUG_
     int         _cellID;
-#endif
 
   public:
     Hexahedron(Grid* grid);
@@ -1035,6 +1082,10 @@ namespace
     bool isInHole() const;
     bool hasStrangeEdge() const;
     bool checkPolyhedronSize( bool isCutByInternalFace, double & volSize ) const;
+    int checkPolyhedronValidity( _volumeDef* volDef, std::vector<std::vector<int>>& splitQuantities, 
+                                  std::vector<std::vector<const SMDS_MeshNode*>>& splitNodes );
+    const SMDS_MeshElement* addPolyhedronToMesh( _volumeDef* volDef,  SMESH_MesherHelper& helper, const std::vector<const SMDS_MeshNode*>& nodes, 
+                                                const std::vector<int>& quantities );
     bool addHexa ();
     bool addTetra();
     bool addPenta();
@@ -1243,7 +1294,7 @@ namespace
       if ( solids.size() == 2 )
       {
         if ( solids == solidsBef )
-          return theUndefID; //solids.contain( prevID ) ? solids.otherThan( prevID ) : theUndefID;
+          return solids.contain( prevID ) ? solids.otherThan( prevID ) : theUndefID; // bos #29212
       }
       return solids.oneCommon( solidsBef );
     }
@@ -1260,6 +1311,7 @@ namespace
   bool B_IntersectPoint::Add( const vector< TGeomID >& fIDs,
                               const SMDS_MeshNode*     n) const
   {
+    const std::lock_guard<std::mutex> lock(_bMutex);
     size_t prevNbF = _faceIDs.size();
 
     if ( _faceIDs.empty() )
@@ -1272,7 +1324,7 @@ namespace
         if ( it == _faceIDs.end() )
           _faceIDs.push_back( fIDs[i] );
       }
-    if ( !_node )
+    if ( !_node && n != NULL )
       _node = n;
 
     return prevNbF < _faceIDs.size();
@@ -1358,6 +1410,20 @@ namespace
                     s[indices[iDir*3]], s[indices[iDir*3+1]], s[indices[iDir*3+2]]);
     return li;
   }
+  //================================================================================
+  /*
+   * Return direction [0,1,2] of a GridLine
+   */
+  size_t Grid::GetLineDir( const GridLine* line, size_t & index ) const
+  {
+    for ( size_t iDir = 0; iDir < 3; ++iDir )
+      if ( &_lines[ iDir ][0] <= line && line <= &_lines[ iDir ].back() )
+      {
+        index = line - &_lines[ iDir ][0];
+        return iDir;
+      }
+    return -1;
+  }
   //=============================================================================
   /*
    * Creates GridLine's of the grid
@@ -1829,6 +1895,7 @@ namespace
     const size_t nbGridNodes = _coords[0].size() * _coords[1].size() * _coords[2].size();
     vector< TGeomID > shapeIDVec( nbGridNodes, theUndefID );
     _nodes.resize( nbGridNodes, 0 );
+    _allBorderNodes.resize( nbGridNodes, 0 );
     _gridIntP.resize( nbGridNodes, NULL );
 
     SMESHDS_Mesh* mesh = helper.GetMeshDS();
@@ -1879,10 +1946,11 @@ namespace
               if ( ++nodeCoord <  coordEnd )
                 nodeParam = *nodeCoord - *coord0;
               else
-                break;
+                break;                
             }
             if ( nodeCoord == coordEnd ) break;
           }
+          
           // create a mesh node on a GridLine at ip if it does not coincide with a grid node
           if ( nodeParam > ip->_paramOnLine + _tol )
           {
@@ -1943,6 +2011,14 @@ namespace
             SetOnShape( _nodes[ nodeIndex ], *_gridIntP[ nodeIndex ], & v );
             UpdateFacesOfVertex( *_gridIntP[ nodeIndex ], v );
           }
+          else if ( _toUseQuanta && !_allBorderNodes[ nodeIndex ] /*add all nodes outside the body. Used to reconstruct the hexahedrals when polys are not desired!*/)
+          {
+            gp_XYZ xyz = ( _coords[0][x] * _axes[0] +
+                           _coords[1][y] * _axes[1] +
+                           _coords[2][z] * _axes[2] );
+            _allBorderNodes[ nodeIndex ] = mesh->AddNode( xyz.X(), xyz.Y(), xyz.Z() );
+            mesh->SetNodeInVolume( _allBorderNodes[ nodeIndex ], shapeIDVec[ nodeIndex ]);
+          }
         }
 
 #ifdef _MY_DEBUG_
@@ -1959,11 +2035,11 @@ namespace
         {
           if ( intPnts.begin()->_transition != Trans_TANGENT &&
                intPnts.begin()->_transition != Trans_APEX )
-          throw SMESH_ComputeError (COMPERR_ALGO_FAILED,
-                                    SMESH_Comment("Wrong SOLE transition of GridLine (")
-                                    << li._curInd[li._iVar1] << ", " << li._curInd[li._iVar2]
-                                    << ") along " << li._nameConst
-                                    << ": " << trName[ intPnts.begin()->_transition] );
+            throw SMESH_ComputeError (COMPERR_ALGO_FAILED,
+                                      SMESH_Comment("Wrong SOLE transition of GridLine (")
+                                      << li._curInd[li._iVar1] << ", " << li._curInd[li._iVar2]
+                                      << ") along " << li._nameConst
+                                      << ": " << trName[ intPnts.begin()->_transition] );
         }
         else
         {
@@ -1978,11 +2054,13 @@ namespace
                                       SMESH_Comment("Wrong END transition of GridLine (")
                                       << li._curInd[li._iVar1] << ", " << li._curInd[li._iVar2]
                                       << ") along " << li._nameConst
-                                    << ": " << trName[ intPnts.rbegin()->_transition ]);
+                                      << ": " << trName[ intPnts.rbegin()->_transition ]);
         }
       }
     }
 #endif
+
+    return;
   }
 
   //=============================================================================
@@ -2429,11 +2507,9 @@ namespace
         tgtLink._link    = _hexLinks + ( srcLink._link - other._hexLinks );
       }
     }
-#ifdef _DEBUG_
-    _cellID = cellID;
-#else
-    (void)cellID; // unused in release mode
-#endif
+    
+    if (SALOME::VerbosityActivated())
+      _cellID = cellID;
   }
 
   //================================================================================
@@ -2615,11 +2691,16 @@ namespace
     {
       _hexNodes[iN]._isInternalFlags = 0;
 
+      // Grid  node 
       _hexNodes[iN]._node     = _grid->_nodes   [ _origNodeInd + _grid->_nodeShift[iN] ];
       _hexNodes[iN]._intPoint = _grid->_gridIntP[ _origNodeInd + _grid->_nodeShift[iN] ];
 
+      if ( _grid->_allBorderNodes[ _origNodeInd + _grid->_nodeShift[iN] ] ) 
+        _hexNodes[iN]._boundaryCornerNode = _grid->_allBorderNodes [ _origNodeInd + _grid->_nodeShift[iN] ];
+      
       if ( _hexNodes[iN]._node && !solid->Contains( _hexNodes[iN]._node->GetShapeID() ))
         _hexNodes[iN]._node = 0;
+
       if ( _hexNodes[iN]._intPoint && !solid->ContainsAny( _hexNodes[iN]._intPoint->_faceIDs ))
         _hexNodes[iN]._intPoint = 0;
 
@@ -2639,7 +2720,7 @@ namespace
     if ( _nbFaceIntNodes + _eIntPoints.size()                  > 0 &&
          _nbFaceIntNodes + _eIntPoints.size() + _nbCornerNodes > 3)
     {
-      _intNodes.reserve( 3 * _nbBndNodes + _nbFaceIntNodes + _eIntPoints.size() );
+      _intNodes.reserve( 3 * ( _nbBndNodes + _nbFaceIntNodes + _eIntPoints.size() ));
 
       // this method can be called in parallel, so use own helper
       SMESH_MesherHelper helper( *_grid->_helper->GetMesh() );
@@ -2711,7 +2792,7 @@ namespace
       // 1) add this->_eIntPoints to _Face::_eIntNodes
       // 2) fill _intNodes and _vIntNodes
       //
-      const double tol2 = _grid->_tol * _grid->_tol;
+      const double tol2 = _grid->_tol * _grid->_tol * 4;
       int facets[3], nbFacets, subEntity;
 
       for ( int iF = 0; iF < 6; ++iF )
@@ -2847,7 +2928,7 @@ namespace
       solid = _grid->GetSolid();
       if ( !_grid->_geometry.IsOneSolid() )
       {
-        TGeomID solidIDs[20];
+        TGeomID solidIDs[20] = { 0 };
         size_t nbSolids = getSolids( solidIDs );
         if ( nbSolids > 1 )
         {
@@ -3508,7 +3589,6 @@ namespace
     _volumeDefs._nodes.clear();
     _volumeDefs._quantities.clear();
     _volumeDefs._names.clear();
-
     // create a classic cell if possible
 
     int nbPolygons = 0;
@@ -3544,6 +3624,35 @@ namespace
 
     return !_volumeDefs._nodes.empty();
   }
+
+  template<typename Type>
+  void computeHexa(Type& hex)
+  {
+    if ( hex ) 
+      hex->computeElements();
+  }
+
+  // Implement parallel computation of Hexa with c++ thread implementation
+  template<typename Iterator, class Function>
+  void parallel_for(const Iterator& first, const Iterator& last, Function&& f, const int nthreads = 1)
+  {
+      const unsigned int group = ((last-first))/std::abs(nthreads);
+
+      std::vector<std::thread> threads;
+      threads.reserve(nthreads);
+      Iterator it = first;
+      for (; it < last-group; it += group) {
+          // to create a thread 
+          // Pass iterators by value and the function by reference!
+          auto lambda = [=,&f](){ std::for_each(it, std::min(it+group, last), f);};
+
+          // stack the threads 
+          threads.push_back( std::thread( lambda ) );
+      }
+
+      std::for_each(it, last, f); // last steps while we wait for other threads
+      std::for_each(threads.begin(), threads.end(), [](std::thread& x){x.join();});
+  }
   //================================================================================
   /*!
    * \brief Create elements in the mesh
@@ -3657,9 +3766,9 @@ namespace
 
     // 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
+    auto numOfThreads = std::thread::hardware_concurrency();
+    numOfThreads = (numOfThreads != 0) ? numOfThreads : 1;
+    parallel_for(intHexa.begin(), intHexa.end(), computeHexa<Hexahedron*>, numOfThreads ); 
 #else
     for ( size_t i = 0; i < intHexa.size(); ++i )
       if ( Hexahedron * hex = intHexa[ i ] )
@@ -4249,11 +4358,10 @@ namespace
         h->_eIntPoints.reserve(2);
         h->_eIntPoints.push_back( ip );
         added = true;
-#ifdef _DEBUG_
+
         // check if ip is really inside the hex
-        if ( h->isOutParam( ip->_uvw ))
+        if (SALOME::VerbosityActivated() && h->isOutParam( ip->_uvw ))
           throw SALOME_Exception("ip outside a hex");
-#endif
       }
     }
     return added;
@@ -4757,6 +4865,7 @@ namespace
   {
     F_IntersectPoint noIntPnt;
     const bool toCheckNodePos = _grid->IsToCheckNodePos();
+    const bool useQuanta      = _grid->_toUseQuanta;
 
     int nbAdded = 0;
     // add elements resulted from hexahedron intersection
@@ -4803,26 +4912,41 @@ namespace
         }
       } // loop to get nodes
 
-      const SMDS_MeshElement* v = 0;
+      const SMDS_MeshElement* v = 0;      
       if ( !volDef->_quantities.empty() )
-      {
-        v = helper.AddPolyhedralVolume( nodes, volDef->_quantities );
-        volDef->_size = SMDS_VolumeTool( v ).GetSize();
-        if ( volDef->_size < 0 ) // invalid polyhedron
+      {                      
+        if ( !useQuanta )
         {
-          if ( ! SMESH_MeshEditor( helper.GetMesh() ).Reorient( v ) || // try to fix
-               SMDS_VolumeTool( v ).GetSize() < 0 )
+          // split polyhedrons of with disjoint volumes
+          std::vector<std::vector<int>> splitQuantities;
+          std::vector<std::vector< const SMDS_MeshNode* > > splitNodes;
+          if ( checkPolyhedronValidity( volDef, splitQuantities, splitNodes ) == 1 )
+            v = addPolyhedronToMesh( volDef, helper, nodes, volDef->_quantities );
+          else
           {
-            helper.GetMeshDS()->RemoveFreeElement( v, /*sm=*/nullptr, /*fromGroups=*/false );
-            v = nullptr;
-            //_hasTooSmall = true;
-#ifdef _DEBUG_
-            std::cout << "Remove INVALID polyhedron, _cellID = " << _cellID
-                      << " ijk = ( " << _i << " " << _j << " " << _k << " ) "
-                      << " solid " << volDef->_solidID << std::endl;
-#endif
+            int counter = -1;
+            for (size_t id = 0; id < splitQuantities.size(); id++)
+            {
+              v = addPolyhedronToMesh( volDef, helper, splitNodes[ id ], splitQuantities[ id ] );
+              if ( id < splitQuantities.size()-1 )
+                volDef->_brotherVolume.push_back( v );
+              counter++;
+            }
+            nbAdded += counter;
           }
         }
+        else
+        {
+          const double quanta = _grid->_quanta;
+          double polyVol      = volDef->_size;
+          double hexaVolume   = _sideLength[0] * _sideLength[1] * _sideLength[2];          
+          if ( hexaVolume > 0.0 && polyVol/hexaVolume >= quanta /*set the volume if the relation is satisfied*/)
+            v = helper.AddVolume( _hexNodes[0].BoundaryNode(), _hexNodes[2].BoundaryNode(),
+                                  _hexNodes[3].BoundaryNode(), _hexNodes[1].BoundaryNode(),
+                                  _hexNodes[4].BoundaryNode(), _hexNodes[6].BoundaryNode(),
+                                  _hexNodes[7].BoundaryNode(), _hexNodes[5].BoundaryNode() );
+          
+        }
       }
       else
       {
@@ -4879,6 +5003,10 @@ namespace
       if ( volDef->_volume )
       {
         helper.GetMeshDS()->SetMeshElementOnShape( volDef->_volume, volDef->_solidID );
+        for (auto broVol : volDef->_brotherVolume )
+        {
+          helper.GetMeshDS()->SetMeshElementOnShape( broVol, volDef->_solidID );
+        }
       }
     }
 
@@ -4887,6 +5015,8 @@ namespace
   //================================================================================
   /*!
    * \brief Return true if the element is in a hole
+   * \remark consider a cell to be in a hole if all links in any direction
+   *          comes OUT of geometry
    */
   bool Hexahedron::isInHole() const
   {
@@ -5028,6 +5158,159 @@ namespace
 
     return volume > initVolume / _grid->_sizeThreshold;
   }
+
+  //================================================================================
+  /*!
+   * \brief Check that all faces in polyhedron are connected so a unique volume is defined.
+   *        We test that it is possible to go from any node to all nodes in the polyhedron.
+   *        The set of nodes that can be visit within then defines a unique element.
+   *        In case more than one polyhedron is detected. The function return the set of quantities and nodes defining separates elements.
+   *        Reference to issue #bos[38521][EDF] Generate polyhedron with separate volume.
+   */
+  int Hexahedron::checkPolyhedronValidity( _volumeDef* volDef, std::vector<std::vector<int>>& splitQuantities, 
+                                           std::vector<std::vector<const SMDS_MeshNode*>>& splitNodes )
+  {  
+    int mySet = 1;
+    std::map<int,int> numberOfSets; // define set id with the number of faces associated!
+    if ( !volDef->_quantities.empty() )
+    {
+      auto connectivity = volDef->_quantities;
+      int accum = 0;
+      std::vector<bool> allFaces( connectivity.size(), false );
+      std::set<int> elementSet;
+      allFaces[ 0 ] = true; // the first node below to the first face
+      size_t connectedFaces = 1;
+      // Start filling the set with the nodes of the first face
+      splitQuantities.push_back( { connectivity[ 0 ] } );
+      splitNodes.push_back( { volDef->_nodes[ 0 ].Node() } );
+      elementSet.insert( volDef->_nodes[ 0 ].Node()->GetID() );
+      for (int n = 1; n < connectivity[ 0 ]; n++)
+      {
+        elementSet.insert( volDef->_nodes[ n ].Node()->GetID() );
+        splitNodes.back().push_back( volDef->_nodes[ n ].Node() );
+      }
+      
+      numberOfSets.insert( std::pair<int,int>(mySet,1) );
+      while ( connectedFaces != allFaces.size() )
+      {
+        for (size_t innerId = 1; innerId < connectivity.size(); innerId++)
+        {
+          if ( innerId == 1 )
+            accum = connectivity[ 0 ];
+
+          if ( !allFaces[ innerId ] )
+          {
+            int faceCounter = 0;
+            for (int n = 0; n < connectivity[ innerId ]; n++)
+            {
+              int nodeId = volDef->_nodes[ accum + n ].Node()->GetID();
+              if ( elementSet.count( nodeId ) != 0 ) 
+                faceCounter++;
+            }
+            if ( faceCounter >= 2 ) // found coincidences nodes
+            {
+              for (int n = 0; n < connectivity[ innerId ]; n++)
+              {
+                int nodeId = volDef->_nodes[ accum + n ].Node()->GetID();
+                // insert new nodes so other faces can be identified as belowing to the element
+                splitNodes.back().push_back( volDef->_nodes[ accum + n ].Node() );
+                elementSet.insert( nodeId );
+              }
+              allFaces[ innerId ] = true;
+              splitQuantities.back().push_back( connectivity[ innerId ] );
+              numberOfSets[ mySet ]++;
+              connectedFaces++;
+              innerId = 0; // to restart searching!
+            }
+          }
+          accum += connectivity[ innerId ];
+        }
+
+        if ( connectedFaces != allFaces.size() )
+        {
+          // empty the set, and fill it with nodes of a unvisited face!
+          elementSet.clear();
+          accum = connectivity[ 0 ];
+          for (size_t faceId = 1; faceId < connectivity.size(); faceId++)
+          {
+            if ( !allFaces[ faceId ] )
+            {
+              splitNodes.push_back( { volDef->_nodes[ accum ].Node() } );
+              elementSet.insert( volDef->_nodes[ accum ].Node()->GetID() );
+              for (int n = 1; n < connectivity[ faceId ]; n++)
+              {
+                elementSet.insert( volDef->_nodes[ accum + n ].Node()->GetID() );
+                splitNodes.back().push_back( volDef->_nodes[ accum + n ].Node() );
+              }
+
+              splitQuantities.push_back( { connectivity[ faceId ] } );
+              allFaces[ faceId ] = true;
+              connectedFaces++;
+              break;
+            }
+            accum += connectivity[ faceId ];
+          }
+          mySet++;
+          numberOfSets.insert( std::pair<int,int>(mySet,1) );
+        }
+      }
+
+      if ( numberOfSets.size() > 1 )
+      {
+        bool allMoreThan2Faces = true;
+        for( auto k : numberOfSets )
+        {
+          if ( k.second <= 2 )
+            allMoreThan2Faces &= false;
+        }
+        
+        if ( allMoreThan2Faces )
+        {        
+          // The separate objects are suspect to be closed
+          return numberOfSets.size();        
+        }
+        else
+        {
+          // Have to index the last face nodes to the final set
+          // contrary case return as it were a valid polyhedron for backward compatibility
+          return 1;  
+        }
+      }
+    }
+    return numberOfSets.size();
+  }
+
+  
+  //================================================================================
+  /*!
+   * \brief add original or separated polyhedrons to the mesh
+   */
+  const SMDS_MeshElement* Hexahedron::addPolyhedronToMesh( _volumeDef* volDef,  SMESH_MesherHelper& helper, const std::vector<const SMDS_MeshNode*>& nodes, 
+                                                            const std::vector<int>& quantities )
+  {
+    const SMDS_MeshElement* v = helper.AddPolyhedralVolume( nodes, quantities );
+
+    volDef->_size = SMDS_VolumeTool( v ).GetSize();
+    if ( volDef->_size < 0 ) // invalid polyhedron
+    {
+      if ( ! SMESH_MeshEditor( helper.GetMesh() ).Reorient( v ) || // try to fix
+          SMDS_VolumeTool( v ).GetSize() < 0 )
+      {
+        helper.GetMeshDS()->RemoveFreeElement( v, /*sm=*/nullptr, /*fromGroups=*/false );
+        v = nullptr;
+        //_hasTooSmall = true;
+
+        if (SALOME::VerbosityActivated())
+        {
+          std::cout << "Remove INVALID polyhedron, _cellID = " << _cellID
+                    << " ijk = ( " << _i << " " << _j << " " << _k << " ) "
+                    << " solid " << volDef->_solidID << std::endl;
+        }
+      }
+    }
+    return v;
+  }
+
   //================================================================================
   /*!
    * \brief Tries to create a hexahedron
@@ -5253,14 +5536,14 @@ namespace
    */
   bool Hexahedron::debugDumpLink( Hexahedron::_Link* link )
   {
-#ifdef _DEBUG_
-    gp_Pnt p1 = link->_nodes[0]->Point(), p2 = link->_nodes[1]->Point();
-    cout << "BUG: not shared link. IKJ = ( "<< _i << " " << _j << " " << _k << " )" << endl
-         << "n1 (" << p1.X() << ", "<< p1.Y() << ", "<< p1.Z() << " )" << endl
-         << "n2 (" << p2.X() << ", "<< p2.Y() << ", "<< p2.Z() << " )" << endl;
-#else
-    (void)link; // unused in release mode
-#endif
+    if (SALOME::VerbosityActivated())
+    {
+      gp_Pnt p1 = link->_nodes[0]->Point(), p2 = link->_nodes[1]->Point();
+      cout << "BUG: not shared link. IKJ = ( "<< _i << " " << _j << " " << _k << " )" << endl
+          << "n1 (" << p1.X() << ", "<< p1.Y() << ", "<< p1.Z() << " )" << endl
+          << "n2 (" << p2.X() << ", "<< p2.Y() << ", "<< p2.Z() << " )" << endl;
+    }
+
     return false;
   }
   //================================================================================
@@ -5484,25 +5767,24 @@ namespace
     SMESH_MeshEditor::ElemFeatures face( SMDSAbs_Face );
     SMESHDS_Mesh* meshDS = helper.GetMeshDS();
 
+    bool isQuantaSet =  _grid->_toUseQuanta;    
     // check if there are internal or shared FACEs
     bool hasInternal = ( !_grid->_geometry.IsOneSolid() ||
-                         _grid->_geometry._soleSolid.HasInternalFaces() );
+                         _grid->_geometry._soleSolid.HasInternalFaces() );   
 
     for ( size_t iV = 0; iV < boundaryVolumes.size(); ++iV )
     {
       if ( !vTool.Set( boundaryVolumes[ iV ]))
         continue;
-
       TGeomID solidID = vTool.Element()->GetShapeID();
       Solid *   solid = _grid->GetOneOfSolids( solidID );
 
       // find boundary facets
-
       bndFacets.clear();
       for ( int iF = 0, n = vTool.NbFaces(); iF < n; iF++ )
       {
         const SMDS_MeshElement* otherVol;
-        bool isBoundary = vTool.IsFreeFace( iF, &otherVol );
+        bool isBoundary = isQuantaSet ? vTool.IsFreeFaceCheckAllNodes( iF, &otherVol ) : vTool.IsFreeFace( iF, &otherVol );
         if ( isBoundary )
         {
           bndFacets.push_back( iF );
@@ -5524,7 +5806,6 @@ namespace
         continue;
 
       // create faces
-
       if ( !vTool.IsPoly() )
         vTool.SetExternalNormal();
       for ( size_t i = 0; i < bndFacets.size(); ++i ) // loop on boundary facets
@@ -5552,7 +5833,7 @@ namespace
             if ( nn[ iN ]->GetPosition()->GetDim() == 2 )
               faceID = nn[ iN ]->GetShapeID();
           }
-          if ( faceID == 0 )
+          if ( faceID == 0 && !isQuantaSet /*if quanta is set boundary nodes at boundary does not coincide with any geometrical face */ )
             faceID = findCommonFace( face.myNodes, helper.GetMesh() );
 
           bool toCheckFace = faceID && (( !isBoundary ) ||
@@ -5569,7 +5850,7 @@ namespace
             // if ( !faceID && !isBoundary )
             //   continue;
           }
-          if ( !faceID && !isBoundary )
+          if ( !faceID && !isBoundary && !isQuantaSet )
             continue;
         }
 
@@ -5644,8 +5925,8 @@ namespace
           continue;
 
         gp_Dir direction(1,0,0);
-        const SMDS_MeshElement* anyFace = *facesToOrient.begin();
-        editor.Reorient2D( facesToOrient, direction, anyFace );
+        TIDSortedElemSet refFaces;
+        editor.Reorient2D( facesToOrient, direction, refFaces, /*allowNonManifold=*/true );
       }
     }
     return;
@@ -5768,6 +6049,14 @@ namespace
           for ( size_t iN = 0; iN < volDef->_nodes.size(); ++iN )
             volDef->_nodes[iN].Node()->setIsMarked( false );
       }
+      if ( volDef->_brotherVolume.size() > 0 )
+      {
+        for (auto _bro : volDef->_brotherVolume )
+        {
+          _bro->setIsMarked( true );
+          boundaryElems.push_back( _bro );
+        }        
+      }
     }
   }
 
@@ -6356,6 +6645,29 @@ namespace
 bool StdMeshers_Cartesian_3D::Compute(SMESH_Mesh &         theMesh,
                                       const TopoDS_Shape & theShape)
 {
+  if ( _hypViscousLayers )
+  {
+    const StdMeshers_ViscousLayers* hypViscousLayers = _hypViscousLayers;
+    _hypViscousLayers = nullptr;
+
+    StdMeshers_Cartesian_VL::ViscousBuilder builder( hypViscousLayers, theMesh, theShape );
+
+    std::string error;
+    TopoDS_Shape offsetShape = builder.MakeOffsetShape( theShape, theMesh, error );
+    if ( offsetShape.IsNull() )
+      throw SALOME_Exception( error );
+
+    SMESH_Mesh* offsetMesh = new TmpMesh(); 
+    offsetMesh->ShapeToMesh( offsetShape );
+    offsetMesh->GetSubMesh( offsetShape )->DependsOn();
+
+    this->_isComputeOffset = true;
+    if ( ! this->Compute( *offsetMesh, offsetShape ))
+      return false;
+
+    return builder.MakeViscousLayers( *offsetMesh, theMesh, theShape );
+  }
+
   // The algorithm generates the mesh in following steps:
 
   // 1) Intersection of grid lines with the geometry boundary.
@@ -6383,6 +6695,13 @@ bool StdMeshers_Cartesian_3D::Compute(SMESH_Mesh &         theMesh,
     grid._toConsiderInternalFaces        = _hyp->GetToConsiderInternalFaces();
     grid._toUseThresholdForInternalFaces = _hyp->GetToUseThresholdForInternalFaces();
     grid._sizeThreshold                  = _hyp->GetSizeThreshold();
+    grid._toUseQuanta                    = _hyp->GetToUseQuanta();
+    grid._quanta                         = _hyp->GetQuanta();
+    if ( _isComputeOffset )
+    {
+      grid._toAddEdges = true;
+      grid._toCreateFaces = true;
+    }
     grid.InitGeometry( theShape );
 
     vector< TopoDS_Shape > faceVec;
@@ -6516,6 +6835,15 @@ bool StdMeshers_Cartesian_3D::Compute(SMESH_Mesh &         theMesh,
           grid._nodes[i]->setIsMarked( true );
         }
 
+      for ( size_t i = 0; i < grid._allBorderNodes.size(); ++i )
+        if ( grid._allBorderNodes[i] &&
+             !grid._allBorderNodes[i]->IsNull() &&
+             grid._allBorderNodes[i]->NbInverseElements() == 0 )
+        {
+          nodesToRemove.push_back( grid._allBorderNodes[i] );
+          grid._allBorderNodes[i]->setIsMarked( true );
+        }
+
       // do remove
       for ( size_t i = 0; i < nodesToRemove.size(); ++i )
         meshDS->RemoveFreeNode( nodesToRemove[i], /*smD=*/0, /*fromGroups=*/false );