Salome HOME
52459: Viscous layers are not normal to the surface.
[modules/smesh.git] / src / StdMeshers / StdMeshers_ViscousLayers.cxx
index 6a6d871c43d65d25128c00a3f466aa903662e998..a2a44a23829f391426f09303ed72a60a62c9baef 100644 (file)
@@ -1,9 +1,9 @@
-// Copyright (C) 2007-2013  CEA/DEN, EDF R&D, OPEN CASCADE
+// Copyright (C) 2007-2014  CEA/DEN, EDF R&D, OPEN CASCADE
 //
 // This library is free software; you can redistribute it and/or
 // modify it under the terms of the GNU Lesser General Public
 // License as published by the Free Software Foundation; either
-// version 2.1 of the License.
+// version 2.1 of the License, or (at your option) any later version.
 //
 // This library is distributed in the hope that it will be useful,
 // but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -35,6 +35,7 @@
 #include "SMESH_ControlsDef.hxx"
 #include "SMESH_Gen.hxx"
 #include "SMESH_Group.hxx"
+#include "SMESH_HypoFilter.hxx"
 #include "SMESH_Mesh.hxx"
 #include "SMESH_MeshAlgos.hxx"
 #include "SMESH_MesherHelper.hxx"
@@ -44,6 +45,8 @@
 #include "StdMeshers_FaceSide.hxx"
 
 #include <BRepAdaptor_Curve2d.hxx>
+#include <BRepAdaptor_Surface.hxx>
+#include <BRepLProp_SLProps.hxx>
 #include <BRep_Tool.hxx>
 #include <Bnd_B2d.hxx>
 #include <Bnd_B3d.hxx>
 #include <Geom2d_Line.hxx>
 #include <Geom2d_TrimmedCurve.hxx>
 #include <GeomAdaptor_Curve.hxx>
+#include <GeomLib.hxx>
 #include <Geom_Circle.hxx>
 #include <Geom_Curve.hxx>
 #include <Geom_Line.hxx>
 #include <Geom_TrimmedCurve.hxx>
 #include <Precision.hxx>
 #include <Standard_ErrorHandler.hxx>
+#include <Standard_Failure.hxx>
 #include <TColStd_Array1OfReal.hxx>
 #include <TopExp.hxx>
 #include <TopExp_Explorer.hxx>
 #include <TopTools_IndexedMapOfShape.hxx>
+#include <TopTools_ListOfShape.hxx>
 #include <TopTools_MapOfShape.hxx>
 #include <TopoDS.hxx>
 #include <TopoDS_Edge.hxx>
@@ -88,6 +94,14 @@ namespace VISCOUS_3D
 
   enum UIndex { U_TGT = 1, U_SRC, LEN_TGT };
 
+  const double theMinSmoothCosin = 0.1;
+  const double theSmoothThickToElemSizeRatio = 0.3;
+
+  bool needSmoothing( double cosin, double tgtThick, double elemSize )
+  {
+    return cosin * tgtThick > theSmoothThickToElemSizeRatio * elemSize;
+  }
+
   /*!
    * \brief SMESH_ProxyMesh computed by _ViscousBuilder for a SOLID.
    * It is stored in a SMESH_subMesh of the SOLID as SMESH_subMeshEventListenerData
@@ -278,6 +292,7 @@ namespace VISCOUS_3D
         c = new _Curvature;
         c->_r = avgDist * avgDist / avgNormProj;
         c->_k = avgDist * avgDist / c->_r / c->_r;
+        //c->_k = avgNormProj / c->_r;
         c->_k *= ( c->_r < 0 ? 1/1.1 : 1.1 ); // not to be too restrictive
         c->_h2lenRatio = avgNormProj / ( avgDist + avgDist );
       }
@@ -286,29 +301,7 @@ namespace VISCOUS_3D
     double lenDelta(double len) const { return _k * ( _r + len ); }
     double lenDeltaByDist(double dist) const { return dist * _h2lenRatio; }
   };
-  struct _LayerEdge;
-  //--------------------------------------------------------------------------------
-  /*!
-   * Structure used to smooth a _LayerEdge (master) based on an EDGE.
-   */
-  struct _2NearEdges
-  {
-    // target nodes of 2 neighbour _LayerEdge's based on the same EDGE
-    const SMDS_MeshNode* _nodes[2];
-    // vectors from source nodes of 2 _LayerEdge's to the source node of master _LayerEdge
-    //gp_XYZ               _vec[2];
-    double               _wgt[2]; // weights of _nodes
-    _LayerEdge*          _edges[2];
-
-     // normal to plane passing through _LayerEdge._normal and tangent of EDGE
-    gp_XYZ*              _plnNorm;
-
-    _2NearEdges() { _nodes[0]=_nodes[1]=0; _plnNorm = 0; }
-    void reverse() {
-      std::swap( _nodes[0], _nodes[1] );
-      std::swap( _wgt[0], _wgt[1] );
-    }
-  };
+  struct _2NearEdges;
   //--------------------------------------------------------------------------------
   /*!
    * \brief Edge normal to surface, connecting a node on solid surface (_nodes[0])
@@ -320,7 +313,7 @@ namespace VISCOUS_3D
 
     gp_XYZ              _normal; // to solid surface
     vector<gp_XYZ>      _pos; // points computed during inflation
-    double              _len; // length achived with the last step
+    double              _len; // length achived with the last inflation step
     double              _cosin; // of angle (_normal ^ surface)
     double              _lenFactor; // to compute _len taking _cosin into account
 
@@ -342,7 +335,7 @@ namespace VISCOUS_3D
     void SetDataByNeighbors( const SMDS_MeshNode* n1,
                              const SMDS_MeshNode* n2,
                              SMESH_MesherHelper&  helper);
-    void InvalidateStep( int curStep );
+    void InvalidateStep( int curStep, bool restoreLength=false );
     bool Smooth(int& badNb);
     bool SmoothOnEdge(Handle(Geom_Surface)& surface,
                       const TopoDS_Face&    F,
@@ -358,9 +351,10 @@ namespace VISCOUS_3D
                        double&              dist,
                        const double&        epsilon) const;
     gp_Ax1 LastSegment(double& segLen) const;
-    bool IsOnEdge() const { return _2neibors; }
-    void Copy( _LayerEdge& other, SMESH_MesherHelper& helper );
-    void SetCosin( double cosin );
+    gp_XY  LastUV( const TopoDS_Face& F ) const;
+    bool   IsOnEdge() const { return _2neibors; }
+    gp_XYZ Copy( _LayerEdge& other, SMESH_MesherHelper& helper );
+    void   SetCosin( double cosin );
   };
   struct _LayerEdgeCmp
   {
@@ -370,6 +364,56 @@ namespace VISCOUS_3D
       return cmpNodes ? ( e1->_nodes[0]->GetID() < e2->_nodes[0]->GetID()) : ( e1 < e2 );
     }
   };
+  struct _LayerEdge;
+  //--------------------------------------------------------------------------------
+  /*!
+   * Structure used to smooth a _LayerEdge based on an EDGE.
+   */
+  struct _2NearEdges
+  {
+    double               _wgt  [2]; // weights of _nodes
+    _LayerEdge*          _edges[2];
+
+     // normal to plane passing through _LayerEdge._normal and tangent of EDGE
+    gp_XYZ*              _plnNorm;
+
+    _2NearEdges() { _edges[0]=_edges[1]=0; _plnNorm = 0; }
+    const SMDS_MeshNode* tgtNode(bool is2nd) {
+      return _edges[is2nd] ? _edges[is2nd]->_nodes.back() : 0;
+    }
+    const SMDS_MeshNode* srcNode(bool is2nd) {
+      return _edges[is2nd] ? _edges[is2nd]->_nodes[0] : 0;
+    }
+    void reverse() {
+      std::swap( _wgt  [0], _wgt  [1] );
+      std::swap( _edges[0], _edges[1] );
+    }
+  };
+  //--------------------------------------------------------------------------------
+  /*!
+   * \brief Convex FACE whose radius of curvature is less than the thickness of 
+   *        layers. It is used to detect distortion of prisms based on a convex
+   *        FACE and to update normals to enable further increasing the thickness
+   */
+  struct _ConvexFace
+  {
+    TopoDS_Face                     _face;
+
+    // edges whose _simplices are used to detect prism destorsion
+    vector< _LayerEdge* >           _simplexTestEdges;
+
+    // map a sub-shape to it's index in _SolidData::_endEdgeOnShape vector
+    map< TGeomID, int >             _subIdToEdgeEnd;
+
+    bool                            _normalsFixed;
+
+    bool GetCenterOfCurvature( _LayerEdge*         ledge,
+                               BRepLProp_SLProps&  surfProp,
+                               SMESH_MesherHelper& helper,
+                               gp_Pnt &            center ) const;
+    bool CheckPrisms() const;
+  };
+
   //--------------------------------------------------------------------------------
 
   typedef map< const SMDS_MeshNode*, _LayerEdge*, TIDCompare > TNode2Edge;
@@ -382,39 +426,51 @@ namespace VISCOUS_3D
   {
     TopoDS_Shape                    _solid;
     const StdMeshers_ViscousLayers* _hyp;
+    TopoDS_Shape                    _hypShape;
     _MeshOfSolid*                   _proxyMesh;
     set<TGeomID>                    _reversedFaceIds;
+    set<TGeomID>                    _ignoreFaceIds; // WOL FACEs and FACEs of other SOLIDS
 
-    double                          _stepSize, _stepSizeCoeff;
+    double                          _stepSize, _stepSizeCoeff, _geomSize;
     const SMDS_MeshNode*            _stepSizeNodes[2];
 
-    TNode2Edge                      _n2eMap;
+    TNode2Edge                      _n2eMap; // nodes and _LayerEdge's based on them
+
+    // map to find _n2eMap of another _SolidData by a shrink shape shared by two _SolidData's
+    map< TGeomID, TNode2Edge* >     _s2neMap;
     // edges of _n2eMap. We keep same data in two containers because
     // iteration over the map is 5 time longer than over the vector
     vector< _LayerEdge* >           _edges;
 
-    // key: an id of shape (EDGE or VERTEX) shared by a FACE with
-    // layers and a FACE w/o layers
+    // key:   an id of shape (EDGE or VERTEX) shared by a FACE with
+    //        layers and a FACE w/o layers
     // value: the shape (FACE or EDGE) to shrink mesh on.
-    // _LayerEdge's basing on nodes on key shape are inflated along the value shape
+    //       _LayerEdge's basing on nodes on key shape are inflated along the value shape
     map< TGeomID, TopoDS_Shape >     _shrinkShape2Shape;
 
-    // FACE's WOL, srink on which is forbiden due to algo on the adjacent SOLID
-    set< TGeomID >                   _noShrinkFaces;
+    // Convex FACEs whose radius of curvature is less than the thickness of layers
+    map< TGeomID, _ConvexFace >      _convexFaces;
+
+    // shapes (EDGEs and VERTEXes) srink from which is forbiden due to collisions with
+    // the adjacent SOLID
+    set< TGeomID >                   _noShrinkShapes;
 
-    // <EDGE to smooth on> to <it's curve>
+    // <EDGE to smooth on> to <it's curve> -- for analytic smooth
     map< TGeomID,Handle(Geom_Curve)> _edge2curve;
 
-    // end indices in _edges of _LayerEdge on one shape to smooth
-    vector< int >                    _endEdgeToSmooth;
+    // end indices in _edges of _LayerEdge on each shape, first go shapes to smooth
+    vector< int >                    _endEdgeOnShape;
+    int                              _nbShapesToSmooth;
 
     double                           _epsilon; // precision for SegTriaInter()
 
-    int                              _index; // for debug
+    TGeomID                          _index; // SOLID id, for debug
 
     _SolidData(const TopoDS_Shape&             s=TopoDS_Shape(),
                const StdMeshers_ViscousLayers* h=0,
-               _MeshOfSolid*                   m=0) :_solid(s), _hyp(h), _proxyMesh(m) {}
+               const TopoDS_Shape&             hs=TopoDS_Shape(),
+               _MeshOfSolid*                   m=0)
+      :_solid(s), _hyp(h), _hypShape(hs), _proxyMesh(m) {}
     ~_SolidData();
 
     Handle(Geom_Curve) CurveForSmooth( const TopoDS_Edge&    E,
@@ -423,6 +479,56 @@ namespace VISCOUS_3D
                                        Handle(Geom_Surface)& surface,
                                        const TopoDS_Face&    F,
                                        SMESH_MesherHelper&   helper);
+
+    void SortOnEdge( const TopoDS_Edge&  E,
+                     const int           iFrom,
+                     const int           iTo,
+                     SMESH_MesherHelper& helper);
+
+    _ConvexFace* GetConvexFace( const TGeomID faceID )
+    {
+      map< TGeomID, _ConvexFace >::iterator id2face = _convexFaces.find( faceID );
+      return id2face == _convexFaces.end() ? 0 : & id2face->second;
+    }
+    void GetEdgesOnShape( size_t end, int &  iBeg, int &  iEnd )
+    {
+      iBeg = end > 0 ? _endEdgeOnShape[ end-1 ] : 0;
+      iEnd = _endEdgeOnShape[ end ];
+    }
+
+    bool GetShapeEdges(const TGeomID shapeID, size_t& edgeEnd, int* iBeg=0, int* iEnd=0 ) const;
+
+    void AddShapesToSmooth( const set< TGeomID >& shapeIDs );
+  };
+  //--------------------------------------------------------------------------------
+  /*!
+   * \brief Container of centers of curvature at nodes on an EDGE bounding _ConvexFace
+   */
+  struct _CentralCurveOnEdge
+  {
+    bool                  _isDegenerated;
+    vector< gp_Pnt >      _curvaCenters;
+    vector< _LayerEdge* > _ledges;
+    vector< gp_XYZ >      _normals; // new normal for each of _ledges
+    vector< double >      _segLength2;
+
+    TopoDS_Edge           _edge;
+    TopoDS_Face           _adjFace;
+    bool                  _adjFaceToSmooth;
+
+    void Append( const gp_Pnt& center, _LayerEdge* ledge )
+    {
+      if ( _curvaCenters.size() > 0 )
+        _segLength2.push_back( center.SquareDistance( _curvaCenters.back() ));
+      _curvaCenters.push_back( center );
+      _ledges.push_back( ledge );
+      _normals.push_back( ledge->_normal );
+    }
+    bool FindNewNormal( const gp_Pnt& center, gp_XYZ& newNormal );
+    void SetShapes( const TopoDS_Edge&  edge,
+                    const _ConvexFace&  convFace,
+                    const _SolidData&   data,
+                    SMESH_MesherHelper& helper);
   };
   //--------------------------------------------------------------------------------
   /*!
@@ -431,10 +537,9 @@ namespace VISCOUS_3D
   struct _SmoothNode
   {
     const SMDS_MeshNode*         _node;
-    //vector<const SMDS_MeshNode*> _nodesAround;
     vector<_Simplex>             _simplices; // for quality check
 
-    enum SmoothType { LAPLACIAN, CENTROIDAL, ANGULAR };
+    enum SmoothType { LAPLACIAN, CENTROIDAL, ANGULAR, TFI };
 
     bool Smooth(int&                  badNb,
                 Handle(Geom_Surface)& surface,
@@ -472,6 +577,14 @@ namespace VISCOUS_3D
     bool makeLayer(_SolidData& data);
     bool setEdgeData(_LayerEdge& edge, const set<TGeomID>& subIds,
                      SMESH_MesherHelper& helper, _SolidData& data);
+    gp_XYZ getFaceNormal(const SMDS_MeshNode* n,
+                         const TopoDS_Face&   face,
+                         SMESH_MesherHelper&  helper,
+                         bool&                isOK,
+                         bool                 shiftInside=false);
+    gp_XYZ getWeigthedNormal( const SMDS_MeshNode*         n,
+                              std::pair< TGeomID, gp_XYZ > fId2Normal[],
+                              const int                    nbFaces );
     bool findNeiborsOnEdge(const _LayerEdge*     edge,
                            const SMDS_MeshNode*& n1,
                            const SMDS_MeshNode*& n2,
@@ -480,11 +593,15 @@ namespace VISCOUS_3D
                        const set<TGeomID>& ingnoreShapes,
                        const _SolidData*   dataToCheckOri = 0,
                        const bool          toSort = false);
+    void findSimplexTestEdges( _SolidData&                    data,
+                               vector< vector<_LayerEdge*> >& edgesByGeom);
+    void computeGeomSize( _SolidData& data );
     bool sortEdges( _SolidData&                    data,
                     vector< vector<_LayerEdge*> >& edgesByGeom);
+    void limitStepSizeByCurvature( _SolidData&  data );
     void limitStepSize( _SolidData&             data,
                         const SMDS_MeshElement* face,
-                        const double            cosin);
+                        const _LayerEdge*       maxCosinEdge );
     void limitStepSize( _SolidData& data, const double minSize);
     bool inflate(_SolidData& data);
     bool smoothAndCheck(_SolidData& data, const int nbSteps, double & distToIntersection);
@@ -494,17 +611,25 @@ namespace VISCOUS_3D
                              Handle(Geom_Surface)& surface,
                              const TopoDS_Face&    F,
                              SMESH_MesherHelper&   helper);
-    bool updateNormals( _SolidData& data, SMESH_MesherHelper& helper );
+    bool updateNormals( _SolidData& data, SMESH_MesherHelper& helper, int stepNb );
+    bool updateNormalsOfConvexFaces( _SolidData&         data,
+                                     SMESH_MesherHelper& helper,
+                                     int                 stepNb );
     bool refine(_SolidData& data);
     bool shrink();
     bool prepareEdgeToShrink( _LayerEdge& edge, const TopoDS_Face& F,
                               SMESH_MesherHelper& helper,
                               const SMESHDS_SubMesh* faceSubMesh );
-    void fixBadFaces(const TopoDS_Face& F, SMESH_MesherHelper& helper);
+    void restoreNoShrink( _LayerEdge& edge ) const;
+    void fixBadFaces(const TopoDS_Face&          F,
+                     SMESH_MesherHelper&         helper,
+                     const bool                  is2D,
+                     const int                   step,
+                     set<const SMDS_MeshNode*> * involvedNodes=NULL);
     bool addBoundaryElements();
 
     bool error( const string& text, int solidID=-1 );
-    SMESHDS_Mesh* getMeshDS() { return _mesh->GetMeshDS(); }
+    SMESHDS_Mesh* getMeshDS() const { return _mesh->GetMeshDS(); }
 
     // debug
     void makeGroupOfLE();
@@ -513,7 +638,6 @@ namespace VISCOUS_3D
     SMESH_ComputeErrorPtr _error;
 
     vector< _SolidData >  _sdVec;
-    set<TGeomID>          _ignoreShapeIds;
     int                   _tmpFaceID;
   };
   //--------------------------------------------------------------------------------
@@ -539,28 +663,29 @@ namespace VISCOUS_3D
    * We can't use SMDS_FaceOfNodes since it's impossible to set it's ID which is
    * needed because SMESH_ElementSearcher internaly uses set of elements sorted by ID
    */
-  struct TmpMeshFace : public SMDS_MeshElement
+  struct _TmpMeshFace : public SMDS_MeshElement
   {
     vector<const SMDS_MeshNode* > _nn;
-    TmpMeshFace( const vector<const SMDS_MeshNode*>& nodes, int id):
-      SMDS_MeshElement(id), _nn(nodes) {}
+    _TmpMeshFace( const vector<const SMDS_MeshNode*>& nodes, int id, int faceID=-1):
+      SMDS_MeshElement(id), _nn(nodes) { setShapeId(faceID); }
     virtual const SMDS_MeshNode* GetNode(const int ind) const { return _nn[ind]; }
     virtual SMDSAbs_ElementType  GetType() const              { return SMDSAbs_Face; }
     virtual vtkIdType GetVtkType() const                      { return -1; }
     virtual SMDSAbs_EntityType   GetEntityType() const        { return SMDSEntity_Last; }
-    virtual SMDSAbs_GeometryType GetGeomType() const          { return SMDSGeom_TRIANGLE; }
-virtual SMDS_ElemIteratorPtr elementsIterator(SMDSAbs_ElementType type) const
+    virtual SMDSAbs_GeometryType GetGeomType() const
+    { return _nn.size() == 3 ? SMDSGeom_TRIANGLE : SMDSGeom_QUADRANGLE; }
+    virtual SMDS_ElemIteratorPtr elementsIterator(SMDSAbs_ElementType) const
     { return SMDS_ElemIteratorPtr( new SMDS_NodeVectorElemIterator( _nn.begin(), _nn.end()));}
   };
   //--------------------------------------------------------------------------------
   /*!
    * \brief Class of temporary mesh face storing _LayerEdge it's based on
    */
-  struct TmpMeshFaceOnEdge : public TmpMeshFace
+  struct _TmpMeshFaceOnEdge : public _TmpMeshFace
   {
     _LayerEdge *_le1, *_le2;
-    TmpMeshFaceOnEdge( _LayerEdge* le1, _LayerEdge* le2, int ID ):
-      TmpMeshFace( vector<const SMDS_MeshNode*>(4), ID ), _le1(le1), _le2(le2)
+    _TmpMeshFaceOnEdge( _LayerEdge* le1, _LayerEdge* le2, int ID ):
+      _TmpMeshFace( vector<const SMDS_MeshNode*>(4), ID ), _le1(le1), _le2(le2)
     {
       _nn[0]=_le1->_nodes[0];
       _nn[1]=_le1->_nodes.back();
@@ -568,14 +693,55 @@ virtual SMDS_ElemIteratorPtr elementsIterator(SMDSAbs_ElementType type) const
       _nn[3]=_le2->_nodes[0];
     }
   };
+  //--------------------------------------------------------------------------------
+  /*!
+   * \brief Retriever of node coordinates either directly of from a surface by node UV.
+   * \warning Location of a surface is ignored
+   */
+  struct _NodeCoordHelper
+  {
+    SMESH_MesherHelper&        _helper;
+    const TopoDS_Face&         _face;
+    Handle(Geom_Surface)       _surface;
+    gp_XYZ (_NodeCoordHelper::* _fun)(const SMDS_MeshNode* n) const;
+
+    _NodeCoordHelper(const TopoDS_Face& F, SMESH_MesherHelper& helper, bool is2D)
+      : _helper( helper ), _face( F )
+    {
+      if ( is2D )
+      {
+        TopLoc_Location loc;
+        _surface = BRep_Tool::Surface( _face, loc );
+      }
+      if ( _surface.IsNull() )
+        _fun = & _NodeCoordHelper::direct;
+      else
+        _fun = & _NodeCoordHelper::byUV;
+    }
+    gp_XYZ operator()(const SMDS_MeshNode* n) const { return (this->*_fun)( n ); }
+
+  private:
+    gp_XYZ direct(const SMDS_MeshNode* n) const
+    {
+      return SMESH_TNodeXYZ( n );
+    }
+    gp_XYZ byUV  (const SMDS_MeshNode* n) const
+    {
+      gp_XY uv = _helper.GetNodeUV( _face, n );
+      return _surface->Value( uv.X(), uv.Y() ).XYZ();
+    }
+  };
+
 } // namespace VISCOUS_3D
 
+
+
 //================================================================================
 // StdMeshers_ViscousLayers hypothesis
 //
 StdMeshers_ViscousLayers::StdMeshers_ViscousLayers(int hypId, int studyId, SMESH_Gen* gen)
   :SMESH_Hypothesis(hypId, studyId, gen),
-   _isToIgnoreShapes(18), _nbLayers(1), _thickness(1), _stretchFactor(1)
+   _isToIgnoreShapes(1), _nbLayers(1), _thickness(1), _stretchFactor(1)
 {
   _name = StdMeshers_ViscousLayers::GetHypType();
   _param_algo_dim = -3; // auxiliary hyp used by 3D algos
@@ -644,7 +810,7 @@ std::ostream & StdMeshers_ViscousLayers::SaveTo(std::ostream & save)
        << " " << _thickness
        << " " << _stretchFactor
        << " " << _shapeIds.size();
-  for ( unsigned i = 0; i < _shapeIds.size(); ++i )
+  for ( size_t i = 0; i < _shapeIds.size(); ++i )
     save << " " << _shapeIds[i];
   save << " " << !_isToIgnoreShapes; // negate to keep the behavior in old studies.
   return save;
@@ -691,22 +857,32 @@ namespace
     gp_Vec dir;
     double f,l; gp_Pnt p;
     Handle(Geom_Curve) c = BRep_Tool::Curve( E, f, l );
+    if ( c.IsNull() ) return gp_XYZ( 1e100, 1e100, 1e100 );
     double u = helper.GetNodeU( E, atNode );
     c->D1( u, p, dir );
     return dir.XYZ();
   }
   //--------------------------------------------------------------------------------
+  gp_XYZ getFaceDir( const TopoDS_Face& F, const TopoDS_Vertex& fromV,
+                     const SMDS_MeshNode* node, SMESH_MesherHelper& helper, bool& ok,
+                     double* cosin=0);
+  //--------------------------------------------------------------------------------
   gp_XYZ getFaceDir( const TopoDS_Face& F, const TopoDS_Edge& fromE,
                      const SMDS_MeshNode* node, SMESH_MesherHelper& helper, bool& ok)
   {
+    double f,l;
+    Handle(Geom_Curve) c = BRep_Tool::Curve( fromE, f, l );
+    if ( c.IsNull() )
+    {
+      TopoDS_Vertex v = helper.IthVertex( 0, fromE );
+      return getFaceDir( F, v, node, helper, ok );
+    }
     gp_XY uv = helper.GetNodeUV( F, node, 0, &ok );
     Handle(Geom_Surface) surface = BRep_Tool::Surface( F );
     gp_Pnt p; gp_Vec du, dv, norm;
     surface->D1( uv.X(),uv.Y(), p, du,dv );
     norm = du ^ dv;
 
-    double f,l;
-    Handle(Geom_Curve) c = BRep_Tool::Curve( fromE, f, l );
     double u = helper.GetNodeU( fromE, node, 0, &ok );
     c->D1( u, p, du );
     TopAbs_Orientation o = helper.GetSubShapeOri( F.Oriented(TopAbs_FORWARD), fromE);
@@ -718,8 +894,8 @@ namespace
     if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX &&
          helper.IsClosedEdge( fromE ))
     {
-      if ( fabs(u-f) < fabs(u-l )) c->D1( l, p, dv );
-      else                         c->D1( f, p, dv );
+      if ( fabs(u-f) < fabs(u-l)) c->D1( l, p, dv );
+      else                        c->D1( f, p, dv );
       if ( o == TopAbs_REVERSED )
         dv.Reverse();
       gp_Vec dir2 = norm ^ dv;
@@ -730,47 +906,73 @@ namespace
   //--------------------------------------------------------------------------------
   gp_XYZ getFaceDir( const TopoDS_Face& F, const TopoDS_Vertex& fromV,
                      const SMDS_MeshNode* node, SMESH_MesherHelper& helper,
-                     bool& ok, double* cosin=0)
+                     bool& ok, double* cosin)
   {
+    TopoDS_Face faceFrw = F;
+    faceFrw.Orientation( TopAbs_FORWARD );
     double f,l; TopLoc_Location loc;
-    vector< TopoDS_Edge > edges; // sharing a vertex
-    PShapeIteratorPtr eIt = helper.GetAncestors( fromV, *helper.GetMesh(), TopAbs_EDGE);
-    while ( eIt->more())
-    {
-      const TopoDS_Edge* e = static_cast<const TopoDS_Edge*>( eIt->next() );
-      if ( helper.IsSubShape( *e, F ) && !BRep_Tool::Curve( *e, loc,f,l).IsNull() )
-        edges.push_back( *e );
-    }
-    gp_XYZ dir(0,0,0);
-    if ( !( ok = ( edges.size() > 0 ))) return dir;
-    // get average dir of edges going fromV
-    gp_XYZ edgeDir;
-    //if ( edges.size() > 1 )
-      for ( unsigned i = 0; i < edges.size(); ++i )
+    TopoDS_Edge edges[2]; // sharing a vertex
+    int nbEdges = 0;
+    {
+      TopoDS_Vertex VV[2];
+      TopExp_Explorer exp( faceFrw, TopAbs_EDGE );
+      for ( ; exp.More() && nbEdges < 2; exp.Next() )
       {
-        edgeDir = getEdgeDir( edges[i], fromV );
-        double size2 = edgeDir.SquareModulus();
-        if ( size2 > numeric_limits<double>::min() )
-          edgeDir /= sqrt( size2 );
-        else
-          ok = false;
-        dir += edgeDir;
+        const TopoDS_Edge& e = TopoDS::Edge( exp.Current() );
+        if ( SMESH_Algo::isDegenerated( e )) continue;
+        TopExp::Vertices( e, VV[0], VV[1], /*CumOri=*/true );
+        if ( VV[1].IsSame( fromV )) {
+          nbEdges += edges[ 0 ].IsNull();
+          edges[ 0 ] = e;
+        }
+        else if ( VV[0].IsSame( fromV )) {
+          nbEdges += edges[ 1 ].IsNull();
+          edges[ 1 ] = e;
+        }
+      }
+    }
+    gp_XYZ dir(0,0,0), edgeDir[2];
+    if ( nbEdges == 2 )
+    {
+      // get dirs of edges going fromV
+      ok = true;
+      for ( size_t i = 0; i < nbEdges && ok; ++i )
+      {
+        edgeDir[i] = getEdgeDir( edges[i], fromV );
+        double size2 = edgeDir[i].SquareModulus();
+        if (( ok = size2 > numeric_limits<double>::min() ))
+          edgeDir[i] /= sqrt( size2 );
+      }
+      if ( !ok ) return dir;
+
+      // get angle between the 2 edges
+      gp_Vec faceNormal;
+      double angle = helper.GetAngle( edges[0], edges[1], faceFrw, fromV, &faceNormal );
+      if ( Abs( angle ) < 5 * M_PI/180 )
+      {
+        dir = ( faceNormal.XYZ() ^ edgeDir[0].Reversed()) + ( faceNormal.XYZ() ^ edgeDir[1] );
+      }
+      else
+      {
+        dir = edgeDir[0] + edgeDir[1];
+        if ( angle < 0 )
+          dir.Reverse();
       }
-    gp_XYZ fromEdgeDir = getFaceDir( F, edges[0], node, helper, ok );
-    if ( edges.size() == 1 )
-      dir = fromEdgeDir;
-    else if ( dir.SquareModulus() < 0.1 ) // ~< 20 degrees
-      dir = fromEdgeDir + getFaceDir( F, edges[1], node, helper, ok );
-    else if ( dir * fromEdgeDir < 0 )
-      dir *= -1;
-    if ( ok )
-    {
-      //dir /= edges.size();
       if ( cosin ) {
-        double angle = gp_Vec( edgeDir ).Angle( dir );
-        *cosin = cos( angle );
+        double angle = gp_Vec( edgeDir[0] ).Angle( dir );
+        *cosin = Cos( angle );
       }
     }
+    else if ( nbEdges == 1 )
+    {
+      dir = getFaceDir( faceFrw, edges[ edges[0].IsNull() ], node, helper, ok );
+      if ( cosin ) *cosin = 1.;
+    }
+    else
+    {
+      ok = false;
+    }
+
     return dir;
   }
   //================================================================================
@@ -801,37 +1003,15 @@ namespace
         double u1 = intervals( i );
         double u2 = intervals( i+1 );
         curve.D2( 0.5*( u1+u2 ), p, drv1, drv2 );
-        double cross = drv2 * drv1; //drv2 ^ drv1;
+        double cross = drv2 ^ drv1;
         if ( E.Orientation() == TopAbs_REVERSED )
           cross = -cross;
-        isConvex = ( cross > -1e-9 );
+        isConvex = ( cross > 0.1 ); //-1e-9 );
       }
-      // check if concavity is strong enough to care about it
-      //const double maxAngle = 5 * Standard_PI180;
       if ( !isConvex )
       {
         //cout << "Concave FACE " << helper.GetMeshDS()->ShapeToIndex( F ) << endl;
         return true;
-        // map< double, const SMDS_MeshNode* > u2nodes;
-        // if ( !SMESH_Algo::GetSortedNodesOnEdge( helper.GetMeshDS(), E,
-        //                                         /*ignoreMedium=*/true, u2nodes))
-        //   continue;
-        // map< double, const SMDS_MeshNode* >::iterator u2n = u2nodes.begin();
-        // gp_Pnt2d uvPrev = helper.GetNodeUV( F, u2n->second );
-        // double    uPrev = u2n->first;
-        // for ( ++u2n; u2n != u2nodes.end(); ++u2n )
-        // {
-        //   gp_Pnt2d uv = helper.GetNodeUV( F, u2n->second );
-        //   gp_Vec2d segmentDir( uvPrev, uv );
-        //   curve.D1( uPrev, p, drv1 );
-        //   try {
-        //     if ( fabs( segmentDir.Angle( drv1 )) > maxAngle )
-        //       return true;
-        //   }
-        //   catch ( ... ) {}
-        //   uvPrev = uv;
-        //   uPrev = u2n->first;
-        // }
       }
     }
     // check angles at VERTEXes
@@ -849,17 +1029,74 @@ namespace
         while ( SMESH_Algo::isDegenerated( wires[iW]->Edge( iE2 )))
           iE2 = ( iE2 + 1 ) % nbEdges;
         double angle = helper.GetAngle( wires[iW]->Edge( iE1 ),
-                                        wires[iW]->Edge( iE2 ), F );
+                                        wires[iW]->Edge( iE2 ), F,
+                                        wires[iW]->FirstVertex( iE2 ));
         if ( angle < -5. * M_PI / 180. )
           return true;
       }
     }
     return false;
   }
+  //================================================================================
+  /*!
+   * \brief Computes mimimal distance of face in-FACE nodes from an EDGE
+   *  \param [in] face - the mesh face to treat
+   *  \param [in] nodeOnEdge - a node on the EDGE
+   *  \param [out] faceSize - the computed distance
+   *  \return bool - true if faceSize computed
+   */
+  //================================================================================
+
+  bool getDistFromEdge( const SMDS_MeshElement* face,
+                        const SMDS_MeshNode*    nodeOnEdge,
+                        double &                faceSize )
+  {
+    faceSize = Precision::Infinite();
+    bool done = false;
+
+    int nbN  = face->NbCornerNodes();
+    int iOnE = face->GetNodeIndex( nodeOnEdge );
+    int iNext[2] = { SMESH_MesherHelper::WrapIndex( iOnE+1, nbN ),
+                     SMESH_MesherHelper::WrapIndex( iOnE-1, nbN ) };
+    const SMDS_MeshNode* nNext[2] = { face->GetNode( iNext[0] ),
+                                      face->GetNode( iNext[1] ) };
+    gp_XYZ segVec, segEnd = SMESH_TNodeXYZ( nodeOnEdge ); // segment on EDGE
+    double segLen = -1.;
+    // look for two neighbor not in-FACE nodes of face
+    for ( int i = 0; i < 2; ++i )
+    {
+      if ( nNext[i]->GetPosition()->GetDim() != 2 &&
+           nNext[i]->GetID() < nodeOnEdge->GetID() )
+      {
+        // look for an in-FACE node
+        for ( int iN = 0; iN < nbN; ++iN )
+        {
+          if ( iN == iOnE || iN == iNext[i] )
+            continue;
+          SMESH_TNodeXYZ pInFace = face->GetNode( iN );
+          gp_XYZ v = pInFace - segEnd;
+          if ( segLen < 0 )
+          {
+            segVec = SMESH_TNodeXYZ( nNext[i] ) - segEnd;
+            segLen = segVec.Modulus();
+          }
+          double distToSeg = v.Crossed( segVec ).Modulus() / segLen;
+          faceSize = Min( faceSize, distToSeg );
+          done = true;
+        }
+        segLen = -1;
+      }
+    }
+    return done;
+  }
+
   //--------------------------------------------------------------------------------
   // DEBUG. Dump intermediate node positions into a python script
+  // HOWTO use: run python commands written in a console to see
+  //  construction steps of viscous layers
 #ifdef __myDEBUG
   ofstream* py;
+  int       theNbFunc;
   struct PyDump {
     PyDump() {
       const char* fname = "/tmp/viscous.py";
@@ -867,22 +1104,27 @@ namespace
       py = new ofstream(fname);
       *py << "import SMESH" << endl
           << "from salome.smesh import smeshBuilder" << endl
-          << "smesh = smeshBuilder.New(salome.myStudy)" << endl
+          << "smesh  = smeshBuilder.New(salome.myStudy)" << endl
           << "meshSO = smesh.GetCurrentStudy().FindObjectID('0:1:2:3')" << endl
-          << "mesh = smesh.Mesh( meshSO.GetObject() )"<<endl;
+          << "mesh   = smesh.Mesh( meshSO.GetObject() )"<<endl;
+      theNbFunc = 0;
     }
     void Finish() {
-      if (py)
-        *py << "mesh.MakeGroup('Viscous Prisms',VOLUME,FT_ElemGeomType,'=',Geom_PENTA)"<<endl;
+      if (py) {
+        *py << "mesh.GroupOnFilter(SMESH.VOLUME,'Viscous Prisms',"
+          "smesh.GetFilter(SMESH.VOLUME,SMESH.FT_ElemGeomType,'=',SMESH.Geom_PENTA))"<<endl;
+        *py << "mesh.GroupOnFilter(SMESH.VOLUME,'Neg Volumes',"
+          "smesh.GetFilter(SMESH.VOLUME,SMESH.FT_Volume3D,'<',0))"<<endl;
+      }
       delete py; py=0;
     }
-    ~PyDump() { Finish(); }
+    ~PyDump() { Finish(); cout << "NB FUNCTIONS: " << theNbFunc << endl; }
   };
 #define dumpFunction(f) { _dumpFunction(f, __LINE__);}
 #define dumpMove(n)     { _dumpMove(n, __LINE__);}
 #define dumpCmd(txt)    { _dumpCmd(txt, __LINE__);}
   void _dumpFunction(const string& fun, int ln)
-  { if (py) *py<< "def "<<fun<<"(): # "<< ln <<endl; cout<<fun<<"()"<<endl;}
+  { if (py) *py<< "def "<<fun<<"(): # "<< ln <<endl; cout<<fun<<"()"<<endl; ++theNbFunc; }
   void _dumpMove(const SMDS_MeshNode* n, int ln)
   { if (py) *py<< "  mesh.MoveNode( "<<n->GetID()<< ", "<< n->X()
                << ", "<<n->Y()<<", "<< n->Z()<< ")\t\t # "<< ln <<endl; }
@@ -894,6 +1136,7 @@ namespace
   { if (py) { *py<< "  mesh.ChangeElemNodes( " << f->GetID()<<", [";
       for ( int i=1; i < f->NbNodes(); ++i ) *py << f->GetNode(i-1)->GetID()<<", ";
       *py << f->GetNode( f->NbNodes()-1 )->GetID() << " ])"<< endl; }}
+#define debugMsg( txt ) { cout << txt << " (line: " << __LINE__ << ")" << endl; }
 #else
   struct PyDump { void Finish() {} };
 #define dumpFunction(f) f
@@ -901,6 +1144,7 @@ namespace
 #define dumpCmd(txt)
 #define dumpFunctionEnd()
 #define dumpChangeNodes(f)
+#define debugMsg( txt ) {}
 #endif
 }
 
@@ -926,19 +1170,36 @@ _ViscousBuilder::_ViscousBuilder()
 
 bool _ViscousBuilder::error(const string& text, int solidId )
 {
+  const string prefix = string("Viscous layers builder: ");
   _error->myName    = COMPERR_ALGO_FAILED;
-  _error->myComment = string("Viscous layers builder: ") + text;
+  _error->myComment = prefix + text;
   if ( _mesh )
   {
     SMESH_subMesh* sm = _mesh->GetSubMeshContaining( solidId );
     if ( !sm && !_sdVec.empty() )
-      sm = _mesh->GetSubMeshContaining( _sdVec[0]._index );
+      sm = _mesh->GetSubMeshContaining( solidId = _sdVec[0]._index );
     if ( sm && sm->GetSubShape().ShapeType() == TopAbs_SOLID )
     {
       SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
       if ( smError && smError->myAlgo )
         _error->myAlgo = smError->myAlgo;
       smError = _error;
+      sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
+    }
+    // set KO to all solids
+    for ( size_t i = 0; i < _sdVec.size(); ++i )
+    {
+      if ( _sdVec[i]._index == solidId )
+        continue;
+      sm = _mesh->GetSubMesh( _sdVec[i]._solid );
+      if ( !sm->IsEmpty() )
+        continue;
+      SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
+      if ( !smError || smError->IsOK() )
+      {
+        smError = SMESH_ComputeError::New( COMPERR_ALGO_FAILED, prefix + "failed");
+        sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
+      }
     }
   }
   makeGroupOfLE(); // debug
@@ -1027,7 +1288,7 @@ SMESH_ComputeErrorPtr _ViscousBuilder::Compute(SMESH_Mesh&         theMesh,
   if ( !findFacesWithLayers() )
     return _error;
 
-  for ( unsigned i = 0; i < _sdVec.size(); ++i )
+  for ( size_t i = 0; i < _sdVec.size(); ++i )
   {
     if ( ! makeLayer(_sdVec[i]) )
       return _error;
@@ -1066,6 +1327,7 @@ bool _ViscousBuilder::findSolidsWithLayers()
   _sdVec.reserve( allSolids.Extent());
 
   SMESH_Gen* gen = _mesh->GetGen();
+  SMESH_HypoFilter filter;
   for ( int i = 1; i <= allSolids.Extent(); ++i )
   {
     // find StdMeshers_ViscousLayers hyp assigned to the i-th solid
@@ -1080,10 +1342,14 @@ bool _ViscousBuilder::findSolidsWithLayers()
       viscHyp = dynamic_cast<const StdMeshers_ViscousLayers*>( *hyp );
     if ( viscHyp )
     {
+      TopoDS_Shape hypShape;
+      filter.Init( filter.Is( viscHyp ));
+      _mesh->GetHypothesis( allSolids(i), filter, true, &hypShape );
+
       _MeshOfSolid* proxyMesh = _ViscousListener::GetSolidMesh( _mesh,
                                                                 allSolids(i),
                                                                 /*toCreate=*/true);
-      _sdVec.push_back( _SolidData( allSolids(i), viscHyp, proxyMesh ));
+      _sdVec.push_back( _SolidData( allSolids(i), viscHyp, hypShape, proxyMesh ));
       _sdVec.back()._index = getMeshDS()->ShapeToIndex( allSolids(i));
     }
   }
@@ -1102,44 +1368,69 @@ bool _ViscousBuilder::findSolidsWithLayers()
 
 bool _ViscousBuilder::findFacesWithLayers()
 {
+  SMESH_MesherHelper helper( *_mesh );
+  TopExp_Explorer exp;
+  TopTools_IndexedMapOfShape solids;
+
   // collect all faces to ignore defined by hyp
-  vector<TopoDS_Shape> ignoreFaces;
-  for ( unsigned i = 0; i < _sdVec.size(); ++i )
+  for ( size_t i = 0; i < _sdVec.size(); ++i )
   {
+    solids.Add( _sdVec[i]._solid );
+
     vector<TGeomID> ids = _sdVec[i]._hyp->GetBndShapes();
-    for ( unsigned i = 0; i < ids.size(); ++i )
+    if ( _sdVec[i]._hyp->IsToIgnoreShapes() ) // FACEs to ignore are given
     {
-      const TopoDS_Shape& s = getMeshDS()->IndexToShape( ids[i] );
-      if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
+      for ( size_t ii = 0; ii < ids.size(); ++ii )
       {
-        _ignoreShapeIds.insert( ids[i] );
-        ignoreFaces.push_back( s );
+        const TopoDS_Shape& s = getMeshDS()->IndexToShape( ids[ii] );
+        if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
+          _sdVec[i]._ignoreFaceIds.insert( ids[ii] );
+      }
+    }
+    else // FACEs with layers are given
+    {
+      exp.Init( _sdVec[i]._solid, TopAbs_FACE );
+      for ( ; exp.More(); exp.Next() )
+      {
+        TGeomID faceInd = getMeshDS()->ShapeToIndex( exp.Current() );
+        if ( find( ids.begin(), ids.end(), faceInd ) == ids.end() )
+          _sdVec[i]._ignoreFaceIds.insert( faceInd );
       }
     }
-  }
 
-  // ignore internal faces
-  SMESH_MesherHelper helper( *_mesh );
-  TopExp_Explorer exp;
-  for ( unsigned i = 0; i < _sdVec.size(); ++i )
-  {
-    exp.Init( _sdVec[i]._solid.Oriented( TopAbs_FORWARD ), TopAbs_FACE );
-    for ( ; exp.More(); exp.Next() )
+    // ignore internal FACEs if inlets and outlets are specified
     {
-      TGeomID faceInd = getMeshDS()->ShapeToIndex( exp.Current() );
-      if ( helper.NbAncestors( exp.Current(), *_mesh, TopAbs_SOLID ) > 1 )
-      {     
-        _ignoreShapeIds.insert( faceInd );
-        ignoreFaces.push_back( exp.Current() );
-        if ( helper.IsReversedSubMesh( TopoDS::Face( exp.Current() )))
+      TopTools_IndexedDataMapOfShapeListOfShape solidsOfFace;
+      if ( _sdVec[i]._hyp->IsToIgnoreShapes() )
+        TopExp::MapShapesAndAncestors( _sdVec[i]._hypShape,
+                                       TopAbs_FACE, TopAbs_SOLID, solidsOfFace);
+
+      exp.Init( _sdVec[i]._solid.Oriented( TopAbs_FORWARD ), TopAbs_FACE );
+      for ( ; exp.More(); exp.Next() )
+      {
+        const TopoDS_Face& face = TopoDS::Face( exp.Current() );
+        if ( helper.NbAncestors( face, *_mesh, TopAbs_SOLID ) < 2 )
+          continue;
+
+        const TGeomID faceInd = getMeshDS()->ShapeToIndex( face );
+        if ( _sdVec[i]._hyp->IsToIgnoreShapes() )
+        {
+          int nbSolids = solidsOfFace.FindFromKey( face ).Extent();
+          if ( nbSolids > 1 )
+            _sdVec[i]._ignoreFaceIds.insert( faceInd );
+        }
+
+        if ( helper.IsReversedSubMesh( face ))
+        {
           _sdVec[i]._reversedFaceIds.insert( faceInd );
+        }
       }
     }
   }
 
   // Find faces to shrink mesh on (solution 2 in issue 0020832);
   TopTools_IndexedMapOfShape shapes;
-  for ( unsigned i = 0; i < _sdVec.size(); ++i )
+  for ( size_t i = 0; i < _sdVec.size(); ++i )
   {
     shapes.Clear();
     TopExp::MapShapes(_sdVec[i]._solid, TopAbs_EDGE, shapes);
@@ -1159,71 +1450,112 @@ bool _ViscousBuilder::findFacesWithLayers()
       // check presence of layers on them
       int ignore[2];
       for ( int j = 0; j < 2; ++j )
-        ignore[j] = _ignoreShapeIds.count ( getMeshDS()->ShapeToIndex( FF[j] ));
-      if ( ignore[0] == ignore[1] ) continue; // nothing interesting
+        ignore[j] = _sdVec[i]._ignoreFaceIds.count ( getMeshDS()->ShapeToIndex( FF[j] ));
+      if ( ignore[0] == ignore[1] )
+        continue; // nothing interesting
       TopoDS_Shape fWOL = FF[ ignore[0] ? 0 : 1 ];
+      // check presence of layers on fWOL within an adjacent SOLID
+      bool collision = false;
+      PShapeIteratorPtr sIt = helper.GetAncestors( fWOL, *_mesh, TopAbs_SOLID );
+      while ( const TopoDS_Shape* solid = sIt->next() )
+        if ( !solid->IsSame( _sdVec[i]._solid ))
+        {
+          int iSolid = solids.FindIndex( *solid );
+          int  iFace = getMeshDS()->ShapeToIndex( fWOL );
+          if ( iSolid > 0 && !_sdVec[ iSolid-1 ]._ignoreFaceIds.count( iFace ))
+          {
+            //_sdVec[i]._noShrinkShapes.insert( iFace );
+            //fWOL.Nullify();
+            collision = true;
+          }
+        }
       // add edge to maps
-      TGeomID edgeInd = getMeshDS()->ShapeToIndex( edge );
-      _sdVec[i]._shrinkShape2Shape.insert( make_pair( edgeInd, fWOL ));
+      if ( !fWOL.IsNull())
+      {
+        TGeomID edgeInd = getMeshDS()->ShapeToIndex( edge );
+        _sdVec[i]._shrinkShape2Shape.insert( make_pair( edgeInd, fWOL ));
+        if ( collision )
+        {
+          // _shrinkShape2Shape will be used to temporary inflate _LayerEdge's based
+          // on the edge but shrink won't be performed
+          _sdVec[i]._noShrinkShapes.insert( edgeInd );
+        }
+      }
     }
   }
   // Exclude from _shrinkShape2Shape FACE's that can't be shrinked since
   // the algo of the SOLID sharing the FACE does not support it
   set< string > notSupportAlgos; notSupportAlgos.insert("Hexa_3D");
-  for ( unsigned i = 0; i < _sdVec.size(); ++i )
+  for ( size_t i = 0; i < _sdVec.size(); ++i )
   {
-    TopTools_MapOfShape noShrinkVertices;
     map< TGeomID, TopoDS_Shape >::iterator e2f = _sdVec[i]._shrinkShape2Shape.begin();
     for ( ; e2f != _sdVec[i]._shrinkShape2Shape.end(); ++e2f )
     {
       const TopoDS_Shape& fWOL = e2f->second;
-      TGeomID           edgeID = e2f->first;
+      const TGeomID     edgeID = e2f->first;
       bool notShrinkFace = false;
       PShapeIteratorPtr soIt = helper.GetAncestors(fWOL, *_mesh, TopAbs_SOLID);
-      while ( soIt->more())
+      while ( soIt->more() )
       {
         const TopoDS_Shape* solid = soIt->next();
         if ( _sdVec[i]._solid.IsSame( *solid )) continue;
         SMESH_Algo* algo = _mesh->GetGen()->GetAlgo( *_mesh, *solid );
         if ( !algo || !notSupportAlgos.count( algo->GetName() )) continue;
         notShrinkFace = true;
-        for ( unsigned j = 0; j < _sdVec.size(); ++j )
+        size_t iSolid = 0;
+        for ( ; iSolid < _sdVec.size(); ++iSolid )
         {
-          if ( _sdVec[j]._solid.IsSame( *solid ) )
-            if ( _sdVec[j]._shrinkShape2Shape.count( edgeID ))
+          if ( _sdVec[iSolid]._solid.IsSame( *solid ) ) {
+            if ( _sdVec[iSolid]._shrinkShape2Shape.count( edgeID ))
               notShrinkFace = false;
+            break;
+          }
         }
-      }
-      if ( notShrinkFace )
-      {
-        _sdVec[i]._noShrinkFaces.insert( getMeshDS()->ShapeToIndex( fWOL ));
-        for ( TopExp_Explorer vExp( fWOL, TopAbs_VERTEX ); vExp.More(); vExp.Next() )
-          noShrinkVertices.Add( vExp.Current() );
-      }
-    }
-    // erase from _shrinkShape2Shape all srink EDGE's of a SOLID connected
-    // to the found not shrinked fWOL's
-    e2f = _sdVec[i]._shrinkShape2Shape.begin();
-    for ( ; e2f != _sdVec[i]._shrinkShape2Shape.end(); )
-    {
-      TGeomID edgeID = e2f->first;
-      TopoDS_Vertex VV[2];
-      TopExp::Vertices( TopoDS::Edge( getMeshDS()->IndexToShape( edgeID )),VV[0],VV[1]);
-      if ( noShrinkVertices.Contains( VV[0] ) || noShrinkVertices.Contains( VV[1] ))
-      {
-        _sdVec[i]._noShrinkFaces.insert( getMeshDS()->ShapeToIndex( e2f->second ));
-        _sdVec[i]._shrinkShape2Shape.erase( e2f++ );
-      }
-      else
-      {
-        e2f++;
-      }
-    }
-  }
-      
+        if ( notShrinkFace )
+        {
+          _sdVec[i]._noShrinkShapes.insert( edgeID );
+
+          // add VERTEXes of the edge in _noShrinkShapes
+          TopoDS_Shape edge = getMeshDS()->IndexToShape( edgeID );
+          for ( TopoDS_Iterator vIt( edge ); vIt.More(); vIt.Next() )
+            _sdVec[i]._noShrinkShapes.insert( getMeshDS()->ShapeToIndex( vIt.Value() ));
+
+          // check if there is a collision with to-shrink-from EDGEs in iSolid
+          if ( iSolid == _sdVec.size() )
+            continue; // no VL in the solid
+          shapes.Clear();
+          TopExp::MapShapes( fWOL, TopAbs_EDGE, shapes);
+          for ( int iE = 1; iE <= shapes.Extent(); ++iE )
+          {
+            const TopoDS_Edge& E = TopoDS::Edge( shapes( iE ));
+            const TGeomID    eID = getMeshDS()->ShapeToIndex( E );
+            if ( eID == edgeID ||
+                 !_sdVec[iSolid]._shrinkShape2Shape.count( eID ) ||
+                 _sdVec[i]._noShrinkShapes.count( eID ))
+              continue;
+            for ( int is1st = 0; is1st < 2; ++is1st )
+            {
+              TopoDS_Vertex V = helper.IthVertex( is1st, E );
+              if ( _sdVec[i]._noShrinkShapes.count( getMeshDS()->ShapeToIndex( V ) ))
+              {
+                // _sdVec[i]._noShrinkShapes.insert( eID );
+                // V = helper.IthVertex( !is1st, E );
+                // _sdVec[i]._noShrinkShapes.insert( getMeshDS()->ShapeToIndex( V ));
+                //iE = 0; // re-start the loop on EDGEs of fWOL
+                return error("No way to make a conformal mesh with "
+                             "the given set of faces with layers", _sdVec[i]._index);
+              }
+            }
+          }
+        }
+
+      } // while ( soIt->more() )
+    } // loop on _sdVec[i]._shrinkShape2Shape
+  } // loop on _sdVec to fill in _SolidData::_noShrinkShapes
+
   // Find the SHAPE along which to inflate _LayerEdge based on VERTEX
 
-  for ( unsigned i = 0; i < _sdVec.size(); ++i )
+  for ( size_t i = 0; i < _sdVec.size(); ++i )
   {
     shapes.Clear();
     TopExp::MapShapes(_sdVec[i]._solid, TopAbs_VERTEX, shapes);
@@ -1237,11 +1569,12 @@ bool _ViscousBuilder::findFacesWithLayers()
       while ( fIt->more())
       {
         const TopoDS_Shape* f = fIt->next();
-        const int         fID = getMeshDS()->ShapeToIndex( *f );
         if ( helper.IsSubShape( *f, _sdVec[i]._solid ) )
         {
           totalNbFaces++;
-          if ( _ignoreShapeIds.count ( fID ) && ! _sdVec[i]._noShrinkFaces.count( fID ))
+          const int fID = getMeshDS()->ShapeToIndex( *f );
+          if ( _sdVec[i]._ignoreFaceIds.count ( fID ) /*&&
+               !_sdVec[i]._noShrinkShapes.count( fID )*/)
             facesWOL.push_back( *f );
         }
       }
@@ -1251,48 +1584,61 @@ bool _ViscousBuilder::findFacesWithLayers()
       switch ( facesWOL.size() )
       {
       case 1:
+      {
+        helper.SetSubShape( facesWOL[0] );
+        if ( helper.IsRealSeam( vInd )) // inflate along a seam edge?
         {
-          helper.SetSubShape( facesWOL[0] );
-          if ( helper.IsRealSeam( vInd )) // inflate along a seam edge?
+          TopoDS_Shape seamEdge;
+          PShapeIteratorPtr eIt = helper.GetAncestors(vertex, *_mesh, TopAbs_EDGE);
+          while ( eIt->more() && seamEdge.IsNull() )
           {
-            TopoDS_Shape seamEdge;
-            PShapeIteratorPtr eIt = helper.GetAncestors(vertex, *_mesh, TopAbs_EDGE);
-            while ( eIt->more() && seamEdge.IsNull() )
-            {
-              const TopoDS_Shape* e = eIt->next();
-              if ( helper.IsRealSeam( *e ) )
-                seamEdge = *e;
-            }
-            if ( !seamEdge.IsNull() )
-            {
-              _sdVec[i]._shrinkShape2Shape.insert( make_pair( vInd, seamEdge ));
-              break;
-            }
+            const TopoDS_Shape* e = eIt->next();
+            if ( helper.IsRealSeam( *e ) )
+              seamEdge = *e;
+          }
+          if ( !seamEdge.IsNull() )
+          {
+            _sdVec[i]._shrinkShape2Shape.insert( make_pair( vInd, seamEdge ));
+            break;
           }
-          _sdVec[i]._shrinkShape2Shape.insert( make_pair( vInd, facesWOL[0] ));
-          break;
         }
+        _sdVec[i]._shrinkShape2Shape.insert( make_pair( vInd, facesWOL[0] ));
+        break;
+      }
       case 2:
+      {
+        // find an edge shared by 2 faces
+        PShapeIteratorPtr eIt = helper.GetAncestors(vertex, *_mesh, TopAbs_EDGE);
+        while ( eIt->more())
         {
-          // find an edge shared by 2 faces
-          PShapeIteratorPtr eIt = helper.GetAncestors(vertex, *_mesh, TopAbs_EDGE);
-          while ( eIt->more())
+          const TopoDS_Shape* e = eIt->next();
+          if ( helper.IsSubShape( *e, facesWOL[0]) &&
+               helper.IsSubShape( *e, facesWOL[1]))
           {
-            const TopoDS_Shape* e = eIt->next();
-            if ( helper.IsSubShape( *e, facesWOL[0]) &&
-                 helper.IsSubShape( *e, facesWOL[1]))
-            {
-              _sdVec[i]._shrinkShape2Shape.insert( make_pair( vInd, *e )); break;
-            }
+            _sdVec[i]._shrinkShape2Shape.insert( make_pair( vInd, *e )); break;
           }
-          break;
         }
+        break;
+      }
       default:
         return error("Not yet supported case", _sdVec[i]._index);
       }
     }
   }
 
+  // add FACEs of other SOLIDs to _ignoreFaceIds
+  for ( size_t i = 0; i < _sdVec.size(); ++i )
+  {
+    shapes.Clear();
+    TopExp::MapShapes(_sdVec[i]._solid, TopAbs_FACE, shapes);
+
+    for ( exp.Init( _mesh->GetShapeToMesh(), TopAbs_FACE ); exp.More(); exp.Next() )
+    {
+      if ( !shapes.Contains( exp.Current() ))
+        _sdVec[i]._ignoreFaceIds.insert( getMeshDS()->ShapeToIndex( exp.Current() ));
+    }
+  }
+
   return true;
 }
 
@@ -1306,34 +1652,32 @@ bool _ViscousBuilder::makeLayer(_SolidData& data)
 {
   // get all sub-shapes to make layers on
   set<TGeomID> subIds, faceIds;
-  subIds = data._noShrinkFaces;
+  subIds = data._noShrinkShapes;
   TopExp_Explorer exp( data._solid, TopAbs_FACE );
   for ( ; exp.More(); exp.Next() )
-    if ( ! _ignoreShapeIds.count( getMeshDS()->ShapeToIndex( exp.Current() )))
     {
       SMESH_subMesh* fSubM = _mesh->GetSubMesh( exp.Current() );
-      faceIds.insert( fSubM->GetId() );
-      SMESH_subMeshIteratorPtr subIt =
-        fSubM->getDependsOnIterator(/*includeSelf=*/true, /*complexShapeFirst=*/false);
+      if ( ! data._ignoreFaceIds.count( fSubM->GetId() ))
+        faceIds.insert( fSubM->GetId() );
+      SMESH_subMeshIteratorPtr subIt = fSubM->getDependsOnIterator(/*includeSelf=*/true);
       while ( subIt->more() )
         subIds.insert( subIt->next()->GetId() );
     }
 
   // make a map to find new nodes on sub-shapes shared with other SOLID
-  map< TGeomID, TNode2Edge* > s2neMap;
   map< TGeomID, TNode2Edge* >::iterator s2ne;
   map< TGeomID, TopoDS_Shape >::iterator s2s = data._shrinkShape2Shape.begin();
   for (; s2s != data._shrinkShape2Shape.end(); ++s2s )
   {
     TGeomID shapeInd = s2s->first;
-    for ( unsigned i = 0; i < _sdVec.size(); ++i )
+    for ( size_t i = 0; i < _sdVec.size(); ++i )
     {
       if ( _sdVec[i]._index == data._index ) continue;
       map< TGeomID, TopoDS_Shape >::iterator s2s2 = _sdVec[i]._shrinkShape2Shape.find( shapeInd );
       if ( s2s2 != _sdVec[i]._shrinkShape2Shape.end() &&
            *s2s == *s2s2 && !_sdVec[i]._n2eMap.empty() )
       {
-        s2neMap.insert( make_pair( shapeInd, &_sdVec[i]._n2eMap ));
+        data._s2neMap.insert( make_pair( shapeInd, &_sdVec[i]._n2eMap ));
         break;
       }
     }
@@ -1348,7 +1692,7 @@ bool _ViscousBuilder::makeLayer(_SolidData& data)
 
   SMESH_MesherHelper helper( *_mesh );
   helper.SetSubShape( data._solid );
-  helper.SetElementsOnShape(true);
+  helper.SetElementsOnShape( true );
 
   vector< const SMDS_MeshNode*> newNodes; // of a mesh face
   TNode2Edge::iterator n2e2;
@@ -1370,55 +1714,93 @@ bool _ViscousBuilder::makeLayer(_SolidData& data)
     while ( eIt->more() )
     {
       const SMDS_MeshElement* face = eIt->next();
+      double          faceMaxCosin = -1;
+      _LayerEdge*     maxCosinEdge = 0;
+      int             nbDegenNodes = 0;
+
       newNodes.resize( face->NbCornerNodes() );
-      double faceMaxCosin = -1;
-      for ( int i = 0 ; i < face->NbCornerNodes(); ++i )
+      for ( size_t i = 0 ; i < newNodes.size(); ++i )
       {
-        const SMDS_MeshNode* n = face->GetNode(i);
+        const SMDS_MeshNode* n = face->GetNode( i );
+        const int      shapeID = n->getshapeId();
+        const bool onDegenShap = helper.IsDegenShape( shapeID );
+        const bool onDegenEdge = ( onDegenShap && n->GetPosition()->GetDim() == 1 );
+        if ( onDegenShap )
+        {
+          if ( onDegenEdge )
+          {
+            // substitute n on a degenerated EDGE with a node on a corresponding VERTEX
+            const TopoDS_Shape& E = getMeshDS()->IndexToShape( shapeID );
+            TopoDS_Vertex       V = helper.IthVertex( 0, TopoDS::Edge( E ));
+            if ( const SMDS_MeshNode* vN = SMESH_Algo::VertexNode( V, getMeshDS() )) {
+              n = vN;
+              nbDegenNodes++;
+            }
+          }
+          else
+          {
+            nbDegenNodes++;
+          }
+        }
         TNode2Edge::iterator n2e = data._n2eMap.insert( make_pair( n, (_LayerEdge*)0 )).first;
         if ( !(*n2e).second )
         {
           // add a _LayerEdge
           _LayerEdge* edge = new _LayerEdge();
-          n2e->second = edge;
           edge->_nodes.push_back( n );
-          const int shapeID = n->getshapeId();
+          n2e->second = edge;
           edgesByGeom[ shapeID ].push_back( edge );
+          const bool noShrink = data._noShrinkShapes.count( shapeID );
+
+          SMESH_TNodeXYZ xyz( n );
 
           // set edge data or find already refined _LayerEdge and get data from it
-          if ( n->GetPosition()->GetTypeOfPosition() != SMDS_TOP_FACE &&
-               ( s2ne = s2neMap.find( shapeID )) != s2neMap.end() &&
-               ( n2e2 = (*s2ne).second->find( n )) != s2ne->second->end())
+          if (( !noShrink                                                     ) &&
+              ( n->GetPosition()->GetTypeOfPosition() != SMDS_TOP_FACE        ) &&
+              (( s2ne = data._s2neMap.find( shapeID )) != data._s2neMap.end() ) &&
+              (( n2e2 = (*s2ne).second->find( n )) != s2ne->second->end()     ))
           {
             _LayerEdge* foundEdge = (*n2e2).second;
-            edge->Copy( *foundEdge, helper );
-            // location of the last node is modified but we can restore
-            // it by node position on _sWOL stored by the node
+            gp_XYZ        lastPos = edge->Copy( *foundEdge, helper );
+            foundEdge->_pos.push_back( lastPos );
+            // location of the last node is modified and we restore it by foundEdge->_pos.back()
             const_cast< SMDS_MeshNode* >
-              ( edge->_nodes.back() )->setXYZ( n->X(), n->Y(), n->Z() );
+              ( edge->_nodes.back() )->setXYZ( xyz.X(), xyz.Y(), xyz.Z() );
           }
           else
           {
-            edge->_nodes.push_back( helper.AddNode( n->X(), n->Y(), n->Z() ));
+            if ( !noShrink )
+            {
+              edge->_nodes.push_back( helper.AddNode( xyz.X(), xyz.Y(), xyz.Z() ));
+            }
             if ( !setEdgeData( *edge, subIds, helper, data ))
               return false;
           }
           dumpMove(edge->_nodes.back());
-          if ( edge->_cosin > 0.01 )
+
+          if ( edge->_cosin > faceMaxCosin )
           {
-            if ( edge->_cosin > faceMaxCosin )
-              faceMaxCosin = edge->_cosin;
+            faceMaxCosin = edge->_cosin;
+            maxCosinEdge = edge;
           }
         }
         newNodes[ i ] = n2e->second->_nodes.back();
+
+        if ( onDegenEdge )
+          data._n2eMap.insert( make_pair( face->GetNode( i ), n2e->second ));
       }
+      if ( newNodes.size() - nbDegenNodes < 2 )
+        continue;
+
       // create a temporary face
-      const SMDS_MeshElement* newFace = new TmpMeshFace( newNodes, --_tmpFaceID );
+      const SMDS_MeshElement* newFace =
+        new _TmpMeshFace( newNodes, --_tmpFaceID, face->getshapeId() );
       proxySub->AddElement( newFace );
 
       // compute inflation step size by min size of element on a convex surface
-      if ( faceMaxCosin > 0.1 )
-        limitStepSize( data, face, faceMaxCosin );
+      if ( faceMaxCosin > theMinSmoothCosin )
+        limitStepSize( data, face, maxCosinEdge );
+
     } // loop on 2D elements on a FACE
   } // loop on FACEs of a SOLID
 
@@ -1427,35 +1809,70 @@ bool _ViscousBuilder::makeLayer(_SolidData& data)
     data._epsilon *= data._stepSize;
 
   // Put _LayerEdge's into the vector data._edges
-
   if ( !sortEdges( data, edgesByGeom ))
     return false;
 
-  // Set target nodes into _Simplex and _2NearEdges
+  // limit data._stepSize depending on surface curvature and fill data._convexFaces
+  limitStepSizeByCurvature( data ); // !!! it must be before node substitution in _Simplex
+
+  // Set target nodes into _Simplex and _LayerEdge's to _2NearEdges
   TNode2Edge::iterator n2e;
-  for ( unsigned i = 0; i < data._edges.size(); ++i )
+  const SMDS_MeshNode* nn[2];
+  for ( size_t i = 0; i < data._edges.size(); ++i )
   {
-    if ( data._edges[i]->IsOnEdge())
-      for ( int j = 0; j < 2; ++j )
+    _LayerEdge* edge = data._edges[i];
+    if ( edge->IsOnEdge() )
+    {
+      // get neighbor nodes
+      bool hasData = ( edge->_2neibors->_edges[0] );
+      if ( hasData ) // _LayerEdge is a copy of another one
       {
-        if ( data._edges[i]->_nodes.back()->NbInverseElements(SMDSAbs_Volume) > 0 )
-          break; // _LayerEdge is shared by two _SolidData's
-        const SMDS_MeshNode* & n = data._edges[i]->_2neibors->_nodes[j];
-        if (( n2e = data._n2eMap.find( n )) == data._n2eMap.end() )
-          return error("_LayerEdge not found by src node", data._index);
-        n = (*n2e).second->_nodes.back();
-        data._edges[i]->_2neibors->_edges[j] = n2e->second;
+        nn[0] = edge->_2neibors->srcNode(0);
+        nn[1] = edge->_2neibors->srcNode(1);
       }
-    else
-      for ( unsigned j = 0; j < data._edges[i]->_simplices.size(); ++j )
+      else if ( !findNeiborsOnEdge( edge, nn[0],nn[1], data ))
       {
-        _Simplex& s = data._edges[i]->_simplices[j];
-        s._nNext = data._n2eMap[ s._nNext ]->_nodes.back();
-        s._nPrev = data._n2eMap[ s._nPrev ]->_nodes.back();
+        return false;
       }
-  }
+      // set neighbor _LayerEdge's
+      for ( int j = 0; j < 2; ++j )
+      {
+        if (( n2e = data._n2eMap.find( nn[j] )) == data._n2eMap.end() )
+          return error("_LayerEdge not found by src node", data._index);
+        edge->_2neibors->_edges[j] = n2e->second;
+      }
+      if ( !hasData )
+        edge->SetDataByNeighbors( nn[0], nn[1], helper);
+    }
 
-  dumpFunctionEnd();
+    for ( size_t j = 0; j < edge->_simplices.size(); ++j )
+    {
+      _Simplex& s = edge->_simplices[j];
+      s._nNext = data._n2eMap[ s._nNext ]->_nodes.back();
+      s._nPrev = data._n2eMap[ s._nPrev ]->_nodes.back();
+    }
+
+    // For an _LayerEdge on a degenerated EDGE, copy some data from
+    // a corresponding _LayerEdge on a VERTEX
+    // (issue 52453, pb on a downloaded SampleCase2-Tet-netgen-mephisto.hdf)
+    if ( helper.IsDegenShape( edge->_nodes[0]->getshapeId() ))
+    {
+      // Generally we should not get here
+      const TopoDS_Shape& E = getMeshDS()->IndexToShape( edge->_nodes[0]->getshapeId() );
+      if ( E.ShapeType() != TopAbs_EDGE )
+        continue;
+      TopoDS_Vertex V = helper.IthVertex( 0, TopoDS::Edge( E ));
+      const SMDS_MeshNode* vN = SMESH_Algo::VertexNode( V, getMeshDS() );
+      if (( n2e = data._n2eMap.find( vN )) == data._n2eMap.end() )
+        continue;
+      const _LayerEdge* vEdge = n2e->second;
+      edge->_normal    = vEdge->_normal;
+      edge->_lenFactor = vEdge->_lenFactor;
+      edge->_cosin     = vEdge->_cosin;
+    }
+  }
+
+  dumpFunctionEnd();
   return true;
 }
 
@@ -1467,7 +1884,7 @@ bool _ViscousBuilder::makeLayer(_SolidData& data)
 
 void _ViscousBuilder::limitStepSize( _SolidData&             data,
                                      const SMDS_MeshElement* face,
-                                     const double            cosin)
+                                     const _LayerEdge*       maxCosinEdge )
 {
   int iN = 0;
   double minSize = 10 * data._stepSize;
@@ -1475,20 +1892,20 @@ void _ViscousBuilder::limitStepSize( _SolidData&             data,
   for ( int i = 0; i < nbNodes; ++i )
   {
     const SMDS_MeshNode* nextN = face->GetNode( SMESH_MesherHelper::WrapIndex( i+1, nbNodes ));
-    const SMDS_MeshNode* curN = face->GetNode( i );
+    const SMDS_MeshNode*  curN = face->GetNode( i );
     if ( nextN->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE ||
-         curN->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
+         curN-> GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
     {
-      double dist = SMESH_TNodeXYZ( face->GetNode(i)).Distance( nextN );
+      double dist = SMESH_TNodeXYZ( curN ).Distance( nextN );
       if ( dist < minSize )
         minSize = dist, iN = i;
     }
   }
-  double newStep = 0.8 * minSize / cosin;
+  double newStep = 0.8 * minSize / maxCosinEdge->_lenFactor;
   if ( newStep < data._stepSize )
   {
     data._stepSize = newStep;
-    data._stepSizeCoeff = 0.8 / cosin;
+    data._stepSizeCoeff = 0.8 / maxCosinEdge->_lenFactor;
     data._stepSizeNodes[0] = face->GetNode( iN );
     data._stepSizeNodes[1] = face->GetNode( SMESH_MesherHelper::WrapIndex( iN+1, nbNodes ));
   }
@@ -1500,7 +1917,7 @@ void _ViscousBuilder::limitStepSize( _SolidData&             data,
  */
 //================================================================================
 
-void _ViscousBuilder::limitStepSize( _SolidData& data, const double minSize)
+void _ViscousBuilder::limitStepSize( _SolidData& data, const double minSize )
 {
   if ( minSize < data._stepSize )
   {
@@ -1514,6 +1931,133 @@ void _ViscousBuilder::limitStepSize( _SolidData& data, const double minSize)
   }
 }
 
+//================================================================================
+/*!
+ * \brief Limit data._stepSize by evaluating curvature of shapes and fill data._convexFaces
+ */
+//================================================================================
+
+void _ViscousBuilder::limitStepSizeByCurvature( _SolidData& data )
+{
+  const int nbTestPnt = 5; // on a FACE sub-shape
+  const double minCurvature = 0.9 / data._hyp->GetTotalThickness();
+
+  BRepLProp_SLProps surfProp( 2, 1e-6 );
+  SMESH_MesherHelper helper( *_mesh );
+
+  data._convexFaces.clear();
+
+  TopExp_Explorer face( data._solid, TopAbs_FACE );
+  for ( ; face.More(); face.Next() )
+  {
+    const TopoDS_Face& F = TopoDS::Face( face.Current() );
+    SMESH_subMesh *   sm = _mesh->GetSubMesh( F );
+    const TGeomID faceID = sm->GetId();
+    if ( data._ignoreFaceIds.count( faceID )) continue;
+
+    BRepAdaptor_Surface surface( F, false );
+    surfProp.SetSurface( surface );
+
+    bool isTooCurved = false;
+    int iBeg, iEnd;
+
+    _ConvexFace cnvFace;
+    const double        oriFactor = ( F.Orientation() == TopAbs_REVERSED ? +1. : -1. );
+    SMESH_subMeshIteratorPtr smIt = sm->getDependsOnIterator(/*includeSelf=*/true);
+    while ( smIt->more() )
+    {
+      sm = smIt->next();
+      const TGeomID subID = sm->GetId();
+      // find _LayerEdge's of a sub-shape
+      size_t edgesEnd;
+      if ( data.GetShapeEdges( subID, edgesEnd, &iBeg, &iEnd ))
+        cnvFace._subIdToEdgeEnd.insert( make_pair( subID, edgesEnd ));
+      else
+        continue;
+      // check concavity and curvature and limit data._stepSize
+      int nbLEdges = iEnd - iBeg;
+      int iStep     = Max( 1, nbLEdges / nbTestPnt );
+      for ( ; iBeg < iEnd; iBeg += iStep )
+      {
+        gp_XY uv = helper.GetNodeUV( F, data._edges[ iBeg ]->_nodes[0] );
+        surfProp.SetParameters( uv.X(), uv.Y() );
+        if ( !surfProp.IsCurvatureDefined() )
+          continue;
+        if ( surfProp.MaxCurvature() * oriFactor > minCurvature )
+        {
+          limitStepSize( data, 0.9 / surfProp.MaxCurvature() * oriFactor );
+          isTooCurved = true;
+        }
+        if ( surfProp.MinCurvature() * oriFactor > minCurvature )
+        {
+          limitStepSize( data, 0.9 / surfProp.MinCurvature() * oriFactor );
+          isTooCurved = true;
+        }
+      }
+    } // loop on sub-shapes of the FACE
+
+    if ( !isTooCurved ) continue;
+
+    _ConvexFace & convFace =
+      data._convexFaces.insert( make_pair( faceID, cnvFace )).first->second;
+
+    convFace._face = F;
+    convFace._normalsFixed = false;
+
+    // Fill _ConvexFace::_simplexTestEdges. These _LayerEdge's are used to detect
+    // prism distortion.
+    map< TGeomID, int >::iterator id2end = convFace._subIdToEdgeEnd.find( faceID );
+    if ( id2end != convFace._subIdToEdgeEnd.end() )
+    {
+      // there are _LayerEdge's on the FACE it-self;
+      // select _LayerEdge's near EDGEs
+      data.GetEdgesOnShape( id2end->second, iBeg, iEnd );
+      for ( ; iBeg < iEnd; ++iBeg )
+      {
+        _LayerEdge* ledge = data._edges[ iBeg ];
+        for ( size_t j = 0; j < ledge->_simplices.size(); ++j )
+          if ( ledge->_simplices[j]._nNext->GetPosition()->GetDim() < 2 )
+          {
+            convFace._simplexTestEdges.push_back( ledge );
+            break;
+          }
+      }
+    }
+    else
+    {
+      // where there are no _LayerEdge's on a _ConvexFace,
+      // as e.g. on a fillet surface with no internal nodes - issue 22580,
+      // so that collision of viscous internal faces is not detected by check of
+      // intersection of _LayerEdge's with the viscous internal faces.
+
+      set< const SMDS_MeshNode* > usedNodes;
+
+      // look for _LayerEdge's with null _sWOL
+      map< TGeomID, int >::iterator id2end = convFace._subIdToEdgeEnd.begin();
+      for ( ; id2end != convFace._subIdToEdgeEnd.end(); ++id2end )
+      {
+        data.GetEdgesOnShape( id2end->second, iBeg, iEnd );
+        if ( iBeg >= iEnd || !data._edges[ iBeg ]->_sWOL.IsNull() )
+          continue;
+        for ( ; iBeg < iEnd; ++iBeg )
+        {
+          _LayerEdge* ledge = data._edges[ iBeg ];
+          const SMDS_MeshNode* srcNode = ledge->_nodes[0];
+          if ( !usedNodes.insert( srcNode ).second ) continue;
+
+          getSimplices( srcNode, ledge->_simplices, data._ignoreFaceIds, &data );
+          for ( size_t i = 0; i < ledge->_simplices.size(); ++i )
+          {
+            usedNodes.insert( ledge->_simplices[i]._nPrev );
+            usedNodes.insert( ledge->_simplices[i]._nNext );
+          }
+          convFace._simplexTestEdges.push_back( ledge );
+        }
+      }
+    }
+  } // loop on FACEs of data._solid
+}
+
 //================================================================================
 /*!
  * \brief Separate shapes (and _LayerEdge's on them) to smooth from the rest ones
@@ -1523,46 +2067,55 @@ void _ViscousBuilder::limitStepSize( _SolidData& data, const double minSize)
 bool _ViscousBuilder::sortEdges( _SolidData&                    data,
                                  vector< vector<_LayerEdge*> >& edgesByGeom)
 {
+  // define allowed thickness
+  computeGeomSize( data ); // compute data._geomSize
+  const double tgtThick = Min( 0.5 * data._geomSize, data._hyp->GetTotalThickness() );
+
   // Find shapes needing smoothing; such a shape has _LayerEdge._normal on it's
-  // boundry inclined at a sharp angle to the shape
+  // boundry inclined to the shape at a sharp angle
 
   list< TGeomID > shapesToSmooth;
   
   SMESH_MesherHelper helper( *_mesh );
   bool ok = true;
 
-  for ( unsigned iS = 0; iS < edgesByGeom.size(); ++iS )
+  for ( size_t iS = 0; iS < edgesByGeom.size(); ++iS )
   {
     vector<_LayerEdge*>& eS = edgesByGeom[iS];
     if ( eS.empty() ) continue;
-    TopoDS_Shape S = getMeshDS()->IndexToShape( iS );
+    const TopoDS_Shape& S = getMeshDS()->IndexToShape( iS );
     bool needSmooth = false;
     switch ( S.ShapeType() )
     {
     case TopAbs_EDGE: {
 
-      bool isShrinkEdge = !eS[0]->_sWOL.IsNull();
+      if ( SMESH_Algo::isDegenerated( TopoDS::Edge( S )))
+        break;
+      //bool isShrinkEdge = !eS[0]->_sWOL.IsNull();
       for ( TopoDS_Iterator vIt( S ); vIt.More() && !needSmooth; vIt.Next() )
       {
         TGeomID iV = getMeshDS()->ShapeToIndex( vIt.Value() );
         vector<_LayerEdge*>& eV = edgesByGeom[ iV ];
         if ( eV.empty() ) continue;
-        double cosin = eV[0]->_cosin;
-        bool badCosin =
-          ( !eV[0]->_sWOL.IsNull() && ( eV[0]->_sWOL.ShapeType() == TopAbs_EDGE || !isShrinkEdge));
-        if ( badCosin )
+        gp_Vec eDir = getEdgeDir( TopoDS::Edge( S ), TopoDS::Vertex( vIt.Value() ));
+        double angle = eDir.Angle( eV[0]->_normal );
+        double cosin = Cos( angle );
+        if ( cosin > theMinSmoothCosin )
         {
-          gp_Vec dir1, dir2;
-          if ( eV[0]->_sWOL.ShapeType() == TopAbs_EDGE )
-            dir1 = getEdgeDir( TopoDS::Edge( eV[0]->_sWOL ), TopoDS::Vertex( vIt.Value() ));
-          else
-            dir1 = getFaceDir( TopoDS::Face( eV[0]->_sWOL ), TopoDS::Vertex( vIt.Value() ),
-                               eV[0]->_nodes[0], helper, ok);
-          dir2 = getEdgeDir( TopoDS::Edge( S ), TopoDS::Vertex( vIt.Value() ));
-          double angle = dir1.Angle( dir2 );
-          cosin = cos( angle );
+          // compare tgtThick with the length of an end segment
+          SMDS_ElemIteratorPtr eIt = eV[0]->_nodes[0]->GetInverseElementIterator(SMDSAbs_Edge);
+          while ( eIt->more() )
+          {
+            const SMDS_MeshElement* endSeg = eIt->next();
+            if ( endSeg->getshapeId() == iS )
+            {
+              double segLen =
+                SMESH_TNodeXYZ( endSeg->GetNode(0) ).Distance( endSeg->GetNode(1 ));
+              needSmooth = needSmoothing( cosin, tgtThick, segLen );
+              break;
+            }
+          }
         }
-        needSmooth = ( cosin > 0.1 );
       }
       break;
     }
@@ -1573,25 +2126,38 @@ bool _ViscousBuilder::sortEdges( _SolidData&                    data,
         TGeomID iE = getMeshDS()->ShapeToIndex( eExp.Current() );
         vector<_LayerEdge*>& eE = edgesByGeom[ iE ];
         if ( eE.empty() ) continue;
-        if ( eE[0]->_sWOL.IsNull() )
-        {
-          for ( unsigned i = 0; i < eE.size() && !needSmooth; ++i )
-            needSmooth = ( eE[i]->_cosin > 0.1 );
-        }
-        else
+        // TopLoc_Location loc;
+        // Handle(Geom_Surface) surface = BRep_Tool::Surface( TopoDS::Face( S ), loc );
+        // bool isPlane = GeomLib_IsPlanarSurface( surface ).IsPlanar();
+        //if ( eE[0]->_sWOL.IsNull() )
         {
-          const TopoDS_Face& F1 = TopoDS::Face( S );
-          const TopoDS_Face& F2 = TopoDS::Face( eE[0]->_sWOL );
-          const TopoDS_Edge& E  = TopoDS::Edge( eExp.Current() );
-          for ( unsigned i = 0; i < eE.size() && !needSmooth; ++i )
-          {
-            gp_Vec dir1 = getFaceDir( F1, E, eE[i]->_nodes[0], helper, ok );
-            gp_Vec dir2 = getFaceDir( F2, E, eE[i]->_nodes[0], helper, ok );
-            double angle = dir1.Angle( dir2 );
-            double cosin = cos( angle );
-            needSmooth = ( cosin > 0.1 );
-          }
+          double faceSize;
+          for ( size_t i = 0; i < eE.size() && !needSmooth; ++i )
+            if ( eE[i]->_cosin > theMinSmoothCosin )
+            {
+              SMDS_ElemIteratorPtr fIt = eE[i]->_nodes[0]->GetInverseElementIterator(SMDSAbs_Face);
+              while ( fIt->more() && !needSmooth )
+              {
+                const SMDS_MeshElement* face = fIt->next();
+                if ( getDistFromEdge( face, eE[i]->_nodes[0], faceSize ))
+                  needSmooth = needSmoothing( eE[i]->_cosin, tgtThick, faceSize );
+              }
+            }
         }
+        // else
+        // {
+        //   const TopoDS_Face& F1 = TopoDS::Face( S );
+        //   const TopoDS_Face& F2 = TopoDS::Face( eE[0]->_sWOL );
+        //   const TopoDS_Edge& E  = TopoDS::Edge( eExp.Current() );
+        //   for ( size_t i = 0; i < eE.size() && !needSmooth; ++i )
+        //   {
+        //     gp_Vec dir1 = getFaceDir( F1, E, eE[i]->_nodes[0], helper, ok );
+        //     gp_Vec dir2 = getFaceDir( F2, E, eE[i]->_nodes[0], helper, ok );
+        //     double angle = dir1.Angle(  );
+        //     double cosin = cos( angle );
+        //     needSmooth = ( cosin > theMinSmoothCosin );
+        //   }
+        // }
       }
       break;
     }
@@ -1599,6 +2165,7 @@ bool _ViscousBuilder::sortEdges( _SolidData&                    data,
       continue;
     default:;
     }
+
     if ( needSmooth )
     {
       if ( S.ShapeType() == TopAbs_EDGE ) shapesToSmooth.push_front( iS );
@@ -1608,25 +2175,29 @@ bool _ViscousBuilder::sortEdges( _SolidData&                    data,
   } // loop on edgesByGeom
 
   data._edges.reserve( data._n2eMap.size() );
-  data._endEdgeToSmooth.clear();
+  data._endEdgeOnShape.clear();
 
   // first we put _LayerEdge's on shapes to smooth
+  data._nbShapesToSmooth = 0;
   list< TGeomID >::iterator gIt = shapesToSmooth.begin();
   for ( ; gIt != shapesToSmooth.end(); ++gIt )
   {
     vector<_LayerEdge*>& eVec = edgesByGeom[ *gIt ];
     if ( eVec.empty() ) continue;
     data._edges.insert( data._edges.end(), eVec.begin(), eVec.end() );
-    data._endEdgeToSmooth.push_back( data._edges.size() );
+    data._endEdgeOnShape.push_back( data._edges.size() );
+    data._nbShapesToSmooth++;
     eVec.clear();
   }
 
   // then the rest _LayerEdge's
-  for ( unsigned iS = 0; iS < edgesByGeom.size(); ++iS )
+  for ( size_t iS = 0; iS < edgesByGeom.size(); ++iS )
   {
     vector<_LayerEdge*>& eVec = edgesByGeom[iS];
+    if ( eVec.empty() ) continue;
     data._edges.insert( data._edges.end(), eVec.begin(), eVec.end() );
-    eVec.clear();
+    data._endEdgeOnShape.push_back( data._edges.size() );
+    //eVec.clear();
   }
 
   return ok;
@@ -1649,8 +2220,8 @@ bool _ViscousBuilder::setEdgeData(_LayerEdge&         edge,
   const SMDS_MeshNode* node = edge._nodes[0]; // source node
   SMDS_TypeOfPosition posType = node->GetPosition()->GetTypeOfPosition();
 
-  edge._len = 0;
-  edge._2neibors = 0;
+  edge._len       = 0;
+  edge._2neibors  = 0;
   edge._curvature = 0;
 
   // --------------------------
@@ -1661,18 +2232,16 @@ bool _ViscousBuilder::setEdgeData(_LayerEdge&         edge,
   edge._normal.SetCoord(0,0,0);
 
   int totalNbFaces = 0;
-  gp_Pnt p;
-  gp_Vec du, dv, geomNorm;
+  gp_Vec geomNorm;
   bool normOK = true;
 
-  TGeomID shapeInd = node->getshapeId();
+  const TGeomID shapeInd = node->getshapeId();
   map< TGeomID, TopoDS_Shape >::const_iterator s2s = data._shrinkShape2Shape.find( shapeInd );
-  bool onShrinkShape ( s2s != data._shrinkShape2Shape.end() );
-  TopoDS_Shape vertEdge;
+  const bool onShrinkShape ( s2s != data._shrinkShape2Shape.end() );
 
   if ( onShrinkShape ) // one of faces the node is on has no layers
   {
-    vertEdge = getMeshDS()->IndexToShape( s2s->first ); // vertex or edge
+    TopoDS_Shape vertEdge = getMeshDS()->IndexToShape( s2s->first ); // vertex or edge
     if ( s2s->second.ShapeType() == TopAbs_EDGE )
     {
       // inflate from VERTEX along EDGE
@@ -1708,32 +2277,50 @@ bool _ViscousBuilder::setEdgeData(_LayerEdge&         edge,
 
     set<TGeomID>::iterator id = faceIds.begin();
     TopoDS_Face F;
+    std::pair< TGeomID, gp_XYZ > id2Norm[20];
     for ( ; id != faceIds.end(); ++id )
     {
       const TopoDS_Shape& s = getMeshDS()->IndexToShape( *id );
       if ( s.IsNull() || s.ShapeType() != TopAbs_FACE || !subIds.count( *id ))
         continue;
-      totalNbFaces++;
-      //nbLayerFaces += subIds.count( *id );
       F = TopoDS::Face( s );
+      geomNorm = getFaceNormal( node, F, helper, normOK );
+      if ( !normOK ) continue;
 
-      gp_XY uv = helper.GetNodeUV( F, node, 0, &normOK );
-      Handle(Geom_Surface) surface = BRep_Tool::Surface( F );
-      surface->D1( uv.X(),uv.Y(), p, du,dv );
-      geomNorm = du ^ dv;
-      double size2 = geomNorm.SquareMagnitude();
-      if ( size2 > numeric_limits<double>::min() )
-        geomNorm /= sqrt( size2 );
-      else
-        normOK = false;
       if ( helper.GetSubShapeOri( data._solid, F ) != TopAbs_REVERSED )
         geomNorm.Reverse();
+      id2Norm[ totalNbFaces ].first  = *id;
+      id2Norm[ totalNbFaces ].second = geomNorm.XYZ();
+      totalNbFaces++;
       edge._normal += geomNorm.XYZ();
     }
     if ( totalNbFaces == 0 )
       return error(SMESH_Comment("Can't get normal to node ") << node->GetID(), data._index);
 
-    edge._normal /= totalNbFaces;
+    if ( normOK && edge._normal.Modulus() < 1e-3 && totalNbFaces > 1 )
+    {
+      // opposite normals, re-get normals at shifted positions (IPAL 52426)
+      edge._normal.SetCoord( 0,0,0 );
+      for ( int i = 0; i < totalNbFaces; ++i )
+      {
+        const TopoDS_Face& F = TopoDS::Face( getMeshDS()->IndexToShape( id2Norm[i].first ));
+        geomNorm = getFaceNormal( node, F, helper, normOK, /*shiftInside=*/true );
+        if ( helper.GetSubShapeOri( data._solid, F ) != TopAbs_REVERSED )
+          geomNorm.Reverse();
+        if ( normOK )
+          id2Norm[ i ].second = geomNorm.XYZ();
+        edge._normal += id2Norm[ i ].second;
+      }
+    }
+
+    if ( totalNbFaces < 3 )
+    {
+      //edge._normal /= totalNbFaces;
+    }
+    else
+    {
+      edge._normal = getWeigthedNormal( node, id2Norm, totalNbFaces );
+    }
 
     switch ( posType )
     {
@@ -1742,7 +2329,7 @@ bool _ViscousBuilder::setEdgeData(_LayerEdge&         edge,
 
     case SMDS_TOP_EDGE: {
       TopoDS_Edge E = TopoDS::Edge( helper.GetSubShapeByNode( node, getMeshDS()));
-      gp_Vec inFaceDir = getFaceDir( F, E, node, helper, normOK);
+      gp_Vec inFaceDir = getFaceDir( F, E, node, helper, normOK );
       double angle = inFaceDir.Angle( edge._normal ); // [0,PI]
       edge._cosin = cos( angle );
       //cout << "Cosin on EDGE " << edge._cosin << " node " << node->GetID() << endl;
@@ -1750,8 +2337,8 @@ bool _ViscousBuilder::setEdgeData(_LayerEdge&         edge,
     }
     case SMDS_TOP_VERTEX: {
       TopoDS_Vertex V = TopoDS::Vertex( helper.GetSubShapeByNode( node, getMeshDS()));
-      gp_XYZ inFaceDir = getFaceDir( F, V, node, helper, normOK);
-      double angle = gp_Vec( inFaceDir).Angle( edge._normal ); // [0,PI]
+      gp_Vec inFaceDir = getFaceDir( F, V, node, helper, normOK );
+      double angle = inFaceDir.Angle( edge._normal ); // [0,PI]
       edge._cosin = cos( angle );
       //cout << "Cosin on VERTEX " << edge._cosin << " node " << node->GetID() << endl;
       break;
@@ -1759,7 +2346,7 @@ bool _ViscousBuilder::setEdgeData(_LayerEdge&         edge,
     default:
       return error(SMESH_Comment("Invalid shape position of node ")<<node, data._index);
     }
-  }
+  } // case _sWOL.IsNull()
 
   double normSize = edge._normal.SquareModulus();
   if ( normSize < numeric_limits<double>::min() )
@@ -1783,14 +2370,16 @@ bool _ViscousBuilder::setEdgeData(_LayerEdge&         edge,
     if ( edge._sWOL.ShapeType() == TopAbs_EDGE )
     {
       double u = helper.GetNodeU( TopoDS::Edge( edge._sWOL ), node, 0, &normOK );
-      edge._pos.push_back( gp_XYZ( u, 0, 0));
-      getMeshDS()->SetNodeOnEdge( tgtNode, TopoDS::Edge( edge._sWOL ), u );
+      edge._pos.push_back( gp_XYZ( u, 0, 0 ));
+      if ( edge._nodes.size() > 1 )
+        getMeshDS()->SetNodeOnEdge( tgtNode, TopoDS::Edge( edge._sWOL ), u );
     }
     else // TopAbs_FACE
     {
       gp_XY uv = helper.GetNodeUV( TopoDS::Face( edge._sWOL ), node, 0, &normOK );
       edge._pos.push_back( gp_XYZ( uv.X(), uv.Y(), 0));
-      getMeshDS()->SetNodeOnFace( tgtNode, TopoDS::Face( edge._sWOL ), uv.X(), uv.Y() );
+      if ( edge._nodes.size() > 1 )
+        getMeshDS()->SetNodeOnFace( tgtNode, TopoDS::Face( edge._sWOL ), uv.X(), uv.Y() );
     }
   }
   else
@@ -1799,9 +2388,9 @@ bool _ViscousBuilder::setEdgeData(_LayerEdge&         edge,
 
     if ( posType == SMDS_TOP_FACE )
     {
-      getSimplices( node, edge._simplices, _ignoreShapeIds, &data );
+      getSimplices( node, edge._simplices, data._ignoreFaceIds, &data );
       double avgNormProj = 0, avgLen = 0;
-      for ( unsigned i = 0; i < edge._simplices.size(); ++i )
+      for ( size_t i = 0; i < edge._simplices.size(); ++i )
       {
         gp_XYZ vec = edge._pos.back() - SMESH_TNodeXYZ( edge._simplices[i]._nPrev );
         avgNormProj += edge._normal * vec;
@@ -1820,14 +2409,14 @@ bool _ViscousBuilder::setEdgeData(_LayerEdge&         edge,
   {
     edge._2neibors = new _2NearEdges;
     // target node instead of source ones will be set later
-    if ( ! findNeiborsOnEdge( &edge,
-                              edge._2neibors->_nodes[0],
-                              edge._2neibors->_nodes[1],
-                              data))
-      return false;
-    edge.SetDataByNeighbors( edge._2neibors->_nodes[0],
-                             edge._2neibors->_nodes[1],
-                             helper);
+    // if ( ! findNeiborsOnEdge( &edge,
+    //                           edge._2neibors->_nodes[0],
+    //                           edge._2neibors->_nodes[1],
+    //                           data))
+    //   return false;
+    // edge.SetDataByNeighbors( edge._2neibors->_nodes[0],
+    //                          edge._2neibors->_nodes[1],
+    //                          helper);
   }
 
   edge.SetCosin( edge._cosin ); // to update edge._lenFactor
@@ -1835,6 +2424,200 @@ bool _ViscousBuilder::setEdgeData(_LayerEdge&         edge,
   return true;
 }
 
+//================================================================================
+/*!
+ * \brief Return normal to a FACE at a node
+ *  \param [in] n - node
+ *  \param [in] face - FACE
+ *  \param [in] helper - helper
+ *  \param [out] isOK - true or false
+ *  \param [in] shiftInside - to find normal at a position shifted inside the face
+ *  \return gp_XYZ - normal
+ */
+//================================================================================
+
+gp_XYZ _ViscousBuilder::getFaceNormal(const SMDS_MeshNode* node,
+                                      const TopoDS_Face&   face,
+                                      SMESH_MesherHelper&  helper,
+                                      bool&                isOK,
+                                      bool                 shiftInside)
+{
+  gp_XY uv;
+  if ( shiftInside )
+  {
+    // get a shifted position
+    gp_Pnt p = SMESH_TNodeXYZ( node );
+    gp_XYZ shift( 0,0,0 );
+    TopoDS_Shape S = helper.GetSubShapeByNode( node, helper.GetMeshDS() );
+    switch ( S.ShapeType() ) {
+    case TopAbs_VERTEX:
+    {
+      shift = getFaceDir( face, TopoDS::Vertex( S ), node, helper, isOK );
+      break;
+    }
+    case TopAbs_EDGE:
+    {
+      shift = getFaceDir( face, TopoDS::Edge( S ), node, helper, isOK );
+      break;
+    }
+    default:
+      isOK = false;
+    }
+    if ( isOK )
+      shift.Normalize();
+    p.Translate( shift * 1e-5 );
+
+    TopLoc_Location loc;
+    GeomAPI_ProjectPointOnSurf& projector = helper.GetProjector( face, loc, 1e-7 );
+
+    if ( !loc.IsIdentity() ) p.Transform( loc.Transformation().Inverted() );
+    
+    projector.Perform( p );
+    if ( !projector.IsDone() || projector.NbPoints() < 1 )
+    {
+      isOK = false;
+      return p.XYZ();
+    }
+    Quantity_Parameter U,V;
+    projector.LowerDistanceParameters(U,V);
+    uv.SetCoord( U,V );
+  }
+  else
+  {
+    uv = helper.GetNodeUV( face, node, 0, &isOK );
+  }
+
+  gp_Dir normal;
+  isOK = false;
+
+  Handle(Geom_Surface) surface = BRep_Tool::Surface( face );
+  int pointKind = GeomLib::NormEstim( surface, uv, 1e-5, normal );
+  enum { REGULAR = 0, QUASYSINGULAR, CONICAL, IMPOSSIBLE };
+
+  if ( pointKind == IMPOSSIBLE &&
+       node->GetPosition()->GetDim() == 2 ) // node inside the FACE
+  {
+    // probably NormEstim() failed due to a too high tolerance
+    pointKind = GeomLib::NormEstim( surface, uv, 1e-20, normal );
+    isOK = ( pointKind < IMPOSSIBLE );
+  }
+  if ( pointKind < IMPOSSIBLE )
+  {
+    if ( pointKind != REGULAR &&
+         !shiftInside &&
+         node->GetPosition()->GetDim() < 2 ) // FACE boundary
+    {
+      gp_XYZ normShift = getFaceNormal( node, face, helper, isOK, /*shiftInside=*/true );
+      if ( normShift * normal.XYZ() < 0. )
+        normal = normShift;
+    }
+    isOK = true;
+  }
+
+  if ( !isOK ) // hard singularity, to call with shiftInside=true ?
+  {
+    const TGeomID faceID = helper.GetMeshDS()->ShapeToIndex( face );
+
+    SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator(SMDSAbs_Face);
+    while ( fIt->more() )
+    {
+      const SMDS_MeshElement* f = fIt->next();
+      if ( f->getshapeId() == faceID )
+      {
+        isOK = SMESH_MeshAlgos::FaceNormal( f, (gp_XYZ&) normal.XYZ(), /*normalized=*/true );
+        if ( isOK )
+        {
+          TopoDS_Face ff = face;
+          ff.Orientation( TopAbs_FORWARD );
+          if ( helper.IsReversedSubMesh( ff ))
+            normal.Reverse();
+          break;
+        }
+      }
+    }
+  }
+  return normal.XYZ();
+}
+
+//================================================================================
+/*!
+ * \brief Return a normal at a node weighted with angles taken by FACEs
+ *  \param [in] n - the node
+ *  \param [in] fId2Normal - FACE ids and normals
+ *  \param [in] nbFaces - nb of FACEs meeting at the node
+ *  \return gp_XYZ - computed normal
+ */
+//================================================================================
+
+gp_XYZ _ViscousBuilder::getWeigthedNormal( const SMDS_MeshNode*         n,
+                                           std::pair< TGeomID, gp_XYZ > fId2Normal[],
+                                           const int                    nbFaces )
+{
+  gp_XYZ resNorm(0,0,0);
+  TopoDS_Shape V = SMESH_MesherHelper::GetSubShapeByNode( n, getMeshDS() );
+  if ( V.ShapeType() != TopAbs_VERTEX )
+  {
+    for ( int i = 0; i < nbFaces; ++i )
+      resNorm += fId2Normal[i].second / nbFaces ;
+    return resNorm;
+  }
+
+  double angles[30];
+  for ( int i = 0; i < nbFaces; ++i )
+  {
+    const TopoDS_Face& F = TopoDS::Face( getMeshDS()->IndexToShape( fId2Normal[i].first ));
+
+    // look for two EDGEs shared by F and other FACEs within fId2Normal
+    TopoDS_Edge ee[2];
+    int nbE = 0;
+    PShapeIteratorPtr eIt = SMESH_MesherHelper::GetAncestors( V, *_mesh, TopAbs_EDGE );
+    while ( const TopoDS_Shape* E = eIt->next() )
+    {
+      if ( !SMESH_MesherHelper::IsSubShape( *E, F ))
+        continue;
+      bool isSharedEdge = false;
+      for ( int j = 0; j < nbFaces && !isSharedEdge; ++j )
+      {
+        if ( i == j ) continue;
+        const TopoDS_Shape& otherF = getMeshDS()->IndexToShape( fId2Normal[j].first );
+        isSharedEdge = SMESH_MesherHelper::IsSubShape( *E, otherF );
+      }
+      if ( !isSharedEdge )
+        continue;
+      ee[ nbE ] = TopoDS::Edge( *E );
+      ee[ nbE ].Orientation( SMESH_MesherHelper::GetSubShapeOri( F, *E ));
+      if ( ++nbE == 2 )
+        break;
+    }
+
+    // get an angle between the two EDGEs
+    angles[i] = 0;
+    if ( nbE < 1 ) continue;
+    if ( nbE == 1 )
+    {
+      ee[ 1 ] == ee[ 0 ];
+    }
+    else
+    {
+      if ( !V.IsSame( SMESH_MesherHelper::IthVertex( 0, ee[ 1 ] )))
+        std::swap( ee[0], ee[1] );
+    }
+    angles[i] = SMESH_MesherHelper::GetAngle( ee[0], ee[1], F, TopoDS::Vertex( V ));
+  }
+
+  // compute a weighted normal
+  double sumAngle = 0;
+  for ( int i = 0; i < nbFaces; ++i )
+  {
+    angles[i] = ( angles[i] > 2*M_PI )  ?  0  :  M_PI - angles[i];
+    sumAngle += angles[i];
+  }
+  for ( int i = 0; i < nbFaces; ++i )
+    resNorm += angles[i] / sumAngle * fId2Normal[i].second;
+
+  return resNorm;
+}
+
 //================================================================================
 /*!
  * \brief Find 2 neigbor nodes of a node on EDGE
@@ -1847,11 +2630,10 @@ bool _ViscousBuilder::findNeiborsOnEdge(const _LayerEdge*     edge,
                                         _SolidData&           data)
 {
   const SMDS_MeshNode* node = edge->_nodes[0];
-  const int shapeInd = node->getshapeId();
-  SMESHDS_SubMesh* edgeSM = 0;
+  const int        shapeInd = node->getshapeId();
+  SMESHDS_SubMesh*   edgeSM = 0;
   if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_EDGE )
   {
-    
     edgeSM = getMeshDS()->MeshElements( shapeInd );
     if ( !edgeSM || edgeSM->NbElements() == 0 )
       return error(SMESH_Comment("Not meshed EDGE ") << shapeInd, data._index);
@@ -1899,30 +2681,31 @@ void _LayerEdge::SetDataByNeighbors( const SMDS_MeshNode* n1,
 
   // Set _curvature
 
-  double sumLen = vec1.Modulus() + vec2.Modulus();
+  double      sumLen = vec1.Modulus() + vec2.Modulus();
   _2neibors->_wgt[0] = 1 - vec1.Modulus() / sumLen;
   _2neibors->_wgt[1] = 1 - vec2.Modulus() / sumLen;
   double avgNormProj = 0.5 * ( _normal * vec1 + _normal * vec2 );
-  double avgLen = 0.5 * ( vec1.Modulus() + vec2.Modulus() );
+  double      avgLen = 0.5 * ( vec1.Modulus() + vec2.Modulus() );
   if ( _curvature ) delete _curvature;
   _curvature = _Curvature::New( avgNormProj, avgLen );
-#ifdef __myDEBUG
-//     if ( _curvature )
-//       cout << _nodes[0]->GetID()
-//            << " CURV r,k: " << _curvature->_r<<","<<_curvature->_k
-//            << " proj = "<<avgNormProj<< " len = " << avgLen << "| lenDelta(0) = "
-//            << _curvature->lenDelta(0) << endl;
-#endif
+  // if ( _curvature )
+  //   debugMsg( _nodes[0]->GetID()
+  //             << " CURV r,k: " << _curvature->_r<<","<<_curvature->_k
+  //             << " proj = "<<avgNormProj<< " len = " << avgLen << "| lenDelta(0) = "
+  //             << _curvature->lenDelta(0) );
 
   // Set _plnNorm
 
   if ( _sWOL.IsNull() )
   {
     TopoDS_Shape S = helper.GetSubShapeByNode( _nodes[0], helper.GetMeshDS() );
-    gp_XYZ dirE = getEdgeDir( TopoDS::Edge( S ), _nodes[0], helper );
+    TopoDS_Edge  E = TopoDS::Edge( S );
+    // if ( SMESH_Algo::isDegenerated( E ))
+    //   return;
+    gp_XYZ dirE    = getEdgeDir( E, _nodes[0], helper );
     gp_XYZ plnNorm = dirE ^ _normal;
-    double proj0 = plnNorm * vec1;
-    double proj1 = plnNorm * vec2;
+    double proj0   = plnNorm * vec1;
+    double proj1   = plnNorm * vec2;
     if ( fabs( proj0 ) > 1e-10 || fabs( proj1 ) > 1e-10 )
     {
       if ( _2neibors->_plnNorm ) delete _2neibors->_plnNorm;
@@ -1938,7 +2721,7 @@ void _LayerEdge::SetDataByNeighbors( const SMDS_MeshNode* n1,
  */
 //================================================================================
 
-void _LayerEdge::Copy( _LayerEdge& other, SMESH_MesherHelper& helper )
+gp_XYZ _LayerEdge::Copy( _LayerEdge& other, SMESH_MesherHelper& helper )
 {
   _nodes     = other._nodes;
   _normal    = other._normal;
@@ -1950,16 +2733,25 @@ void _LayerEdge::Copy( _LayerEdge& other, SMESH_MesherHelper& helper )
   _curvature = 0; std::swap( _curvature, other._curvature );
   _2neibors  = 0; std::swap( _2neibors,  other._2neibors );
 
+  gp_XYZ lastPos( 0,0,0 );
   if ( _sWOL.ShapeType() == TopAbs_EDGE )
   {
     double u = helper.GetNodeU( TopoDS::Edge( _sWOL ), _nodes[0] );
     _pos.push_back( gp_XYZ( u, 0, 0));
+
+    u = helper.GetNodeU( TopoDS::Edge( _sWOL ), _nodes.back() );
+    lastPos.SetX( u );
   }
   else // TopAbs_FACE
   {
     gp_XY uv = helper.GetNodeUV( TopoDS::Face( _sWOL ), _nodes[0]);
     _pos.push_back( gp_XYZ( uv.X(), uv.Y(), 0));
+
+    uv = helper.GetNodeUV( TopoDS::Face( _sWOL ), _nodes.back() );
+    lastPos.SetX( uv.X() );
+    lastPos.SetY( uv.Y() );
   }
+  return lastPos;
 }
 
 //================================================================================
@@ -1971,7 +2763,8 @@ void _LayerEdge::Copy( _LayerEdge& other, SMESH_MesherHelper& helper )
 void _LayerEdge::SetCosin( double cosin )
 {
   _cosin = cosin;
-  _lenFactor = ( _cosin > 0.1 ) ?  1./sqrt(1-_cosin*_cosin) : 1.0;
+  cosin = Abs( _cosin );
+  _lenFactor = ( /*0.1 < cosin &&*/ cosin < 1-1e-12 ) ?  1./sqrt(1-cosin*cosin) : 1.0;
 }
 
 //================================================================================
@@ -1986,14 +2779,15 @@ void _ViscousBuilder::getSimplices( const SMDS_MeshNode* node,
                                     const _SolidData*    dataToCheckOri,
                                     const bool           toSort)
 {
+  simplices.clear();
   SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator(SMDSAbs_Face);
   while ( fIt->more() )
   {
     const SMDS_MeshElement* f = fIt->next();
-    const TGeomID shapeInd = f->getshapeId();
+    const TGeomID    shapeInd = f->getshapeId();
     if ( ingnoreShapes.count( shapeInd )) continue;
     const int nbNodes = f->NbCornerNodes();
-    int srcInd = f->GetNodeIndex( node );
+    const int  srcInd = f->GetNodeIndex( node );
     const SMDS_MeshNode* nPrev = f->GetNode( SMESH_MesherHelper::WrapIndex( srcInd-1, nbNodes ));
     const SMDS_MeshNode* nNext = f->GetNode( SMESH_MesherHelper::WrapIndex( srcInd+1, nbNodes ));
     const SMDS_MeshNode* nOpp  = f->GetNode( SMESH_MesherHelper::WrapIndex( srcInd+2, nbNodes ));
@@ -2031,28 +2825,22 @@ void _ViscousBuilder::getSimplices( const SMDS_MeshNode* node,
 void _ViscousBuilder::makeGroupOfLE()
 {
 #ifdef _DEBUG_
-  for ( unsigned i = 0 ; i < _sdVec.size(); ++i )
+  for ( size_t i = 0 ; i < _sdVec.size(); ++i )
   {
     if ( _sdVec[i]._edges.empty() ) continue;
-//     string name = SMESH_Comment("_LayerEdge's_") << i;
-//     int id;
-//     SMESH_Group* g = _mesh->AddGroup(SMDSAbs_Edge, name.c_str(), id );
-//     SMESHDS_Group* gDS = (SMESHDS_Group*)g->GetGroupDS();
-//     SMESHDS_Mesh* mDS = _mesh->GetMeshDS();
 
     dumpFunction( SMESH_Comment("make_LayerEdge_") << i );
-    for ( unsigned j = 0 ; j < _sdVec[i]._edges.size(); ++j )
+    for ( size_t j = 0 ; j < _sdVec[i]._edges.size(); ++j )
     {
       _LayerEdge* le = _sdVec[i]._edges[j];
-      for ( unsigned iN = 1; iN < le->_nodes.size(); ++iN )
+      for ( size_t iN = 1; iN < le->_nodes.size(); ++iN )
         dumpCmd(SMESH_Comment("mesh.AddEdge([ ") <<le->_nodes[iN-1]->GetID()
                 << ", " << le->_nodes[iN]->GetID() <<"])");
-      //gDS->SMDSGroup().Add( mDS->AddEdge( le->_nodes[iN-1], le->_nodes[iN]));
     }
     dumpFunctionEnd();
 
     dumpFunction( SMESH_Comment("makeNormals") << i );
-    for ( unsigned j = 0 ; j < _sdVec[i]._edges.size(); ++j )
+    for ( size_t j = 0 ; j < _sdVec[i]._edges.size(); ++j )
     {
       _LayerEdge& edge = *_sdVec[i]._edges[j];
       SMESH_TNodeXYZ nXYZ( edge._nodes[0] );
@@ -2062,10 +2850,6 @@ void _ViscousBuilder::makeGroupOfLE()
     }
     dumpFunctionEnd();
 
-//     name = SMESH_Comment("tmp_faces ") << i;
-//     g = _mesh->AddGroup(SMDSAbs_Face, name.c_str(), id );
-//     gDS = (SMESHDS_Group*)g->GetGroupDS();
-//     SMESH_MeshEditor editor( _mesh );
     dumpFunction( SMESH_Comment("makeTmpFaces_") << i );
     TopExp_Explorer fExp( _sdVec[i]._solid, TopAbs_FACE );
     for ( ; fExp.More(); fExp.Next() )
@@ -2080,8 +2864,6 @@ void _ViscousBuilder::makeGroupOfLE()
           for ( int j=0; j < e->NbCornerNodes(); ++j )
             cmd << e->GetNode(j)->GetID() << (j+1<e->NbCornerNodes() ? ",": "])");
           dumpCmd( cmd );
-          //vector<const SMDS_MeshNode*> nodes( e->begin_nodes(), e->end_nodes() );
-          //gDS->SMDSGroup().Add( editor.AddElement( nodes, e->GetType(), e->IsPoly()));
         }
       }
     }
@@ -2090,6 +2872,31 @@ void _ViscousBuilder::makeGroupOfLE()
 #endif
 }
 
+//================================================================================
+/*!
+ * \brief Find maximal _LayerEdge length (layer thickness) limited by geometry
+ */
+//================================================================================
+
+void _ViscousBuilder::computeGeomSize( _SolidData& data )
+{
+  data._geomSize = Precision::Infinite();
+  double intersecDist;
+  auto_ptr<SMESH_ElementSearcher> searcher
+    ( SMESH_MeshAlgos::GetElementSearcher( *getMeshDS(),
+                                           data._proxyMesh->GetFaces( data._solid )) );
+
+  TNode2Edge::iterator n2e = data._n2eMap.begin(), n2eEnd = data._n2eMap.end();
+  for ( ; n2e != n2eEnd; ++n2e )
+  {
+    _LayerEdge* edge = n2e->second;
+    if ( edge->IsOnEdge() ) continue;
+    edge->FindIntersection( *searcher, intersecDist, data._epsilon );
+    if ( data._geomSize > intersecDist && intersecDist > 0 )
+      data._geomSize = intersecDist;
+  }
+}
+
 //================================================================================
 /*!
  * \brief Increase length of _LayerEdge's to reach the required thickness of layers
@@ -2102,19 +2909,8 @@ bool _ViscousBuilder::inflate(_SolidData& data)
 
   // Limit inflation step size by geometry size found by itersecting
   // normals of _LayerEdge's with mesh faces
-  double geomSize = Precision::Infinite(), intersecDist;
-  auto_ptr<SMESH_ElementSearcher> searcher
-    ( SMESH_MeshAlgos::GetElementSearcher( *getMeshDS(),
-                                           data._proxyMesh->GetFaces( data._solid )) );
-  for ( unsigned i = 0; i < data._edges.size(); ++i )
-  {
-    if ( data._edges[i]->IsOnEdge() ) continue;
-    data._edges[i]->FindIntersection( *searcher, intersecDist, data._epsilon );
-    if ( geomSize > intersecDist && intersecDist > 0 )
-      geomSize = intersecDist;
-  }
-  if ( data._stepSize > 0.3 * geomSize )
-    limitStepSize( data, 0.3 * geomSize );
+  if ( data._stepSize > 0.3 * data._geomSize )
+    limitStepSize( data, 0.3 * data._geomSize );
 
   const double tgtThick = data._hyp->GetTotalThickness();
   if ( data._stepSize > tgtThick )
@@ -2123,9 +2919,7 @@ bool _ViscousBuilder::inflate(_SolidData& data)
   if ( data._stepSize < 1. )
     data._epsilon = data._stepSize * 1e-7;
 
-#ifdef __myDEBUG
-  cout << "-- geomSize = " << geomSize << ", stepSize = " << data._stepSize << endl;
-#endif
+  debugMsg( "-- geomSize = " << data._geomSize << ", stepSize = " << data._stepSize );
 
   double avgThick = 0, curThick = 0, distToIntersection = Precision::Infinite();
   int nbSteps = 0, nbRepeats = 0;
@@ -2141,15 +2935,14 @@ bool _ViscousBuilder::inflate(_SolidData& data)
 
     // Elongate _LayerEdge's
     dumpFunction(SMESH_Comment("inflate")<<data._index<<"_step"<<nbSteps); // debug
-    for ( unsigned i = 0; i < data._edges.size(); ++i )
+    for ( size_t i = 0; i < data._edges.size(); ++i )
     {
       data._edges[i]->SetNewLength( curThick, helper );
     }
     dumpFunctionEnd();
 
-    if ( !nbSteps )
-      if ( !updateNormals( data, helper ) )
-        return false;
+    if ( !updateNormals( data, helper, nbSteps ))
+      return false;
 
     // Improve and check quality
     if ( !smoothAndCheck( data, nbSteps, distToIntersection ))
@@ -2157,7 +2950,7 @@ bool _ViscousBuilder::inflate(_SolidData& data)
       if ( nbSteps > 0 )
       {
         dumpFunction(SMESH_Comment("invalidate")<<data._index<<"_step"<<nbSteps); // debug
-        for ( unsigned i = 0; i < data._edges.size(); ++i )
+        for ( size_t i = 0; i < data._edges.size(); ++i )
         {
           data._edges[i]->InvalidateStep( nbSteps+1 );
         }
@@ -2169,19 +2962,16 @@ bool _ViscousBuilder::inflate(_SolidData& data)
 
     // Evaluate achieved thickness
     avgThick = 0;
-    for ( unsigned i = 0; i < data._edges.size(); ++i )
+    for ( size_t i = 0; i < data._edges.size(); ++i )
       avgThick += data._edges[i]->_len;
     avgThick /= data._edges.size();
-#ifdef __myDEBUG
-    cout << "-- Thickness " << avgThick << " reached" << endl;
-#endif
+    debugMsg( "-- Thickness " << avgThick << " reached" );
 
     if ( distToIntersection < avgThick*1.5 )
     {
-#ifdef __myDEBUG
-      cout << "-- Stop inflation since distToIntersection( "<<distToIntersection<<" ) < avgThick( "
-           << avgThick << " ) * 1.5" << endl;
-#endif
+      debugMsg( "-- Stop inflation since "
+                << " distToIntersection( "<<distToIntersection<<" ) < avgThick( "
+                << avgThick << " ) * 1.5" );
       break;
     }
     // new step size
@@ -2189,11 +2979,40 @@ bool _ViscousBuilder::inflate(_SolidData& data)
     if ( data._stepSizeNodes[0] )
       data._stepSize = data._stepSizeCoeff *
         SMESH_TNodeXYZ(data._stepSizeNodes[0]).Distance(data._stepSizeNodes[1]);
-  }
+
+  } // while ( 1.01 * avgThick < tgtThick )
 
   if (nbSteps == 0 )
     return error("failed at the very first inflation step", data._index);
 
+  if ( 1.01 * avgThick < tgtThick )
+    if ( SMESH_subMesh* sm = _mesh->GetSubMeshContaining( data._index ))
+    {
+      SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
+      if ( !smError || smError->IsOK() )
+        smError.reset
+          ( new SMESH_ComputeError (COMPERR_WARNING,
+                                    SMESH_Comment("Thickness ") << tgtThick <<
+                                    " of viscous layers not reached,"
+                                    " average reached thickness is " << avgThick ));
+    }
+
+
+  // Restore position of src nodes moved by infaltion on _noShrinkShapes
+  dumpFunction(SMESH_Comment("restoNoShrink_So")<<data._index); // debug
+  int iBeg, iEnd = 0;
+  for ( int iS = 0; iS < data._endEdgeOnShape.size(); ++iS )
+  {
+    iBeg = iEnd;
+    iEnd = data._endEdgeOnShape[ iS ];
+    if ( data._edges[ iBeg ]->_nodes.size() == 1 )
+      for ( ; iBeg < iEnd; ++iBeg )
+      {
+        restoreNoShrink( *data._edges[ iBeg ] );
+      }
+  }
+  dumpFunctionEnd();
+
   return true;
 }
 
@@ -2207,7 +3026,7 @@ bool _ViscousBuilder::smoothAndCheck(_SolidData& data,
                                      const int   nbSteps,
                                      double &    distToIntersection)
 {
-  if ( data._endEdgeToSmooth.empty() )
+  if ( data._nbShapesToSmooth == 0 )
     return true; // no shapes needing smoothing
 
   bool moved, improved;
@@ -2217,10 +3036,10 @@ bool _ViscousBuilder::smoothAndCheck(_SolidData& data,
   TopoDS_Face F;
 
   int iBeg, iEnd = 0;
-  for ( unsigned iS = 0; iS < data._endEdgeToSmooth.size(); ++iS )
+  for ( int iS = 0; iS < data._nbShapesToSmooth; ++iS )
   {
     iBeg = iEnd;
-    iEnd = data._endEdgeToSmooth[ iS ];
+    iEnd = data._endEdgeOnShape[ iS ];
 
     if ( !data._edges[ iBeg ]->_sWOL.IsNull() &&
          data._edges[ iBeg ]->_sWOL.ShapeType() == TopAbs_FACE )
@@ -2255,25 +3074,32 @@ bool _ViscousBuilder::smoothAndCheck(_SolidData& data,
           dumpCmd( SMESH_Comment("# end step ")<<step);
         }
         while ( moved && step++ < 5 );
-        //cout << " NB STEPS: " << step << endl;
       }
       dumpFunctionEnd();
     }
     else
     {
       // smooth on FACE's
-      int step = 0, badNb = 0; moved = true;
-      while (( ++step <= 5 && moved ) || improved )
+      int step = 0, stepLimit = 5, badNb = 0; moved = true;
+      while (( ++step <= stepLimit && moved ) || improved )
       {
         dumpFunction(SMESH_Comment("smooth")<<data._index<<"_Fa"<<sInd
                      <<"_InfStep"<<nbSteps<<"_"<<step); // debug
         int oldBadNb = badNb;
         badNb = 0;
         moved = false;
-        for ( int i = iBeg; i < iEnd; ++i )
-          moved |= data._edges[i]->Smooth(badNb);
+        if ( step % 2 )
+          for ( int i = iBeg; i < iEnd; ++i )
+            moved |= data._edges[i]->Smooth(badNb);
+        else
+          for ( int i = iEnd-1; i >= iBeg; --i )
+            moved |= data._edges[i]->Smooth(badNb);
         improved = ( badNb < oldBadNb );
 
+        // issue 22576 -- no bad faces but still there are intersections to fix
+        if ( improved && badNb == 0 )
+          stepLimit = step + 3;
+
         dumpFunctionEnd();
       }
       if ( badNb > 0 )
@@ -2283,7 +3109,7 @@ bool _ViscousBuilder::smoothAndCheck(_SolidData& data,
         {
           _LayerEdge* edge = data._edges[i];
           SMESH_TNodeXYZ tgtXYZ( edge->_nodes.back() );
-          for ( unsigned j = 0; j < edge->_simplices.size(); ++j )
+          for ( size_t j = 0; j < edge->_simplices.size(); ++j )
             if ( !edge->_simplices[j].IsForward( edge->_nodes[0], &tgtXYZ ))
             {
               cout << "Bad simplex ( " << edge->_nodes[0]->GetID()<< " "<< tgtXYZ._node->GetID()
@@ -2298,6 +3124,19 @@ bool _ViscousBuilder::smoothAndCheck(_SolidData& data,
     }
   } // loop on shapes to smooth
 
+  // Check orientation of simplices of _ConvexFace::_simplexTestEdges
+  map< TGeomID, _ConvexFace >::iterator id2face = data._convexFaces.begin();
+  for ( ; id2face != data._convexFaces.end(); ++id2face )
+  {
+    _ConvexFace & convFace = (*id2face).second;
+    if ( !convFace._simplexTestEdges.empty() &&
+         convFace._simplexTestEdges[0]->_nodes[0]->GetPosition()->GetDim() == 2 )
+      continue; // _simplexTestEdges are based on FACE -- already checked while smoothing
+
+    if ( !convFace.CheckPrisms() )
+      return false;
+  }
+
   // Check if the last segments of _LayerEdge intersects 2D elements;
   // checked elements are either temporary faces or faces on surfaces w/o the layers
 
@@ -2308,21 +3147,25 @@ bool _ViscousBuilder::smoothAndCheck(_SolidData& data,
   distToIntersection = Precision::Infinite();
   double dist;
   const SMDS_MeshElement* intFace = 0;
-#ifdef __myDEBUG
   const SMDS_MeshElement* closestFace = 0;
   int iLE = 0;
-#endif
-  for ( unsigned i = 0; i < data._edges.size(); ++i )
+  for ( size_t i = 0; i < data._edges.size(); ++i )
   {
+    if ( !data._edges[i]->_sWOL.IsNull() )
+      continue;
     if ( data._edges[i]->FindIntersection( *searcher, dist, data._epsilon, &intFace ))
       return false;
     if ( distToIntersection > dist )
     {
+      // ignore intersection of a _LayerEdge based on a _ConvexFace with a face
+      // lying on this _ConvexFace
+      if ( _ConvexFace* convFace = data.GetConvexFace( intFace->getshapeId() ))
+        if ( convFace->_subIdToEdgeEnd.count ( data._edges[i]->_nodes[0]->getshapeId() ))
+          continue;
+
       distToIntersection = dist;
-#ifdef __myDEBUG
       iLE = i;
       closestFace = intFace;
-#endif
     }
   }
 #ifdef __myDEBUG
@@ -2360,24 +3203,7 @@ Handle(Geom_Curve) _SolidData::CurveForSmooth( const TopoDS_Edge&    E,
   if ( i2curve == _edge2curve.end() )
   {
     // sort _LayerEdge's by position on the EDGE
-    {
-      map< double, _LayerEdge* > u2edge;
-      for ( int i = iFrom; i < iTo; ++i )
-        u2edge.insert( make_pair( helper.GetNodeU( E, _edges[i]->_nodes[0] ), _edges[i] ));
-
-      ASSERT( u2edge.size() == iTo - iFrom );
-      map< double, _LayerEdge* >::iterator u2e = u2edge.begin();
-      for ( int i = iFrom; i < iTo; ++i, ++u2e )
-        _edges[i] = u2e->second;
-
-      // set _2neibors according to the new order
-      for ( int i = iFrom; i < iTo-1; ++i )
-        if ( _edges[i]->_2neibors->_nodes[1] != _edges[i+1]->_nodes.back() )
-          _edges[i]->_2neibors->reverse();
-      if ( u2edge.size() > 1 &&
-           _edges[iTo-1]->_2neibors->_nodes[0] != _edges[iTo-2]->_nodes.back() )
-        _edges[iTo-1]->_2neibors->reverse();
-    }
+    SortOnEdge( E, iFrom, iTo, helper );
 
     SMESHDS_SubMesh* smDS = helper.GetMeshDS()->MeshElements( eIndex );
 
@@ -2406,8 +3232,8 @@ Handle(Geom_Curve) _SolidData::CurveForSmooth( const TopoDS_Edge&    E,
           bndBox.Add( SMESH_TNodeXYZ( nIt->next() ));
         gp_XYZ size = bndBox.CornerMax() - bndBox.CornerMin();
 
-        SMESH_TNodeXYZ p0( _edges[iFrom]->_2neibors->_nodes[0] );
-        SMESH_TNodeXYZ p1( _edges[iFrom]->_2neibors->_nodes[1] );
+        SMESH_TNodeXYZ p0( _edges[iFrom]->_2neibors->tgtNode(0) );
+        SMESH_TNodeXYZ p1( _edges[iFrom]->_2neibors->tgtNode(1) );
         const double lineTol = 1e-2 * ( p0 - p1 ).Modulus();
         for ( int i = 0; i < 3 && !isLine; ++i )
           isLine = ( size.Coord( i+1 ) <= lineTol );
@@ -2468,6 +3294,121 @@ Handle(Geom_Curve) _SolidData::CurveForSmooth( const TopoDS_Edge&    E,
   return i2curve->second;
 }
 
+//================================================================================
+/*!
+ * \brief Sort _LayerEdge's by a parameter on a given EDGE
+ */
+//================================================================================
+
+void _SolidData::SortOnEdge( const TopoDS_Edge&  E,
+                             const int           iFrom,
+                             const int           iTo,
+                             SMESH_MesherHelper& helper)
+{
+  map< double, _LayerEdge* > u2edge;
+  for ( int i = iFrom; i < iTo; ++i )
+    u2edge.insert( make_pair( helper.GetNodeU( E, _edges[i]->_nodes[0] ), _edges[i] ));
+
+  ASSERT( u2edge.size() == iTo - iFrom );
+  map< double, _LayerEdge* >::iterator u2e = u2edge.begin();
+  for ( int i = iFrom; i < iTo; ++i, ++u2e )
+    _edges[i] = u2e->second;
+
+  // set _2neibors according to the new order
+  for ( int i = iFrom; i < iTo-1; ++i )
+    if ( _edges[i]->_2neibors->tgtNode(1) != _edges[i+1]->_nodes.back() )
+      _edges[i]->_2neibors->reverse();
+  if ( u2edge.size() > 1 &&
+       _edges[iTo-1]->_2neibors->tgtNode(0) != _edges[iTo-2]->_nodes.back() )
+    _edges[iTo-1]->_2neibors->reverse();
+}
+
+//================================================================================
+/*!
+ * \brief Return index corresponding to the shape in _endEdgeOnShape
+ */
+//================================================================================
+
+bool _SolidData::GetShapeEdges(const TGeomID shapeID,
+                               size_t &      edgesEnd,
+                               int*          iBeg,
+                               int*          iEnd ) const
+{
+  int beg = 0, end = 0;
+  for ( edgesEnd = 0; edgesEnd < _endEdgeOnShape.size(); ++edgesEnd )
+  {
+    end = _endEdgeOnShape[ edgesEnd ];
+    TGeomID sID = _edges[ beg ]->_nodes[0]->getshapeId();
+    if ( sID == shapeID )
+    {
+      if ( iBeg ) *iBeg = beg;
+      if ( iEnd ) *iEnd = end;
+      return true;
+    }
+    beg = end;
+  }
+  return false;
+}
+
+//================================================================================
+/*!
+ * \brief Add faces for smoothing
+ */
+//================================================================================
+
+void _SolidData::AddShapesToSmooth( const set< TGeomID >& faceIDs )
+{
+  // convert faceIDs to indices in _endEdgeOnShape
+  set< size_t > iEnds;
+  size_t end;
+  set< TGeomID >::const_iterator fId = faceIDs.begin();
+  for ( ; fId != faceIDs.end(); ++fId )
+    if ( GetShapeEdges( *fId, end ) && end >= _nbShapesToSmooth )
+      iEnds.insert( end );
+
+  set< size_t >::iterator endsIt = iEnds.begin();
+
+  // "add" by move of _nbShapesToSmooth
+  int nbFacesToAdd = iEnds.size();
+  while ( endsIt != iEnds.end() && *endsIt == _nbShapesToSmooth )
+  {
+    ++endsIt;
+    ++_nbShapesToSmooth;
+    --nbFacesToAdd;
+  }
+  if ( endsIt == iEnds.end() )
+    return;
+
+  // Move _LayerEdge's on FACEs just after _nbShapesToSmooth
+
+  vector< _LayerEdge* > nonSmoothLE, smoothLE;
+  size_t lastSmooth = *iEnds.rbegin();
+  int iBeg, iEnd;
+  for ( size_t i = _nbShapesToSmooth; i <= lastSmooth; ++i )
+  {
+    vector< _LayerEdge* > & edgesVec = iEnds.count(i) ? smoothLE : nonSmoothLE;
+    iBeg = i ? _endEdgeOnShape[ i-1 ] : 0;
+    iEnd = _endEdgeOnShape[ i ];
+    edgesVec.insert( edgesVec.end(), _edges.begin() + iBeg, _edges.begin() + iEnd ); 
+  }
+
+  iBeg = _nbShapesToSmooth ? _endEdgeOnShape[ _nbShapesToSmooth-1 ] : 0;
+  std::copy( smoothLE.begin(),    smoothLE.end(),    &_edges[ iBeg ] );
+  std::copy( nonSmoothLE.begin(), nonSmoothLE.end(), &_edges[ iBeg + smoothLE.size()]);
+
+  // update _endEdgeOnShape
+  for ( size_t i = _nbShapesToSmooth; i < _endEdgeOnShape.size(); ++i )
+  {
+    TGeomID curShape = _edges[ iBeg ]->_nodes[0]->getshapeId();
+    while ( ++iBeg < _edges.size() &&
+            curShape == _edges[ iBeg ]->_nodes[0]->getshapeId() );
+
+    _endEdgeOnShape[ i ] = iBeg;
+  }
+
+  _nbShapesToSmooth += nbFacesToAdd;
+}
+
 //================================================================================
 /*!
  * \brief smooth _LayerEdge's on a staight EDGE or circular EDGE
@@ -2504,8 +3445,8 @@ bool _ViscousBuilder::smoothAnalyticEdge( _SolidData&           data,
   {
     if ( F.IsNull() ) // 3D
     {
-      SMESH_TNodeXYZ p0( data._edges[iFrom]->_2neibors->_nodes[0]);
-      SMESH_TNodeXYZ p1( data._edges[iTo-1]->_2neibors->_nodes[1]);
+      SMESH_TNodeXYZ p0( data._edges[iFrom]->_2neibors->tgtNode(0));
+      SMESH_TNodeXYZ p1( data._edges[iTo-1]->_2neibors->tgtNode(1));
       for ( int i = iFrom; i < iTo; ++i )
       {
         double r = len[i-iFrom] / len.back();
@@ -2518,10 +3459,12 @@ bool _ViscousBuilder::smoothAnalyticEdge( _SolidData&           data,
     }
     else
     {
-      gp_XY uv0 = helper.GetNodeUV( F, data._edges[iFrom]->_2neibors->_nodes[0]);
-      gp_XY uv1 = helper.GetNodeUV( F, data._edges[iTo-1]->_2neibors->_nodes[1]);
-      if ( data._edges[iFrom]->_2neibors->_nodes[0] ==
-           data._edges[iTo-1]->_2neibors->_nodes[1] ) // closed edge
+      // gp_XY uv0 = helper.GetNodeUV( F, data._edges[iFrom]->_2neibors->tgtNode(0));
+      // gp_XY uv1 = helper.GetNodeUV( F, data._edges[iTo-1]->_2neibors->tgtNode(1));
+      gp_XY uv0 = data._edges[iFrom]->_2neibors->_edges[0]->LastUV( F );
+      gp_XY uv1 = data._edges[iTo-1]->_2neibors->_edges[1]->LastUV( F );
+      if ( data._edges[iFrom]->_2neibors->tgtNode(0) ==
+           data._edges[iTo-1]->_2neibors->tgtNode(1) ) // closed edge
       {
         int iPeriodic = helper.GetPeriodicIndex();
         if ( iPeriodic == 1 || iPeriodic == 2 )
@@ -2558,8 +3501,8 @@ bool _ViscousBuilder::smoothAnalyticEdge( _SolidData&           data,
 
     if ( F.IsNull() ) // 3D
     {
-      if ( data._edges[iFrom]->_2neibors->_nodes[0] ==
-           data._edges[iTo-1]->_2neibors->_nodes[1] )
+      if ( data._edges[iFrom]->_2neibors->tgtNode(0) ==
+           data._edges[iTo-1]->_2neibors->tgtNode(1) )
         return true; // closed EDGE - nothing to do
 
       return false; // TODO ???
@@ -2568,15 +3511,18 @@ bool _ViscousBuilder::smoothAnalyticEdge( _SolidData&           data,
     {
       const gp_XY center( center3D.X(), center3D.Y() );
 
-      gp_XY uv0 = helper.GetNodeUV( F, data._edges[iFrom]->_2neibors->_nodes[0]);
-      gp_XY uvM = helper.GetNodeUV( F, data._edges[iFrom]->_nodes.back());
-      gp_XY uv1 = helper.GetNodeUV( F, data._edges[iTo-1]->_2neibors->_nodes[1]);
+      gp_XY uv0 = data._edges[iFrom]->_2neibors->_edges[0]->LastUV( F );
+      gp_XY uvM = data._edges[iFrom]->LastUV( F );
+      gp_XY uv1 = data._edges[iTo-1]->_2neibors->_edges[1]->LastUV( F );
+      // gp_XY uv0 = helper.GetNodeUV( F, data._edges[iFrom]->_2neibors->tgtNode(0));
+      // gp_XY uvM = helper.GetNodeUV( F, data._edges[iFrom]->_nodes.back());
+      // gp_XY uv1 = helper.GetNodeUV( F, data._edges[iTo-1]->_2neibors->tgtNode(1));
       gp_Vec2d vec0( center, uv0 );
       gp_Vec2d vecM( center, uvM );
       gp_Vec2d vec1( center, uv1 );
       double uLast = vec0.Angle( vec1 ); // -PI - +PI
       double uMidl = vec0.Angle( vecM );
-      if ( uLast * uMidl < 0. )
+      if ( uLast * uMidl <= 0. )
         uLast += ( uMidl > 0 ? +2. : -2. ) * M_PI;
       const double radius = 0.5 * ( vec0.Magnitude() + vec1.Magnitude() );
 
@@ -2612,8 +3558,12 @@ bool _ViscousBuilder::smoothAnalyticEdge( _SolidData&           data,
 //================================================================================
 
 bool _ViscousBuilder::updateNormals( _SolidData&         data,
-                                     SMESH_MesherHelper& helper )
+                                     SMESH_MesherHelper& helper,
+                                     int                 stepNb )
 {
+  if ( stepNb > 0 )
+    return updateNormalsOfConvexFaces( data, helper, stepNb );
+
   // make temporary quadrangles got by extrusion of
   // mesh edges along _LayerEdge._normal's
 
@@ -2623,14 +3573,14 @@ bool _ViscousBuilder::updateNormals( _SolidData&         data,
     vector< const SMDS_MeshNode*> nodes(4); // of a tmp mesh face
 
     dumpFunction(SMESH_Comment("makeTmpFacesOnEdges")<<data._index);
-    for ( unsigned i = 0; i < data._edges.size(); ++i )
+    for ( size_t i = 0; i < data._edges.size(); ++i )
     {
       _LayerEdge* edge = data._edges[i];
       if ( !edge->IsOnEdge() || !edge->_sWOL.IsNull() ) continue;
       const SMDS_MeshNode* tgt1 = edge->_nodes.back();
       for ( int j = 0; j < 2; ++j ) // loop on _2NearEdges
       {
-        const SMDS_MeshNode* tgt2 = edge->_2neibors->_nodes[j];
+        const SMDS_MeshNode* tgt2 = edge->_2neibors->tgtNode(j);
         pair< set< SMESH_TLink >::iterator, bool > link_isnew =
           extrudedLinks.insert( SMESH_TLink( tgt1, tgt2 ));
         if ( !link_isnew.second )
@@ -2638,21 +3588,10 @@ bool _ViscousBuilder::updateNormals( _SolidData&         data,
           extrudedLinks.erase( link_isnew.first );
           continue; // already extruded and will no more encounter
         }
-        // look for a _LayerEdge containg tgt2
-//         _LayerEdge* neiborEdge = 0;
-//         unsigned di = 0; // check _edges[i+di] and _edges[i-di]
-//         while ( !neiborEdge && ++di <= data._edges.size() )
-//         {
-//           if ( i+di < data._edges.size() && data._edges[i+di]->_nodes.back() == tgt2 )
-//             neiborEdge = data._edges[i+di];
-//           else if ( di <= i && data._edges[i-di]->_nodes.back() == tgt2 )
-//             neiborEdge = data._edges[i-di];
-//         }
-//         if ( !neiborEdge )
-//           return error("updateNormals(): neighbor _LayerEdge not found", data._index);
+        // a _LayerEdge containg tgt2
         _LayerEdge* neiborEdge = edge->_2neibors->_edges[j];
 
-        TmpMeshFaceOnEdge* f = new TmpMeshFaceOnEdge( edge, neiborEdge, --_tmpFaceID );
+        _TmpMeshFaceOnEdge* f = new _TmpMeshFaceOnEdge( edge, neiborEdge, --_tmpFaceID );
         tmpFaces.push_back( f );
 
         dumpCmd(SMESH_Comment("mesh.AddFace([ ")
@@ -2679,13 +3618,15 @@ bool _ViscousBuilder::updateNormals( _SolidData&         data,
   TLEdge2LEdgeSet edge2CloseEdge;
 
   const double eps = data._epsilon * data._epsilon;
-  for ( unsigned i = 0; i < data._edges.size(); ++i )
+  for ( size_t i = 0; i < data._edges.size(); ++i )
   {
     _LayerEdge* edge = data._edges[i];
-    if ( !edge->IsOnEdge() || !edge->_sWOL.IsNull() ) continue;
+    if (( !edge->IsOnEdge() ) &&
+        ( edge->_sWOL.IsNull() || edge->_sWOL.ShapeType() != TopAbs_FACE ))
+      continue;
     if ( edge->FindIntersection( *searcher, dist, eps, &face ))
     {
-      const TmpMeshFaceOnEdge* f = (const TmpMeshFaceOnEdge*) face;
+      const _TmpMeshFaceOnEdge* f = (const _TmpMeshFaceOnEdge*) face;
       set< _LayerEdge*, _LayerEdgeCmp > & ee = edge2CloseEdge[ edge ];
       ee.insert( f->_le1 );
       ee.insert( f->_le2 );
@@ -2702,109 +3643,163 @@ bool _ViscousBuilder::updateNormals( _SolidData&         data,
   {
     dumpFunction(SMESH_Comment("updateNormals")<<data._index);
 
+    set< TGeomID > shapesToSmooth;
+
+    // vector to store new _normal and _cosin for each edge in edge2CloseEdge
+    vector< pair< _LayerEdge*, _LayerEdge > > edge2newEdge( edge2CloseEdge.size() );
+
     TLEdge2LEdgeSet::iterator e2ee = edge2CloseEdge.begin();
-    for ( ; e2ee != edge2CloseEdge.end(); ++e2ee )
+    for ( size_t iE = 0; e2ee != edge2CloseEdge.end(); ++e2ee, ++iE )
     {
-      _LayerEdge* edge1       = e2ee->first;
-      _LayerEdge* edge2       = 0;
-      set< _LayerEdge*, _LayerEdgeCmp >& ee  = e2ee->second;
+      _LayerEdge* edge1 = e2ee->first;
+      _LayerEdge* edge2 = 0;
+      set< _LayerEdge*, _LayerEdgeCmp >& ee = e2ee->second;
+
+      edge2newEdge[ iE ].first = NULL;
 
       // find EDGEs the edges reside
-      TopoDS_Edge E1, E2;
-      TopoDS_Shape S = helper.GetSubShapeByNode( edge1->_nodes[0], getMeshDS() );
-      if ( S.ShapeType() != TopAbs_EDGE )
-        continue; // TODO: find EDGE by VERTEX
-      E1 = TopoDS::Edge( S );
+      // TopoDS_Edge E1, E2;
+      // TopoDS_Shape S = helper.GetSubShapeByNode( edge1->_nodes[0], getMeshDS() );
+      // if ( S.ShapeType() != TopAbs_EDGE )
+      //   continue; // TODO: find EDGE by VERTEX
+      // E1 = TopoDS::Edge( S );
       set< _LayerEdge*, _LayerEdgeCmp >::iterator eIt = ee.begin();
-      while ( E2.IsNull() && eIt != ee.end())
+      for ( ; !edge2 && eIt != ee.end(); ++eIt )
       {
-        _LayerEdge* e2 = *eIt++;
-        TopoDS_Shape S = helper.GetSubShapeByNode( e2->_nodes[0], getMeshDS() );
-        if ( S.ShapeType() == TopAbs_EDGE )
-          E2 = TopoDS::Edge( S ), edge2 = e2;
+        if ( edge1->_sWOL == (*eIt)->_sWOL )
+          edge2 = *eIt;
       }
-      if ( E2.IsNull() ) continue; // TODO: find EDGE by VERTEX
+      if ( !edge2 ) continue;
+
+      edge2newEdge[ iE ].first = edge1;
+      _LayerEdge& newEdge = edge2newEdge[ iE ].second;
+      // while ( E2.IsNull() && eIt != ee.end())
+      // {
+      //   _LayerEdge* e2 = *eIt++;
+      //   TopoDS_Shape S = helper.GetSubShapeByNode( e2->_nodes[0], getMeshDS() );
+      //   if ( S.ShapeType() == TopAbs_EDGE )
+      //     E2 = TopoDS::Edge( S ), edge2 = e2;
+      // }
+      // if ( E2.IsNull() ) continue; // TODO: find EDGE by VERTEX
 
       // find 3 FACEs sharing 2 EDGEs
 
-      TopoDS_Face FF1[2], FF2[2];
-      PShapeIteratorPtr fIt = helper.GetAncestors(E1, *_mesh, TopAbs_FACE);
-      while ( fIt->more() && FF1[1].IsNull())
+      // TopoDS_Face FF1[2], FF2[2];
+      // PShapeIteratorPtr fIt = helper.GetAncestors(E1, *_mesh, TopAbs_FACE);
+      // while ( fIt->more() && FF1[1].IsNull() )
+      // {
+      //   const TopoDS_Face *F = (const TopoDS_Face*) fIt->next();
+      //   if ( helper.IsSubShape( *F, data._solid))
+      //     FF1[ FF1[0].IsNull() ? 0 : 1 ] = *F;
+      // }
+      // fIt = helper.GetAncestors(E2, *_mesh, TopAbs_FACE);
+      // while ( fIt->more() && FF2[1].IsNull())
+      // {
+      //   const TopoDS_Face *F = (const TopoDS_Face*) fIt->next();
+      //   if ( helper.IsSubShape( *F, data._solid))
+      //     FF2[ FF2[0].IsNull() ? 0 : 1 ] = *F;
+      // }
+      // // exclude a FACE common to E1 and E2 (put it to FFn[1] )
+      // if ( FF1[0].IsSame( FF2[0]) || FF1[0].IsSame( FF2[1]))
+      //   std::swap( FF1[0], FF1[1] );
+      // if ( FF2[0].IsSame( FF1[0]) )
+      //   std::swap( FF2[0], FF2[1] );
+      // if ( FF1[0].IsNull() || FF2[0].IsNull() )
+      //   continue;
+
+      // get a new normal for edge1
+      //bool ok;
+      gp_Vec dir1 = edge1->_normal, dir2 = edge2->_normal;
+      // if ( edge1->_cosin < 0 )
+      //   dir1 = getFaceDir( FF1[0], E1, edge1->_nodes[0], helper, ok ).Normalized();
+      // if ( edge2->_cosin < 0 )
+      //   dir2 = getFaceDir( FF2[0], E2, edge2->_nodes[0], helper, ok ).Normalized();
+
+      double cos1 = Abs( edge1->_cosin ), cos2 = Abs( edge2->_cosin );
+      double wgt1 = ( cos1 + 0.001 ) / ( cos1 + cos2 + 0.002 );
+      double wgt2 = ( cos2 + 0.001 ) / ( cos1 + cos2 + 0.002 );
+      newEdge._normal = ( wgt1 * dir1 + wgt2 * dir2 ).XYZ();
+      newEdge._normal.Normalize();
+
+      // cout << edge1->_nodes[0]->GetID() << " "
+      //      << edge2->_nodes[0]->GetID() << " NORM: "
+      //      << newEdge._normal.X() << ", " << newEdge._normal.Y() << ", " << newEdge._normal.Z() << endl;
+
+      // get new cosin
+      if ( cos1 < theMinSmoothCosin )
       {
-        const TopoDS_Face *F = (const TopoDS_Face*) fIt->next();
-        if ( helper.IsSubShape( *F, data._solid))
-          FF1[ FF1[0].IsNull() ? 0 : 1 ] = *F;
+        newEdge._cosin = edge2->_cosin;
       }
-      fIt = helper.GetAncestors(E2, *_mesh, TopAbs_FACE);
-      while ( fIt->more() && FF2[1].IsNull())
+      else if ( cos2 > theMinSmoothCosin ) // both cos1 and cos2 > theMinSmoothCosin
       {
-        const TopoDS_Face *F = (const TopoDS_Face*) fIt->next();
-        if ( helper.IsSubShape( *F, data._solid))
-          FF2[ FF2[0].IsNull() ? 0 : 1 ] = *F;
+        // gp_Vec dirInFace;
+        // if ( edge1->_cosin < 0 )
+        //   dirInFace = dir1;
+        // else
+        //   dirInFace = getFaceDir( FF1[0], E1, edge1->_nodes[0], helper, ok );
+        // double angle = dirInFace.Angle( edge1->_normal ); // [0,PI]
+        // edge1->SetCosin( Cos( angle ));
+        //newEdge._cosin = 0; // ???????????
+        newEdge._cosin = ( wgt1 * cos1 + wgt2 * cos2 ) * edge1->_cosin / cos1;
       }
-      // exclude a FACE common to E1 and E2 (put it at [1] in FF* )
-      if ( FF1[0].IsSame( FF2[0]) || FF1[0].IsSame( FF2[1]))
-        std::swap( FF1[0], FF1[1] );
-      if ( FF2[0].IsSame( FF1[0]) )
-        std::swap( FF2[0], FF2[1] );
-      if ( FF1[0].IsNull() || FF2[0].IsNull() )
-        continue;
-
-//       // get a new normal for edge1
-      bool ok;
-      gp_Vec dir1 = edge1->_normal, dir2 = edge2->_normal;
-      if ( edge1->_cosin < 0 )
-        dir1 = getFaceDir( FF1[0], E1, edge1->_nodes[0], helper, ok ).Normalized();
-      if ( edge2->_cosin < 0 )
-        dir2 = getFaceDir( FF2[0], E2, edge2->_nodes[0], helper, ok ).Normalized();
-      //      gp_Vec dir1 = getFaceDir( FF1[0], E1, edge1->_nodes[0], helper, ok );
-//       gp_Vec dir2 = getFaceDir( FF2[0], E2, edge2->_nodes[0], helper, ok2 );
-//       double wgt1 = ( edge1->_cosin + 1 ) / ( edge1->_cosin + edge2->_cosin + 2 );
-//       double wgt2 = ( edge2->_cosin + 1 ) / ( edge1->_cosin + edge2->_cosin + 2 );
-//       gp_Vec newNorm = wgt1 * dir1 + wgt2 * dir2;
-//       newNorm.Normalize();
-
-      double wgt1 = ( edge1->_cosin + 1 ) / ( edge1->_cosin + edge2->_cosin + 2 );
-      double wgt2 = ( edge2->_cosin + 1 ) / ( edge1->_cosin + edge2->_cosin + 2 );
-      gp_Vec newNorm = wgt1 * dir1 + wgt2 * dir2;
-      newNorm.Normalize();
-
-      edge1->_normal = newNorm.XYZ();
-
-      // update data of edge1 depending on _normal
-      const SMDS_MeshNode *n1, *n2;
-      n1 = edge1->_2neibors->_edges[0]->_nodes[0];
-      n2 = edge1->_2neibors->_edges[1]->_nodes[0];
-      //if ( !findNeiborsOnEdge( edge1, n1, n2, data ))
-      //continue;
-      edge1->SetDataByNeighbors( n1, n2, helper );
-      gp_Vec dirInFace;
-      if ( edge1->_cosin < 0 )
-        dirInFace = dir1;
       else
-        getFaceDir( FF1[0], E1, edge1->_nodes[0], helper, ok );
-      double angle = dir1.Angle( edge1->_normal ); // [0,PI]
-      edge1->SetCosin( cos( angle ));
+      {
+        newEdge._cosin = edge1->_cosin;
+      }
 
-      // limit data._stepSize
-      if ( edge1->_cosin > 0.1 )
+      // find shapes that need smoothing due to change of _normal
+      if ( edge1->_cosin  < theMinSmoothCosin &&
+           newEdge._cosin > theMinSmoothCosin )
       {
-        SMDS_ElemIteratorPtr fIt = edge1->_nodes[0]->GetInverseElementIterator(SMDSAbs_Face);
-        while ( fIt->more() )
-          limitStepSize( data, fIt->next(), edge1->_cosin );
+        if ( edge1->_sWOL.IsNull() )
+        {
+          SMDS_ElemIteratorPtr fIt = edge1->_nodes[0]->GetInverseElementIterator(SMDSAbs_Face);
+          while ( fIt->more() )
+            shapesToSmooth.insert( fIt->next()->getshapeId() );
+          //limitStepSize( data, fIt->next(), edge1->_cosin ); // too late
+        }
+        else // edge1 inflates along a FACE
+        {
+          TopoDS_Shape V = helper.GetSubShapeByNode( edge1->_nodes[0], getMeshDS() );
+          PShapeIteratorPtr eIt = helper.GetAncestors( V, *_mesh, TopAbs_EDGE );
+          while ( const TopoDS_Shape* E = eIt->next() )
+          {
+            if ( !helper.IsSubShape( *E, /*FACE=*/edge1->_sWOL ))
+              continue;
+            gp_Vec edgeDir = getEdgeDir( TopoDS::Edge( *E ), TopoDS::Vertex( V ));
+            double   angle = edgeDir.Angle( newEdge._normal ); // [0,PI]
+            if ( angle < M_PI / 2 )
+              shapesToSmooth.insert( getMeshDS()->ShapeToIndex( *E ));
+          }
+        }
       }
-      // set new XYZ of target node
+    }
+
+    data.AddShapesToSmooth( shapesToSmooth );
+
+    // Update data of edges depending on a new _normal
+
+    for ( size_t iE = 0; iE < edge2newEdge.size(); ++iE )
+    {
+      _LayerEdge*   edge1 = edge2newEdge[ iE ].first;
+      _LayerEdge& newEdge = edge2newEdge[ iE ].second;
+      if ( !edge1 ) continue;
+
+      edge1->_normal = newEdge._normal;
+      edge1->SetCosin( newEdge._cosin );
       edge1->InvalidateStep( 1 );
       edge1->_len = 0;
       edge1->SetNewLength( data._stepSize, helper );
-    }
+      if ( edge1->IsOnEdge() )
+      {
+        const SMDS_MeshNode * n1 = edge1->_2neibors->srcNode(0);
+        const SMDS_MeshNode * n2 = edge1->_2neibors->srcNode(1);
+        edge1->SetDataByNeighbors( n1, n2, helper );
+      }
 
-    // Update normals and other dependent data of not intersecting _LayerEdge's
-    // neighboring the intersecting ones
+      // Update normals and other dependent data of not intersecting _LayerEdge's
+      // neighboring the intersecting ones
 
-    for ( e2ee = edge2CloseEdge.begin(); e2ee != edge2CloseEdge.end(); ++e2ee )
-    {
-      _LayerEdge* edge1 = e2ee->first;
       if ( !edge1->_2neibors )
         continue;
       for ( int j = 0; j < 2; ++j ) // loop on 2 neighbors
@@ -2813,7 +3808,7 @@ bool _ViscousBuilder::updateNormals( _SolidData&         data,
         if ( edge2CloseEdge.count ( neighbor ))
           continue; // j-th neighbor is also intersected
         _LayerEdge* prevEdge = edge1;
-        const int nbSteps = 6;
+        const int nbSteps = 10;
         for ( int step = nbSteps; step; --step ) // step from edge1 in j-th direction
         {
           if ( !neighbor->_2neibors )
@@ -2822,8 +3817,6 @@ bool _ViscousBuilder::updateNormals( _SolidData&         data,
           _LayerEdge* nextEdge = neighbor->_2neibors->_edges[iNext];
           if ( nextEdge == prevEdge )
             nextEdge = neighbor->_2neibors->_edges[ ++iNext ];
-//           const double&  wgtPrev = neighbor->_2neibors->_wgt[1-iNext];
-//           const double&  wgtNext = neighbor->_2neibors->_wgt[iNext];
           double r = double(step-1)/nbSteps;
           if ( !nextEdge->_2neibors )
             r = 0.5;
@@ -2835,25 +3828,514 @@ bool _ViscousBuilder::updateNormals( _SolidData&         data,
           neighbor->SetCosin( prevEdge->_cosin * r + nextEdge->_cosin * (1-r) );
           neighbor->SetDataByNeighbors( prevEdge->_nodes[0], nextEdge->_nodes[0], helper );
 
-          neighbor->InvalidateStep( 1 );
-          neighbor->_len = 0;
-          neighbor->SetNewLength( data._stepSize, helper );
+          neighbor->InvalidateStep( 1 );
+          neighbor->_len = 0;
+          neighbor->SetNewLength( data._stepSize, helper );
+
+          // goto the next neighbor
+          prevEdge = neighbor;
+          neighbor = nextEdge;
+        }
+      }
+    }
+    dumpFunctionEnd();
+  }
+  // 2) Check absence of intersections
+  // TODO?
+
+  for ( size_t i = 0 ; i < tmpFaces.size(); ++i )
+    delete tmpFaces[i];
+
+  return true;
+}
+
+//================================================================================
+/*!
+ * \brief Modify normals of _LayerEdge's on _ConvexFace's
+ */
+//================================================================================
+
+bool _ViscousBuilder::updateNormalsOfConvexFaces( _SolidData&         data,
+                                                  SMESH_MesherHelper& helper,
+                                                  int                 stepNb )
+{
+  SMESHDS_Mesh* meshDS = helper.GetMeshDS();
+  bool isOK;
+
+  map< TGeomID, _ConvexFace >::iterator id2face = data._convexFaces.begin();
+  for ( ; id2face != data._convexFaces.end(); ++id2face )
+  {
+    _ConvexFace & convFace = (*id2face).second;
+    if ( convFace._normalsFixed )
+      continue; // already fixed
+    if ( convFace.CheckPrisms() )
+      continue; // nothing to fix
+
+    convFace._normalsFixed = true;
+
+    BRepAdaptor_Surface surface ( convFace._face, false );
+    BRepLProp_SLProps   surfProp( surface, 2, 1e-6 );
+
+    // check if the convex FACE is of spherical shape
+
+    Bnd_B3d centersBox; // bbox of centers of curvature of _LayerEdge's on VERTEXes
+    Bnd_B3d nodesBox;
+    gp_Pnt  center;
+    int     iBeg, iEnd;
+
+    map< TGeomID, int >::iterator id2end = convFace._subIdToEdgeEnd.begin();
+    for ( ; id2end != convFace._subIdToEdgeEnd.end(); ++id2end )
+    {
+      data.GetEdgesOnShape( id2end->second, iBeg, iEnd );
+
+      if ( meshDS->IndexToShape( id2end->first ).ShapeType() == TopAbs_VERTEX )
+      {
+        _LayerEdge* ledge = data._edges[ iBeg ];
+        if ( convFace.GetCenterOfCurvature( ledge, surfProp, helper, center ))
+          centersBox.Add( center );
+      }
+      for ( ; iBeg < iEnd; ++iBeg )
+        nodesBox.Add( SMESH_TNodeXYZ( data._edges[ iBeg ]->_nodes[0] ));
+    }
+    if ( centersBox.IsVoid() )
+    {
+      debugMsg( "Error: centersBox.IsVoid()" );
+      return false;
+    }
+    const bool isSpherical =
+      ( centersBox.SquareExtent() < 1e-6 * nodesBox.SquareExtent() );
+
+    int nbEdges = helper.Count( convFace._face, TopAbs_EDGE, /*ignoreSame=*/false );
+    vector < _CentralCurveOnEdge > centerCurves( nbEdges );
+
+    if ( isSpherical )
+    {
+      // set _LayerEdge::_normal as average of all normals
+
+      // WARNING: different density of nodes on EDGEs is not taken into account that
+      // can lead to an improper new normal
+
+      gp_XYZ avgNormal( 0,0,0 );
+      nbEdges = 0;
+      id2end = convFace._subIdToEdgeEnd.begin();
+      for ( ; id2end != convFace._subIdToEdgeEnd.end(); ++id2end )
+      {
+        data.GetEdgesOnShape( id2end->second, iBeg, iEnd );
+        // set data of _CentralCurveOnEdge
+        const TopoDS_Shape& S = meshDS->IndexToShape( id2end->first );
+        if ( S.ShapeType() == TopAbs_EDGE )
+        {
+          _CentralCurveOnEdge& ceCurve = centerCurves[ nbEdges++ ];
+          ceCurve.SetShapes( TopoDS::Edge(S), convFace, data, helper );
+          if ( !data._edges[ iBeg ]->_sWOL.IsNull() )
+            ceCurve._adjFace.Nullify();
+          else
+            ceCurve._ledges.insert( ceCurve._ledges.end(),
+                                    &data._edges[ iBeg ], &data._edges[ iEnd ]);
+        }
+        // summarize normals
+        for ( ; iBeg < iEnd; ++iBeg )
+          avgNormal += data._edges[ iBeg ]->_normal;
+      }
+      double normSize = avgNormal.SquareModulus();
+      if ( normSize < 1e-200 )
+      {
+        debugMsg( "updateNormalsOfConvexFaces(): zero avgNormal" );
+        return false;
+      }
+      avgNormal /= Sqrt( normSize );
+
+      // compute new _LayerEdge::_cosin on EDGEs
+      double avgCosin = 0;
+      int     nbCosin = 0;
+      gp_Vec inFaceDir;
+      for ( size_t iE = 0; iE < centerCurves.size(); ++iE )
+      {
+        _CentralCurveOnEdge& ceCurve = centerCurves[ iE ];
+        if ( ceCurve._adjFace.IsNull() )
+          continue;
+        for ( size_t iLE = 0; iLE < ceCurve._ledges.size(); ++iLE )
+        {
+          const SMDS_MeshNode* node = ceCurve._ledges[ iLE ]->_nodes[0];
+          inFaceDir = getFaceDir( ceCurve._adjFace, ceCurve._edge, node, helper, isOK );
+          if ( isOK )
+          {
+            double angle = inFaceDir.Angle( avgNormal ); // [0,PI]
+            ceCurve._ledges[ iLE ]->_cosin = Cos( angle );
+            avgCosin += ceCurve._ledges[ iLE ]->_cosin;
+            nbCosin++;
+          }
+        }
+      }
+      if ( nbCosin > 0 )
+        avgCosin /= nbCosin;
+
+      // set _LayerEdge::_normal = avgNormal
+      id2end = convFace._subIdToEdgeEnd.begin();
+      for ( ; id2end != convFace._subIdToEdgeEnd.end(); ++id2end )
+      {
+        data.GetEdgesOnShape( id2end->second, iBeg, iEnd );
+        const TopoDS_Shape& S = meshDS->IndexToShape( id2end->first );
+        if ( S.ShapeType() != TopAbs_EDGE )
+          for ( int i = iBeg; i < iEnd; ++i )
+            data._edges[ i ]->_cosin = avgCosin;
+
+        for ( ; iBeg < iEnd; ++iBeg )
+          data._edges[ iBeg ]->_normal = avgNormal;
+      }
+    }
+    else // if ( isSpherical )
+    {
+      // We suppose that centers of curvature at all points of the FACE
+      // lie on some curve, let's call it "central curve". For all _LayerEdge's
+      // having a common center of curvature we define the same new normal
+      // as a sum of normals of _LayerEdge's on EDGEs among them.
+
+      // get all centers of curvature for each EDGE
+
+      helper.SetSubShape( convFace._face );
+      _LayerEdge* vertexLEdges[2], **edgeLEdge, **edgeLEdgeEnd;
+
+      TopExp_Explorer edgeExp( convFace._face, TopAbs_EDGE );
+      for ( int iE = 0; edgeExp.More(); edgeExp.Next(), ++iE )
+      {
+        const TopoDS_Edge& edge = TopoDS::Edge( edgeExp.Current() );
+
+        // set adjacent FACE
+        centerCurves[ iE ].SetShapes( edge, convFace, data, helper );
+
+        // get _LayerEdge's of the EDGE
+        TGeomID edgeID = meshDS->ShapeToIndex( edge );
+        id2end = convFace._subIdToEdgeEnd.find( edgeID );
+        if ( id2end == convFace._subIdToEdgeEnd.end() )
+        {
+          // no _LayerEdge's on EDGE, use _LayerEdge's on VERTEXes
+          for ( int iV = 0; iV < 2; ++iV )
+          {
+            TopoDS_Vertex v = helper.IthVertex( iV, edge );
+            TGeomID     vID = meshDS->ShapeToIndex( v );
+            int  end = convFace._subIdToEdgeEnd[ vID ];
+            int iBeg = end > 0 ? data._endEdgeOnShape[ end-1 ] : 0;
+            vertexLEdges[ iV ] = data._edges[ iBeg ];
+          }
+          edgeLEdge    = &vertexLEdges[0];
+          edgeLEdgeEnd = edgeLEdge + 2;
+
+          centerCurves[ iE ]._adjFace.Nullify();
+        }
+        else
+        {
+          data.GetEdgesOnShape( id2end->second, iBeg, iEnd );
+          if ( id2end->second >= data._nbShapesToSmooth )
+            data.SortOnEdge( edge, iBeg, iEnd, helper );
+          edgeLEdge    = &data._edges[ iBeg ];
+          edgeLEdgeEnd = edgeLEdge + iEnd - iBeg;
+          vertexLEdges[0] = data._edges[ iBeg   ]->_2neibors->_edges[0];
+          vertexLEdges[1] = data._edges[ iEnd-1 ]->_2neibors->_edges[1];
+
+          if ( ! data._edges[ iBeg ]->_sWOL.IsNull() )
+            centerCurves[ iE ]._adjFace.Nullify();
+        }
+
+        // Get curvature centers
+
+        centersBox.Clear();
+
+        if ( edgeLEdge[0]->IsOnEdge() &&
+             convFace.GetCenterOfCurvature( vertexLEdges[0], surfProp, helper, center ))
+        { // 1st VERTEX
+          centerCurves[ iE ].Append( center, vertexLEdges[0] );
+          centersBox.Add( center );
+        }
+        for ( ; edgeLEdge < edgeLEdgeEnd; ++edgeLEdge )
+          if ( convFace.GetCenterOfCurvature( *edgeLEdge, surfProp, helper, center ))
+          { // EDGE or VERTEXes
+            centerCurves[ iE ].Append( center, *edgeLEdge );
+            centersBox.Add( center );
+          }
+        if ( edgeLEdge[-1]->IsOnEdge() &&
+             convFace.GetCenterOfCurvature( vertexLEdges[1], surfProp, helper, center ))
+        { // 2nd VERTEX
+          centerCurves[ iE ].Append( center, vertexLEdges[1] );
+          centersBox.Add( center );
+        }
+        centerCurves[ iE ]._isDegenerated =
+          ( centersBox.IsVoid() || centersBox.SquareExtent() < 1e-6 * nodesBox.SquareExtent() );
+
+      } // loop on EDGES of convFace._face to set up data of centerCurves
+
+      // Compute new normals for _LayerEdge's on EDGEs
+
+      double avgCosin = 0;
+      int     nbCosin = 0;
+      gp_Vec inFaceDir;
+      for ( size_t iE1 = 0; iE1 < centerCurves.size(); ++iE1 )
+      {
+        _CentralCurveOnEdge& ceCurve = centerCurves[ iE1 ];
+        if ( ceCurve._isDegenerated )
+          continue;
+        const vector< gp_Pnt >& centers = ceCurve._curvaCenters;
+        vector< gp_XYZ > &   newNormals = ceCurve._normals;
+        for ( size_t iC1 = 0; iC1 < centers.size(); ++iC1 )
+        {
+          isOK = false;
+          for ( size_t iE2 = 0; iE2 < centerCurves.size() && !isOK; ++iE2 )
+          {
+            if ( iE1 != iE2 )
+              isOK = centerCurves[ iE2 ].FindNewNormal( centers[ iC1 ], newNormals[ iC1 ]);
+          }
+          if ( isOK && !ceCurve._adjFace.IsNull() )
+          {
+            // compute new _LayerEdge::_cosin
+            const SMDS_MeshNode* node = ceCurve._ledges[ iC1 ]->_nodes[0];
+            inFaceDir = getFaceDir( ceCurve._adjFace, ceCurve._edge, node, helper, isOK );
+            if ( isOK )
+            {
+              double angle = inFaceDir.Angle( newNormals[ iC1 ] ); // [0,PI]
+              ceCurve._ledges[ iC1 ]->_cosin = Cos( angle );
+              avgCosin += ceCurve._ledges[ iC1 ]->_cosin;
+              nbCosin++;
+            }
+          }
+        }
+      }
+      // set new normals to _LayerEdge's of NOT degenerated central curves
+      for ( size_t iE = 0; iE < centerCurves.size(); ++iE )
+      {
+        if ( centerCurves[ iE ]._isDegenerated )
+          continue;
+        for ( size_t iLE = 0; iLE < centerCurves[ iE ]._ledges.size(); ++iLE )
+          centerCurves[ iE ]._ledges[ iLE ]->_normal = centerCurves[ iE ]._normals[ iLE ];
+      }
+      // set new normals to _LayerEdge's of     degenerated central curves
+      for ( size_t iE = 0; iE < centerCurves.size(); ++iE )
+      {
+        if ( !centerCurves[ iE ]._isDegenerated ||
+             centerCurves[ iE ]._ledges.size() < 3 )
+          continue;
+        // new normal is an average of new normals at VERTEXes that
+        // was computed on non-degenerated _CentralCurveOnEdge's
+        gp_XYZ newNorm = ( centerCurves[ iE ]._ledges.front()->_normal +
+                           centerCurves[ iE ]._ledges.back ()->_normal );
+        double sz = newNorm.Modulus();
+        if ( sz < 1e-200 )
+          continue;
+        newNorm /= sz;
+        double newCosin = ( 0.5 * centerCurves[ iE ]._ledges.front()->_cosin +
+                            0.5 * centerCurves[ iE ]._ledges.back ()->_cosin );
+        for ( size_t iLE = 1, nb = centerCurves[ iE ]._ledges.size() - 1; iLE < nb; ++iLE )
+        {
+          centerCurves[ iE ]._ledges[ iLE ]->_normal = newNorm;
+          centerCurves[ iE ]._ledges[ iLE ]->_cosin  = newCosin;
+        }
+      }
+
+      // Find new normals for _LayerEdge's based on FACE
+
+      if ( nbCosin > 0 )
+        avgCosin /= nbCosin;
+      const TGeomID faceID = meshDS->ShapeToIndex( convFace._face );
+      map< TGeomID, int >::iterator id2end = convFace._subIdToEdgeEnd.find( faceID );
+      if ( id2end != convFace._subIdToEdgeEnd.end() )
+      {
+        int iE = 0;
+        gp_XYZ newNorm;
+        data.GetEdgesOnShape( id2end->second, iBeg, iEnd );
+        for ( ; iBeg < iEnd; ++iBeg )
+        {
+          _LayerEdge* ledge = data._edges[ iBeg ];
+          if ( !convFace.GetCenterOfCurvature( ledge, surfProp, helper, center ))
+            continue;
+          for ( size_t i = 0; i < centerCurves.size(); ++i, ++iE )
+          {
+            iE = iE % centerCurves.size();
+            if ( centerCurves[ iE ]._isDegenerated )
+              continue;
+            newNorm.SetCoord( 0,0,0 );
+            if ( centerCurves[ iE ].FindNewNormal( center, newNorm ))
+            {
+              ledge->_normal = newNorm;
+              ledge->_cosin  = avgCosin;
+              break;
+            }
+          }
+        }
+      }
+
+    } // not a quasi-spherical FACE
+
+    // Update _LayerEdge's data according to a new normal
+
+    dumpFunction(SMESH_Comment("updateNormalsOfConvexFaces")<<data._index
+                 <<"_F"<<meshDS->ShapeToIndex( convFace._face ));
+
+    id2end = convFace._subIdToEdgeEnd.begin();
+    for ( ; id2end != convFace._subIdToEdgeEnd.end(); ++id2end )
+    {
+      data.GetEdgesOnShape( id2end->second, iBeg, iEnd );
+      for ( ; iBeg < iEnd; ++iBeg )
+      {
+        _LayerEdge* & ledge = data._edges[ iBeg ];
+        double len = ledge->_len;
+        ledge->InvalidateStep( stepNb + 1, /*restoreLength=*/true );
+        ledge->SetCosin( ledge->_cosin );
+        ledge->SetNewLength( len, helper );
+      }
+
+    } // loop on sub-shapes of convFace._face
+
+    // Find FACEs adjacent to convFace._face that got necessity to smooth
+    // as a result of normals modification
+
+    set< TGeomID > adjFacesToSmooth;
+    for ( size_t iE = 0; iE < centerCurves.size(); ++iE )
+    {
+      if ( centerCurves[ iE ]._adjFace.IsNull() ||
+           centerCurves[ iE ]._adjFaceToSmooth )
+        continue;
+      for ( size_t iLE = 0; iLE < centerCurves[ iE ]._ledges.size(); ++iLE )
+      {
+        if ( centerCurves[ iE ]._ledges[ iLE ]->_cosin > theMinSmoothCosin )
+        {
+          adjFacesToSmooth.insert( meshDS->ShapeToIndex( centerCurves[ iE ]._adjFace ));
+          break;
+        }
+      }
+    }
+    data.AddShapesToSmooth( adjFacesToSmooth );
+
+    dumpFunctionEnd();
+
+
+  } // loop on data._convexFaces
+
+  return true;
+}
+
+//================================================================================
+/*!
+ * \brief Finds a center of curvature of a surface at a _LayerEdge
+ */
+//================================================================================
+
+bool _ConvexFace::GetCenterOfCurvature( _LayerEdge*         ledge,
+                                        BRepLProp_SLProps&  surfProp,
+                                        SMESH_MesherHelper& helper,
+                                        gp_Pnt &            center ) const
+{
+  gp_XY uv = helper.GetNodeUV( _face, ledge->_nodes[0] );
+  surfProp.SetParameters( uv.X(), uv.Y() );
+  if ( !surfProp.IsCurvatureDefined() )
+    return false;
+
+  const double oriFactor = ( _face.Orientation() == TopAbs_REVERSED ? +1. : -1. );
+  double surfCurvatureMax = surfProp.MaxCurvature() * oriFactor;
+  double surfCurvatureMin = surfProp.MinCurvature() * oriFactor;
+  if ( surfCurvatureMin > surfCurvatureMax )
+    center = surfProp.Value().Translated( surfProp.Normal().XYZ() / surfCurvatureMin * oriFactor );
+  else
+    center = surfProp.Value().Translated( surfProp.Normal().XYZ() / surfCurvatureMax * oriFactor );
+
+  return true;
+}
+
+//================================================================================
+/*!
+ * \brief Check that prisms are not distorted
+ */
+//================================================================================
+
+bool _ConvexFace::CheckPrisms() const
+{
+  for ( size_t i = 0; i < _simplexTestEdges.size(); ++i )
+  {
+    const _LayerEdge* edge = _simplexTestEdges[i];
+    SMESH_TNodeXYZ tgtXYZ( edge->_nodes.back() );
+    for ( size_t j = 0; j < edge->_simplices.size(); ++j )
+      if ( !edge->_simplices[j].IsForward( edge->_nodes[0], &tgtXYZ ))
+      {
+        debugMsg( "Bad simplex of _simplexTestEdges ("
+                  << " "<< edge->_nodes[0]->GetID()<< " "<< tgtXYZ._node->GetID()
+                  << " "<< edge->_simplices[j]._nPrev->GetID()
+                  << " "<< edge->_simplices[j]._nNext->GetID() << " )" );
+        return false;
+      }
+  }
+  return true;
+}
+
+//================================================================================
+/*!
+ * \brief Try to compute a new normal by interpolating normals of _LayerEdge's
+ *        stored in this _CentralCurveOnEdge.
+ *  \param [in] center - curvature center of a point of another _CentralCurveOnEdge.
+ *  \param [in,out] newNormal - current normal at this point, to be redefined
+ *  \return bool - true if succeeded.
+ */
+//================================================================================
+
+bool _CentralCurveOnEdge::FindNewNormal( const gp_Pnt& center, gp_XYZ& newNormal )
+{
+  if ( this->_isDegenerated )
+    return false;
+
+  // find two centers the given one lies between
+
+  for ( size_t i = 0, nb = _curvaCenters.size()-1;  i < nb;  ++i )
+  {
+    double sl2 = 1.001 * _segLength2[ i ];
+
+    double d1 = center.SquareDistance( _curvaCenters[ i ]);
+    if ( d1 > sl2 )
+      continue;
+    
+    double d2 = center.SquareDistance( _curvaCenters[ i+1 ]);
+    if ( d2 > sl2 || d2 + d1 < 1e-100 )
+      continue;
+
+    d1 = Sqrt( d1 );
+    d2 = Sqrt( d2 );
+    double r = d1 / ( d1 + d2 );
+    gp_XYZ norm = (( 1. - r ) * _ledges[ i   ]->_normal +
+                   (      r ) * _ledges[ i+1 ]->_normal );
+    norm.Normalize();
 
-          // goto the next neighbor
-          prevEdge = neighbor;
-          neighbor = nextEdge;
-        }
-      }
-    }
-    dumpFunctionEnd();
+    newNormal += norm;
+    double sz = newNormal.Modulus();
+    if ( sz < 1e-200 )
+      break;
+    newNormal /= sz;
+    return true;
   }
-  // 2) Check absence of intersections
-  // TODO?
+  return false;
+}
 
-  for ( unsigned i = 0 ; i < tmpFaces.size(); ++i )
-    delete tmpFaces[i];
+//================================================================================
+/*!
+ * \brief Set shape members
+ */
+//================================================================================
 
-  return true;
+void _CentralCurveOnEdge::SetShapes( const TopoDS_Edge&  edge,
+                                     const _ConvexFace&  convFace,
+                                     const _SolidData&   data,
+                                     SMESH_MesherHelper& helper)
+{
+  _edge = edge;
+
+  PShapeIteratorPtr fIt = helper.GetAncestors( edge, *helper.GetMesh(), TopAbs_FACE );
+  while ( const TopoDS_Shape* F = fIt->next())
+    if ( !convFace._face.IsSame( *F ))
+    {
+      _adjFace = TopoDS::Face( *F );
+      _adjFaceToSmooth = false;
+      // _adjFace already in a smoothing queue ?
+      size_t end;
+      TGeomID adjFaceID = helper.GetMeshDS()->ShapeToIndex( *F );
+      if ( data.GetShapeEdges( adjFaceID, end ))
+        _adjFaceToSmooth = ( end < data._nbShapesToSmooth );
+      break;
+    }
 }
 
 //================================================================================
@@ -2876,7 +4358,7 @@ bool _LayerEdge::FindIntersection( SMESH_ElementSearcher&   searcher,
   bool segmentIntersected = false;
   distance = Precision::Infinite();
   int iFace = -1; // intersected face
-  for ( unsigned j = 0 ; j < suspectFaces.size() && !segmentIntersected; ++j )
+  for ( size_t j = 0 ; j < suspectFaces.size() /*&& !segmentIntersected*/; ++j )
   {
     const SMDS_MeshElement* face = suspectFaces[j];
     if ( face->GetNodeIndex( _nodes.back() ) >= 0 ||
@@ -2894,7 +4376,7 @@ bool _LayerEdge::FindIntersection( SMESH_ElementSearcher&   searcher,
     {
       const SMDS_MeshNode* tria[3];
       tria[0] = *nIt++;
-      tria[1] = *nIt++;;
+      tria[1] = *nIt++;
       for ( int n2 = 2; n2 < nbNodes && !intFound; ++n2 )
       {
         tria[2] = *nIt++;
@@ -2904,21 +4386,14 @@ bool _LayerEdge::FindIntersection( SMESH_ElementSearcher&   searcher,
     }
     if ( intFound )
     {
-      if ( dist < segLen*(1.01) && dist > -(_len-segLen) )
+      if ( dist < segLen*(1.01) && dist > -(_len*_lenFactor-segLen) )
         segmentIntersected = true;
       if ( distance > dist )
         distance = dist, iFace = j;
     }
   }
   if ( iFace != -1 && face ) *face = suspectFaces[iFace];
-//   if ( distance && iFace > -1 )
-//   {
-//     // distance is used to limit size of inflation step which depends on
-//     // whether the intersected face bears viscous layers or not
-//     bool faceHasVL = suspectFaces[iFace]->GetID() < 1;
-//     if ( faceHasVL )
-//       *distance /= 2;
-//   }
+
   if ( segmentIntersected )
   {
 #ifdef __myDEBUG
@@ -2993,6 +4468,31 @@ gp_Ax1 _LayerEdge::LastSegment(double& segLen) const
   return segDir;
 }
 
+//================================================================================
+/*!
+ * \brief Return the last position of the target node on a FACE. 
+ *  \param [in] F - the FACE this _LayerEdge is inflated along
+ *  \return gp_XY - result UV
+ */
+//================================================================================
+
+gp_XY _LayerEdge::LastUV( const TopoDS_Face& F ) const
+{
+  if ( F.IsSame( _sWOL )) // F is my FACE
+    return gp_XY( _pos.back().X(), _pos.back().Y() );
+
+  if ( _sWOL.IsNull() || _sWOL.ShapeType() != TopAbs_EDGE ) // wrong call
+    return gp_XY( 1e100, 1e100 );
+
+  // _sWOL is EDGE of F; _pos.back().X() is the last U on the EDGE
+  double f, l, u = _pos.back().X();
+  Handle(Geom2d_Curve) C2d = BRep_Tool::CurveOnSurface( TopoDS::Edge(_sWOL), F, f,l);
+  if ( !C2d.IsNull() && f <= u && u <= l )
+    return C2d->Value( u ).XY();
+
+  return gp_XY( 1e100, 1e100 );
+}
+
 //================================================================================
 /*!
  * \brief Test intersection of the last segment with a given triangle
@@ -3020,9 +4520,9 @@ bool _LayerEdge::SegTriaInter( const gp_Ax1&        lastSegment,
   /* calculate distance from vert0 to ray origin */
   gp_XYZ tvec = orig - vert0;
 
-  if ( tvec * dir > EPSILON )
+  //if ( tvec * dir > EPSILON )
     // intersected face is at back side of the temporary face this _LayerEdge belongs to
-    return false;
+    //return false;
 
   gp_XYZ edge1 = vert1 - vert0;
   gp_XYZ edge2 = vert2 - vert0;
@@ -3034,52 +4534,29 @@ bool _LayerEdge::SegTriaInter( const gp_Ax1&        lastSegment,
   double det = edge1 * pvec;
 
   if (det > -EPSILON && det < EPSILON)
-    return 0;
+    return false;
   double inv_det = 1.0 / det;
 
   /* calculate U parameter and test bounds */
   double u = ( tvec * pvec ) * inv_det;
-  if (u < 0.0 || u > 1.0)
-    return 0;
+  //if (u < 0.0 || u > 1.0)
+  if (u < -EPSILON || u > 1.0 + EPSILON)
+    return false;
 
   /* prepare to test V parameter */
   gp_XYZ qvec = tvec ^ edge1;
 
   /* calculate V parameter and test bounds */
   double v = (dir * qvec) * inv_det;
-  if ( v < 0.0 || u + v > 1.0 )
-    return 0;
+  //if ( v < 0.0 || u + v > 1.0 )
+  if ( v < -EPSILON || u + v > 1.0 + EPSILON)
+    return false;
 
   /* calculate t, ray intersects triangle */
   t = (edge2 * qvec) * inv_det;
 
-  //   if (det < EPSILON)
-  //     return false;
-
-  //   /* calculate distance from vert0 to ray origin */
-  //   gp_XYZ tvec = orig - vert0;
-
-  //   /* calculate U parameter and test bounds */
-  //   double u = tvec * pvec;
-  //   if (u < 0.0 || u > det)
-//     return 0;
-
-//   /* prepare to test V parameter */
-//   gp_XYZ qvec = tvec ^ edge1;
-
-//   /* calculate V parameter and test bounds */
-//   double v = dir * qvec;
-//   if (v < 0.0 || u + v > det)
-//     return 0;
-
-//   /* calculate t, scale parameters, ray intersects triangle */
-//   double t = edge2 * qvec;
-//   double inv_det = 1.0 / det;
-//   t *= inv_det;
-//   //u *= inv_det;
-//   //v *= inv_det;
-
-  return true;
+  //return true;
+  return t > 0.;
 }
 
 //================================================================================
@@ -3099,9 +4576,9 @@ bool _LayerEdge::SmoothOnEdge(Handle(Geom_Surface)& surface,
   SMESH_TNodeXYZ oldPos( tgtNode );
   double dist01, distNewOld;
   
-  SMESH_TNodeXYZ p0( _2neibors->_nodes[0]);
-  SMESH_TNodeXYZ p1( _2neibors->_nodes[1]);
-  dist01 = p0.Distance( _2neibors->_nodes[1] );
+  SMESH_TNodeXYZ p0( _2neibors->tgtNode(0));
+  SMESH_TNodeXYZ p1( _2neibors->tgtNode(1));
+  dist01 = p0.Distance( _2neibors->tgtNode(1) );
 
   gp_Pnt newPos = p0 * _2neibors->_wgt[0] + p1 * _2neibors->_wgt[1];
   double lenDelta = 0;
@@ -3164,34 +4641,34 @@ bool _LayerEdge::Smooth(int& badNb)
 
   // compute new position for the last _pos
   gp_XYZ newPos (0,0,0);
-  for ( unsigned i = 0; i < _simplices.size(); ++i )
+  for ( size_t i = 0; i < _simplices.size(); ++i )
     newPos += SMESH_TNodeXYZ( _simplices[i]._nPrev );
   newPos /= _simplices.size();
 
+  const gp_XYZ& curPos ( _pos.back() );
+  const gp_Pnt  prevPos( _pos[ _pos.size()-2 ]);
   if ( _curvature )
-    newPos += _normal * _curvature->lenDelta( _len );
-
-  gp_Pnt prevPos( _pos[ _pos.size()-2 ]);
-//   if ( _cosin < -0.1)
-//   {
-//     // Avoid decreasing length of edge on concave surface
-//     //gp_Vec oldMove( _pos[ _pos.size()-2 ], _pos.back() );
-//     gp_Vec newMove( prevPos, newPos );
-//     newPos = _pos.back() + newMove.XYZ();
-//   }
-//   else if ( _cosin > 0.3 )
-//   {
-//     // Avoid increasing length of edge too much
-
-//   }
+  {
+    double delta  = _curvature->lenDelta( _len );
+    if ( delta > 0 )
+      newPos += _normal * delta;
+    else
+    {
+      double segLen = _normal * ( newPos - prevPos.XYZ() );
+      if ( segLen + delta > 0 )
+        newPos += _normal * delta;
+    }
+    // double segLenChange = _normal * ( curPos - newPos );
+    // newPos += 0.5 * _normal * segLenChange;
+  }
+
   // count quality metrics (orientation) of tetras around _tgtNode
   int nbOkBefore = 0;
-  SMESH_TNodeXYZ tgtXYZ( _nodes.back() );
-  for ( unsigned i = 0; i < _simplices.size(); ++i )
-    nbOkBefore += _simplices[i].IsForward( _nodes[0], &tgtXYZ );
+  for ( size_t i = 0; i < _simplices.size(); ++i )
+    nbOkBefore += _simplices[i].IsForward( _nodes[0], &curPos );
 
   int nbOkAfter = 0;
-  for ( unsigned i = 0; i < _simplices.size(); ++i )
+  for ( size_t i = 0; i < _simplices.size(); ++i )
     nbOkAfter += _simplices[i].IsForward( _nodes[0], &newPos );
 
   if ( nbOkAfter < nbOkBefore )
@@ -3241,17 +4718,23 @@ void _LayerEdge::SetNewLength( double len, SMESH_MesherHelper& helper )
       double u = Precision::Infinite(); // to force projection w/o distance check
       helper.CheckNodeU( TopoDS::Edge( _sWOL ), n, u, 1e-10, /*force=*/true, distXYZ );
       _pos.back().SetCoord( u, 0, 0 );
-      SMDS_EdgePosition* pos = static_cast<SMDS_EdgePosition*>( n->GetPosition() );
-      pos->SetUParameter( u );
+      if ( _nodes.size() > 1 )
+      {
+        SMDS_EdgePosition* pos = static_cast<SMDS_EdgePosition*>( n->GetPosition() );
+        pos->SetUParameter( u );
+      }
     }
     else //  TopAbs_FACE
     {
       gp_XY uv( Precision::Infinite(), 0 );
       helper.CheckNodeUV( TopoDS::Face( _sWOL ), n, uv, 1e-10, /*force=*/true, distXYZ );
       _pos.back().SetCoord( uv.X(), uv.Y(), 0 );
-      SMDS_FacePosition* pos = static_cast<SMDS_FacePosition*>( n->GetPosition() );
-      pos->SetUParameter( uv.X() );
-      pos->SetVParameter( uv.Y() );
+      if ( _nodes.size() > 1 )
+      {
+        SMDS_FacePosition* pos = static_cast<SMDS_FacePosition*>( n->GetPosition() );
+        pos->SetUParameter( uv.X() );
+        pos->SetVParameter( uv.Y() );
+      }
     }
     n->setXYZ( distXYZ[1], distXYZ[2], distXYZ[3]);
   }
@@ -3264,10 +4747,13 @@ void _LayerEdge::SetNewLength( double len, SMESH_MesherHelper& helper )
  */
 //================================================================================
 
-void _LayerEdge::InvalidateStep( int curStep )
+void _LayerEdge::InvalidateStep( int curStep, bool restoreLength )
 {
   if ( _pos.size() > curStep )
   {
+    if ( restoreLength )
+      _len -= ( _pos[ curStep-1 ] - _pos.back() ).Modulus();
+
     _pos.resize( curStep );
     gp_Pnt nXYZ = _pos.back();
     SMDS_MeshNode* n = const_cast< SMDS_MeshNode*>( _nodes.back() );
@@ -3312,19 +4798,28 @@ bool _ViscousBuilder::refine(_SolidData& data)
   Handle(Geom_Surface) surface;
   TopoDS_Edge geomEdge;
   TopoDS_Face geomFace;
+  TopoDS_Shape prevSWOL;
   TopLoc_Location loc;
-  double f,l, u/*, distXYZ[4]*/;
+  double f,l, u;
   gp_XY uv;
   bool isOnEdge;
+  TGeomID prevBaseId = -1;
+  TNode2Edge* n2eMap = 0;
+  TNode2Edge::iterator n2e;
+
+  // Create intermediate nodes on each _LayerEdge
 
-  for ( unsigned i = 0; i < data._edges.size(); ++i )
+  for ( size_t i = 0; i < data._edges.size(); ++i )
   {
     _LayerEdge& edge = *data._edges[i];
 
+    if ( edge._nodes.size() < 2 )
+      continue; // on _noShrinkShapes
+
     // get accumulated length of segments
     vector< double > segLen( edge._pos.size() );
     segLen[0] = 0.0;
-    for ( unsigned j = 1; j < edge._pos.size(); ++j )
+    for ( size_t j = 1; j < edge._pos.size(); ++j )
       segLen[j] = segLen[j-1] + (edge._pos[j-1] - edge._pos[j] ).Modulus();
 
     // allocate memory for new nodes if it is not yet refined
@@ -3335,27 +4830,47 @@ bool _ViscousBuilder::refine(_SolidData& data)
       edge._nodes[1] = 0;
       edge._nodes.back() = tgtNode;
     }
-    if ( !edge._sWOL.IsNull() )
+    // get data of a shrink shape
+    if ( !edge._sWOL.IsNull() && edge._sWOL != prevSWOL )
     {
       isOnEdge = ( edge._sWOL.ShapeType() == TopAbs_EDGE );
-      // restore position of the last node
-//       gp_Pnt p;
       if ( isOnEdge )
       {
         geomEdge = TopoDS::Edge( edge._sWOL );
-        curve = BRep_Tool::Curve( geomEdge, loc, f,l);
-//         double u = helper.GetNodeU( tgtNode );
-//         p = curve->Value( u );
+        curve    = BRep_Tool::Curve( geomEdge, loc, f,l);
       }
       else
       {
         geomFace = TopoDS::Face( edge._sWOL );
-        surface = BRep_Tool::Surface( geomFace, loc );
-//         gp_XY uv = helper.GetNodeUV( tgtNode );
-//         p = surface->Value( uv.X(), uv.Y() );
+        surface  = BRep_Tool::Surface( geomFace, loc );
+      }
+      prevSWOL = edge._sWOL;
+    }
+    // restore shapePos of the last node by already treated _LayerEdge of another _SolidData
+    const TGeomID baseShapeId = edge._nodes[0]->getshapeId();
+    if ( baseShapeId != prevBaseId )
+    {
+      map< TGeomID, TNode2Edge* >::iterator s2ne = data._s2neMap.find( baseShapeId );
+      n2eMap = ( s2ne == data._s2neMap.end() ) ? 0 : n2eMap = s2ne->second;
+      prevBaseId = baseShapeId;
+    }
+    _LayerEdge* edgeOnSameNode = 0;
+    if ( n2eMap && (( n2e = n2eMap->find( edge._nodes[0] )) != n2eMap->end() ))
+    {
+      edgeOnSameNode = n2e->second;
+      const gp_XYZ& otherTgtPos = edgeOnSameNode->_pos.back();
+      SMDS_PositionPtr  lastPos = tgtNode->GetPosition();
+      if ( isOnEdge )
+      {
+        SMDS_EdgePosition* epos = static_cast<SMDS_EdgePosition*>( lastPos );
+        epos->SetUParameter( otherTgtPos.X() );
+      }
+      else
+      {
+        SMDS_FacePosition* fpos = static_cast<SMDS_FacePosition*>( lastPos );
+        fpos->SetUParameter( otherTgtPos.X() );
+        fpos->SetVParameter( otherTgtPos.Y() );
       }
-//       p.Transform( loc );
-//       const_cast< SMDS_MeshNode* >( tgtNode )->setXYZ( p.X(), p.Y(), p.Z() );
     }
     // calculate height of the first layer
     double h0;
@@ -3372,8 +4887,8 @@ bool _ViscousBuilder::refine(_SolidData& data)
 
     // create intermediate nodes
     double hSum = 0, hi = h0/f;
-    unsigned iSeg = 1;
-    for ( unsigned iStep = 1; iStep < edge._nodes.size(); ++iStep )
+    size_t iSeg = 1;
+    for ( size_t iStep = 1; iStep < edge._nodes.size(); ++iStep )
     {
       // compute an intermediate position
       hi *= f;
@@ -3386,19 +4901,21 @@ bool _ViscousBuilder::refine(_SolidData& data)
       double r = ( segLen[iSeg] - hSum ) / ( segLen[iSeg] - segLen[iPrevSeg] );
       gp_Pnt pos = r * edge._pos[iPrevSeg] + (1-r) * edge._pos[iSeg];
 
-      SMDS_MeshNode*& node = const_cast< SMDS_MeshNode*& >(edge._nodes[ iStep ]);
+      SMDS_MeshNode*& node = const_cast< SMDS_MeshNode*& >( edge._nodes[ iStep ]);
       if ( !edge._sWOL.IsNull() )
       {
         // compute XYZ by parameters <pos>
         if ( isOnEdge )
         {
           u = pos.X();
-          pos = curve->Value( u ).Transformed(loc);
+          if ( !node )
+            pos = curve->Value( u ).Transformed(loc);
         }
         else
         {
           uv.SetCoord( pos.X(), pos.Y() );
-          pos = surface->Value( pos.X(), pos.Y() ).Transformed(loc);
+          if ( !node )
+            pos = surface->Value( pos.X(), pos.Y() ).Transformed(loc);
         }
       }
       // create or update the node
@@ -3426,71 +4943,191 @@ bool _ViscousBuilder::refine(_SolidData& data)
           {
             u = 0.5 * ( u + helper.GetNodeU( geomEdge, node ));
             pos = curve->Value( u ).Transformed(loc);
+
+            SMDS_EdgePosition* epos = static_cast<SMDS_EdgePosition*>( node->GetPosition() );
+            epos->SetUParameter( u );
           }
           else
           {
             uv = 0.5 * ( uv + helper.GetNodeUV( geomFace, node ));
             pos = surface->Value( uv.X(), uv.Y()).Transformed(loc);
+
+            SMDS_FacePosition* fpos = static_cast<SMDS_FacePosition*>( node->GetPosition() );
+            fpos->SetUParameter( uv.X() );
+            fpos->SetVParameter( uv.Y() );
           }
         }
         node->setXYZ( pos.X(), pos.Y(), pos.Z() );
       }
+    } // loop on edge._nodes
+
+    if ( !edge._sWOL.IsNull() ) // prepare for shrink()
+    {
+      if ( isOnEdge )
+        edge._pos.back().SetCoord( u, 0,0);
+      else
+        edge._pos.back().SetCoord( uv.X(), uv.Y() ,0);
+
+      if ( edgeOnSameNode )
+        edgeOnSameNode->_pos.back() = edge._pos.back();
     }
-  }
+
+  } // loop on data._edges to create nodes
 
   if ( !getMeshDS()->IsEmbeddedMode() )
     // Log node movement
-    for ( unsigned i = 0; i < data._edges.size(); ++i )
+    for ( size_t i = 0; i < data._edges.size(); ++i )
     {
       _LayerEdge& edge = *data._edges[i];
       SMESH_TNodeXYZ p ( edge._nodes.back() );
       getMeshDS()->MoveNode( p._node, p.X(), p.Y(), p.Z() );
     }
 
-  // TODO: make quadratic prisms and polyhedrons(?)
+  // Create volumes
 
   helper.SetElementsOnShape(true);
 
+  vector< vector<const SMDS_MeshNode*>* > nnVec;
+  set< vector<const SMDS_MeshNode*>* >    nnSet;
+  set< int > degenEdgeInd;
+  vector<const SMDS_MeshElement*> degenVols;
+
   TopExp_Explorer exp( data._solid, TopAbs_FACE );
   for ( ; exp.More(); exp.Next() )
   {
-    if ( _ignoreShapeIds.count( getMeshDS()->ShapeToIndex( exp.Current() )))
+    if ( data._ignoreFaceIds.count( getMeshDS()->ShapeToIndex( exp.Current() )))
       continue;
-    SMESHDS_SubMesh* fSubM = getMeshDS()->MeshElements( exp.Current() );
+    SMESHDS_SubMesh*   fSubM = getMeshDS()->MeshElements( exp.Current() );
     SMDS_ElemIteratorPtr fIt = fSubM->GetElements();
-    vector< vector<const SMDS_MeshNode*>* > nnVec;
     while ( fIt->more() )
     {
       const SMDS_MeshElement* face = fIt->next();
-      int nbNodes = face->NbCornerNodes();
+      const int            nbNodes = face->NbCornerNodes();
       nnVec.resize( nbNodes );
-      SMDS_ElemIteratorPtr nIt = face->nodesIterator();
+      nnSet.clear();
+      degenEdgeInd.clear();
+      int nbZ = 0;
+      SMDS_NodeIteratorPtr nIt = face->nodeIterator();
       for ( int iN = 0; iN < nbNodes; ++iN )
       {
-        const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
+        const SMDS_MeshNode* n = nIt->next();
         nnVec[ iN ] = & data._n2eMap[ n ]->_nodes;
+        if ( nnVec[ iN ]->size() < 2 )
+          degenEdgeInd.insert( iN );
+        else
+          nbZ = nnVec[ iN ]->size();
+
+        if ( helper.HasDegeneratedEdges() )
+          nnSet.insert( nnVec[ iN ]);
       }
+      if ( nbZ == 0 )
+        continue;
+      if ( 0 < nnSet.size() && nnSet.size() < 3 )
+        continue;
 
-      int nbZ = nnVec[0]->size();
       switch ( nbNodes )
       {
       case 3:
-        for ( int iZ = 1; iZ < nbZ; ++iZ )
-          helper.AddVolume( (*nnVec[0])[iZ-1], (*nnVec[1])[iZ-1], (*nnVec[2])[iZ-1],
-                            (*nnVec[0])[iZ],   (*nnVec[1])[iZ],   (*nnVec[2])[iZ]);
+        switch ( degenEdgeInd.size() )
+        {
+        case 0: // PENTA
+        {
+          for ( int iZ = 1; iZ < nbZ; ++iZ )
+            helper.AddVolume( (*nnVec[0])[iZ-1], (*nnVec[1])[iZ-1], (*nnVec[2])[iZ-1],
+                              (*nnVec[0])[iZ],   (*nnVec[1])[iZ],   (*nnVec[2])[iZ]);
+          break;
+        }
+        case 1: // PYRAM
+        {
+          int i2 = *degenEdgeInd.begin();
+          int i0 = helper.WrapIndex( i2 - 1, nbNodes );
+          int i1 = helper.WrapIndex( i2 + 1, nbNodes );
+          for ( int iZ = 1; iZ < nbZ; ++iZ )
+            helper.AddVolume( (*nnVec[i0])[iZ-1], (*nnVec[i1])[iZ-1],
+                              (*nnVec[i1])[iZ],   (*nnVec[i0])[iZ],   (*nnVec[i2])[0]);
+          break;
+        }
+        case 2: // TETRA
+        {
+          int i3 = !degenEdgeInd.count(0) ? 0 : !degenEdgeInd.count(1) ? 1 : 2;
+          for ( int iZ = 1; iZ < nbZ; ++iZ )
+            helper.AddVolume( (*nnVec[0])[iZ-1], (*nnVec[1])[iZ-1], (*nnVec[2])[iZ-1],
+                              (*nnVec[i3])[iZ]);
+          break;
+        }
+        }
         break;
+
       case 4:
-        for ( int iZ = 1; iZ < nbZ; ++iZ )
-          helper.AddVolume( (*nnVec[0])[iZ-1], (*nnVec[1])[iZ-1],
-                            (*nnVec[2])[iZ-1], (*nnVec[3])[iZ-1],
-                            (*nnVec[0])[iZ],   (*nnVec[1])[iZ],
-                            (*nnVec[2])[iZ],   (*nnVec[3])[iZ]);
+        switch ( degenEdgeInd.size() )
+        {
+        case 0: // HEX
+        {
+          for ( int iZ = 1; iZ < nbZ; ++iZ )
+            helper.AddVolume( (*nnVec[0])[iZ-1], (*nnVec[1])[iZ-1],
+                              (*nnVec[2])[iZ-1], (*nnVec[3])[iZ-1],
+                              (*nnVec[0])[iZ],   (*nnVec[1])[iZ],
+                              (*nnVec[2])[iZ],   (*nnVec[3])[iZ]);
+          break;
+        }
+        case 2: // PENTA?
+        {
+          int i2 = *degenEdgeInd.begin();
+          int i3 = *degenEdgeInd.rbegin();
+          bool ok = ( i3 - i2 == 1 );
+          if ( i2 == 0 && i3 == 3 ) { i2 = 3; i3 = 0; ok = true; }
+          int i0 = helper.WrapIndex( i3 + 1, nbNodes );
+          int i1 = helper.WrapIndex( i0 + 1, nbNodes );
+          for ( int iZ = 1; iZ < nbZ; ++iZ )
+          {
+            const SMDS_MeshElement* vol =
+              helper.AddVolume( (*nnVec[i3])[0], (*nnVec[i0])[iZ], (*nnVec[i0])[iZ-1],
+                                (*nnVec[i2])[0], (*nnVec[i1])[iZ], (*nnVec[i1])[iZ-1]);
+            if ( !ok && vol )
+              degenVols.push_back( vol );
+          }
+          break;
+        }
+        case 3: // degen HEX
+        {
+          const SMDS_MeshNode* nn[8];
+          for ( int iZ = 1; iZ < nbZ; ++iZ )
+          {
+            const SMDS_MeshElement* vol =
+              helper.AddVolume( nnVec[0]->size() > 1 ? (*nnVec[0])[iZ-1] : (*nnVec[0])[0],
+                                nnVec[1]->size() > 1 ? (*nnVec[1])[iZ-1] : (*nnVec[1])[0],
+                                nnVec[2]->size() > 1 ? (*nnVec[2])[iZ-1] : (*nnVec[2])[0],
+                                nnVec[3]->size() > 1 ? (*nnVec[3])[iZ-1] : (*nnVec[3])[0],
+                                nnVec[0]->size() > 1 ? (*nnVec[0])[iZ]   : (*nnVec[0])[0],
+                                nnVec[1]->size() > 1 ? (*nnVec[1])[iZ]   : (*nnVec[1])[0],
+                                nnVec[2]->size() > 1 ? (*nnVec[2])[iZ]   : (*nnVec[2])[0],
+                                nnVec[3]->size() > 1 ? (*nnVec[3])[iZ]   : (*nnVec[3])[0]);
+            degenVols.push_back( vol );
+          }
+        }
+        break;
+        }
         break;
+
       default:
         return error("Not supported type of element", data._index);
-      }
+
+      } // switch ( nbNodes )
+    } // while ( fIt->more() )
+  } // loop on FACEs
+
+  if ( !degenVols.empty() )
+  {
+    SMESH_ComputeErrorPtr& err = _mesh->GetSubMesh( data._solid )->GetComputeError();
+    if ( !err || err->IsOK() )
+    {
+      err.reset( new SMESH_ComputeError( COMPERR_WARNING,
+                                         "Degenerated volumes created" ));
+      err->myBadElements.insert( err->myBadElements.end(),
+                                 degenVols.begin(),degenVols.end() );
     }
   }
+
   return true;
 }
 
@@ -3505,7 +5142,7 @@ bool _ViscousBuilder::shrink()
   // make map of (ids of FACEs to shrink mesh on) to (_SolidData containing _LayerEdge's
   // inflated along FACE or EDGE)
   map< TGeomID, _SolidData* > f2sdMap;
-  for ( unsigned i = 0 ; i < _sdVec.size(); ++i )
+  for ( size_t i = 0 ; i < _sdVec.size(); ++i )
   {
     _SolidData& data = _sdVec[i];
     TopTools_MapOfShape FFMap;
@@ -3536,20 +5173,19 @@ bool _ViscousBuilder::shrink()
 
   // EDGE's to shrink
   map< TGeomID, _Shrinker1D > e2shrMap;
+  vector< _LayerEdge* > lEdges;
 
   // loop on FACES to srink mesh on
   map< TGeomID, _SolidData* >::iterator f2sd = f2sdMap.begin();
   for ( ; f2sd != f2sdMap.end(); ++f2sd )
   {
-    _SolidData&     data = *f2sd->second;
-    TNode2Edge&   n2eMap = data._n2eMap;
-    const TopoDS_Face& F = TopoDS::Face( getMeshDS()->IndexToShape( f2sd->first ));
-
-    Handle(Geom_Surface) surface = BRep_Tool::Surface(F);
-
+    _SolidData&      data = *f2sd->second;
+    const TopoDS_Face&  F = TopoDS::Face( getMeshDS()->IndexToShape( f2sd->first ));
     SMESH_subMesh*     sm = _mesh->GetSubMesh( F );
     SMESHDS_SubMesh* smDS = sm->GetSubMeshDS();
 
+    Handle(Geom_Surface) surface = BRep_Tool::Surface(F);
+
     helper.SetSubShape(F);
 
     // ===========================
@@ -3584,29 +5220,43 @@ bool _ViscousBuilder::shrink()
     }
 
     // Find _LayerEdge's inflated along F
-    vector< _LayerEdge* > lEdges;
+    lEdges.clear();
     {
-      SMESH_subMeshIteratorPtr subIt =
-        sm->getDependsOnIterator(/*includeSelf=*/false, /*complexShapeFirst=*/false);
+      set< TGeomID > subIDs;
+      SMESH_subMeshIteratorPtr subIt = sm->getDependsOnIterator(/*includeSelf=*/false);
       while ( subIt->more() )
+        subIDs.insert( subIt->next()->GetId() );
+
+      int iBeg, iEnd = 0;
+      for ( int iS = 0; iS < data._endEdgeOnShape.size() && !subIDs.empty(); ++iS )
       {
-        SMESH_subMesh*     sub = subIt->next();
-        SMESHDS_SubMesh* subDS = sub->GetSubMeshDS();
-        if ( subDS->NbNodes() == 0 || !n2eMap.count( subDS->GetNodes()->next() ))
-          continue;
-        SMDS_NodeIteratorPtr nIt = subDS->GetNodes();
-        while ( nIt->more() )
-        {
-          _LayerEdge* edge = n2eMap[ nIt->next() ];
-          lEdges.push_back( edge );
-          prepareEdgeToShrink( *edge, F, helper, smDS );
-        }
+        iBeg = iEnd;
+        iEnd = data._endEdgeOnShape[ iS ];
+        TGeomID shapeID = data._edges[ iBeg ]->_nodes[0]->getshapeId();
+        set< TGeomID >::iterator idIt = subIDs.find( shapeID );
+        if ( idIt == subIDs.end() ||
+             data._edges[ iBeg ]->_sWOL.IsNull() ) continue;
+        subIDs.erase( idIt );
+
+        if ( !data._noShrinkShapes.count( shapeID ))
+          for ( ; iBeg < iEnd; ++iBeg )
+          {
+            lEdges.push_back( data._edges[ iBeg ] );
+            prepareEdgeToShrink( *data._edges[ iBeg ], F, helper, smDS );
+          }
       }
     }
 
+    dumpFunction(SMESH_Comment("beforeShrinkFace")<<f2sd->first); // debug
+    SMDS_ElemIteratorPtr fIt = smDS->GetElements();
+    while ( fIt->more() )
+      if ( const SMDS_MeshElement* f = fIt->next() )
+        dumpChangeNodes( f );
+
     // Replace source nodes by target nodes in mesh faces to shrink
+    dumpFunction(SMESH_Comment("replNodesOnFace")<<f2sd->first); // debug
     const SMDS_MeshNode* nodes[20];
-    for ( unsigned i = 0; i < lEdges.size(); ++i )
+    for ( size_t i = 0; i < lEdges.size(); ++i )
     {
       _LayerEdge& edge = *lEdges[i];
       const SMDS_MeshNode* srcNode = edge._nodes[0];
@@ -3617,13 +5267,14 @@ bool _ViscousBuilder::shrink()
         const SMDS_MeshElement* f = fIt->next();
         if ( !smDS->Contains( f ))
           continue;
-        SMDS_ElemIteratorPtr nIt = f->nodesIterator();
-        for ( int iN = 0; iN < f->NbNodes(); ++iN )
+        SMDS_NodeIteratorPtr nIt = f->nodeIterator();
+        for ( int iN = 0; nIt->more(); ++iN )
         {
-          const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
+          const SMDS_MeshNode* n = nIt->next();
           nodes[iN] = ( n == srcNode ? tgtNode : n );
         }
         helper.GetMeshDS()->ChangeElementNodes( f, nodes, f->NbNodes() );
+        dumpChangeNodes( f );
       }
     }
 
@@ -3633,9 +5284,9 @@ bool _ViscousBuilder::shrink()
     // Create _SmoothNode's on face F
     vector< _SmoothNode > nodesToSmooth( smoothNodes.size() );
     {
-      dumpFunction(SMESH_Comment("beforeShrinkFace")<<f2sd->first); // debug
+      dumpFunction(SMESH_Comment("fixUVOnFace")<<f2sd->first); // debug
       const bool sortSimplices = isConcaveFace;
-      for ( unsigned i = 0; i < smoothNodes.size(); ++i )
+      for ( size_t i = 0; i < smoothNodes.size(); ++i )
       {
         const SMDS_MeshNode* n = smoothNodes[i];
         nodesToSmooth[ i ]._node = n;
@@ -3645,14 +5296,13 @@ bool _ViscousBuilder::shrink()
         helper.GetNodeUV( F, n, 0, &isOkUV);
         dumpMove( n );
       }
-      dumpFunctionEnd();
     }
     //if ( nodesToSmooth.empty() ) continue;
 
-    // Find EDGE's to shrink
+    // Find EDGE's to shrink and set simpices to LayerEdge's
     set< _Shrinker1D* > eShri1D;
     {
-      for ( unsigned i = 0; i < lEdges.size(); ++i )
+      for ( size_t i = 0; i < lEdges.size(); ++i )
       {
         _LayerEdge* edge = lEdges[i];
         if ( edge->_sWOL.ShapeType() == TopAbs_EDGE )
@@ -3666,6 +5316,23 @@ bool _ViscousBuilder::shrink()
           // srinked while srinking another FACE
           srinker.RestoreParams();
         }
+        getSimplices( /*tgtNode=*/edge->_nodes.back(), edge->_simplices, ignoreShapes );
+      }
+    }
+
+    bool toFixTria = false; // to improve quality of trias by diagonal swap
+    if ( isConcaveFace )
+    {
+      const bool hasTria = _mesh->NbTriangles(), hasQuad = _mesh->NbQuadrangles();
+      if ( hasTria != hasQuad ) {
+        toFixTria = hasTria;
+      }
+      else {
+        set<int> nbNodesSet;
+        SMDS_ElemIteratorPtr fIt = smDS->GetElements();
+        while ( fIt->more() && nbNodesSet.size() < 2 )
+          nbNodesSet.insert( fIt->next()->NbCornerNodes() );
+        toFixTria = ( *nbNodesSet.begin() == 3 );
       }
     }
 
@@ -3676,7 +5343,7 @@ bool _ViscousBuilder::shrink()
     bool shrinked = true;
     int badNb, shriStep=0, smooStep=0;
     _SmoothNode::SmoothType smoothType
-      = isConcaveFace ? _SmoothNode::CENTROIDAL : _SmoothNode::LAPLACIAN;
+      = isConcaveFace ? _SmoothNode::ANGULAR : _SmoothNode::LAPLACIAN;
     while ( shrinked )
     {
       shriStep++;
@@ -3684,7 +5351,7 @@ bool _ViscousBuilder::shrink()
       // -----------------------------------------------
       dumpFunction(SMESH_Comment("moveBoundaryOnF")<<f2sd->first<<"_st"<<shriStep ); // debug
       shrinked = false;
-      for ( unsigned i = 0; i < lEdges.size(); ++i )
+      for ( size_t i = 0; i < lEdges.size(); ++i )
       {
         shrinked |= lEdges[i]->SetNewLength2d( surface,F,helper );
       }
@@ -3699,7 +5366,7 @@ bool _ViscousBuilder::shrink()
       // Smoothing in 2D
       // -----------------
       int nbNoImpSteps = 0;
-      bool moved = true;
+      bool       moved = true;
       badNb = 1;
       while (( nbNoImpSteps < 5 && badNb > 0) && moved)
       {
@@ -3708,7 +5375,7 @@ bool _ViscousBuilder::shrink()
         int oldBadNb = badNb;
         badNb = 0;
         moved = false;
-        for ( unsigned i = 0; i < nodesToSmooth.size(); ++i )
+        for ( size_t i = 0; i < nodesToSmooth.size(); ++i )
         {
           moved |= nodesToSmooth[i].Smooth( badNb,surface,helper,refSign,
                                             smoothType, /*set3D=*/isConcaveFace);
@@ -3724,7 +5391,52 @@ bool _ViscousBuilder::shrink()
         return error(SMESH_Comment("Can't shrink 2D mesh on face ") << f2sd->first );
       if ( shriStep > 200 )
         return error(SMESH_Comment("Infinite loop at shrinking 2D mesh on face ") << f2sd->first );
-    }
+
+      // Fix narrow triangles by swapping diagonals
+      // ---------------------------------------
+      if ( toFixTria )
+      {
+        set<const SMDS_MeshNode*> usedNodes;
+        fixBadFaces( F, helper, /*is2D=*/true, shriStep, & usedNodes); // swap diagonals
+
+        // update working data
+        set<const SMDS_MeshNode*>::iterator n;
+        for ( size_t i = 0; i < nodesToSmooth.size() && !usedNodes.empty(); ++i )
+        {
+          n = usedNodes.find( nodesToSmooth[ i ]._node );
+          if ( n != usedNodes.end())
+          {
+            getSimplices( nodesToSmooth[ i ]._node,
+                          nodesToSmooth[ i ]._simplices,
+                          ignoreShapes, NULL,
+                          /*sortSimplices=*/ smoothType == _SmoothNode::ANGULAR );
+            usedNodes.erase( n );
+          }
+        }
+        for ( size_t i = 0; i < lEdges.size() && !usedNodes.empty(); ++i )
+        {
+          n = usedNodes.find( /*tgtNode=*/ lEdges[i]->_nodes.back() );
+          if ( n != usedNodes.end())
+          {
+            getSimplices( lEdges[i]->_nodes.back(),
+                          lEdges[i]->_simplices,
+                          ignoreShapes );
+            usedNodes.erase( n );
+          }
+        }
+      }
+      // TODO: check effect of this additional smooth
+      // additional laplacian smooth to increase allowed shrink step
+      // for ( int st = 1; st; --st )
+      // {
+      //   dumpFunction(SMESH_Comment("shrinkFace")<<f2sd->first<<"_st"<<++smooStep); // debug
+      //   for ( size_t i = 0; i < nodesToSmooth.size(); ++i )
+      //   {
+      //     nodesToSmooth[i].Smooth( badNb,surface,helper,refSign,
+      //                              _SmoothNode::LAPLACIAN,/*set3D=*/false);
+      //   }
+      // }
+    } // while ( shrinked )
 
     // No wrongly shaped faces remain; final smooth. Set node XYZ.
     bool isStructuredFixed = false;
@@ -3732,12 +5444,18 @@ bool _ViscousBuilder::shrink()
       isStructuredFixed = algo->FixInternalNodes( *data._proxyMesh, F );
     if ( !isStructuredFixed )
     {
-      if ( isConcaveFace )
-        fixBadFaces( F, helper ); // fix narrow faces by swapping diagonals
-      for ( int st = /*highQuality ? 10 :*/ 3; st; --st )
+      if ( isConcaveFace ) // fix narrow faces by swapping diagonals
+        fixBadFaces( F, helper, /*is2D=*/false, ++shriStep );
+
+      for ( int st = 3; st; --st )
       {
+        switch( st ) {
+        case 1: smoothType = _SmoothNode::LAPLACIAN; break;
+        case 2: smoothType = _SmoothNode::LAPLACIAN; break;
+        case 3: smoothType = _SmoothNode::ANGULAR; break;
+        }
         dumpFunction(SMESH_Comment("shrinkFace")<<f2sd->first<<"_st"<<++smooStep); // debug
-        for ( unsigned i = 0; i < nodesToSmooth.size(); ++i )
+        for ( size_t i = 0; i < nodesToSmooth.size(); ++i )
         {
           nodesToSmooth[i].Smooth( badNb,surface,helper,refSign,
                                    smoothType,/*set3D=*/st==1 );
@@ -3750,7 +5468,7 @@ bool _ViscousBuilder::shrink()
 
     if ( !getMeshDS()->IsEmbeddedMode() )
       // Log node movement
-      for ( unsigned i = 0; i < nodesToSmooth.size(); ++i )
+      for ( size_t i = 0; i < nodesToSmooth.size(); ++i )
       {
         SMESH_TNodeXYZ p ( nodesToSmooth[i]._node );
         getMeshDS()->MoveNode( nodesToSmooth[i]._node, p.X(), p.Y(), p.Z() );
@@ -3782,66 +5500,19 @@ bool _ViscousBuilder::prepareEdgeToShrink( _LayerEdge&            edge,
   const SMDS_MeshNode* srcNode = edge._nodes[0];
   const SMDS_MeshNode* tgtNode = edge._nodes.back();
 
-  edge._pos.clear();
-
   if ( edge._sWOL.ShapeType() == TopAbs_FACE )
   {
-    gp_XY srcUV = helper.GetNodeUV( F, srcNode );
-    gp_XY tgtUV = helper.GetNodeUV( F, tgtNode );
+    gp_XY srcUV( edge._pos[0].X(), edge._pos[0].Y() );//helper.GetNodeUV( F, srcNode );
+    gp_XY tgtUV = edge.LastUV( F );                   //helper.GetNodeUV( F, tgtNode );
     gp_Vec2d uvDir( srcUV, tgtUV );
     double uvLen = uvDir.Magnitude();
     uvDir /= uvLen;
-    edge._normal.SetCoord( uvDir.X(),uvDir.Y(), 0);
+    edge._normal.SetCoord( uvDir.X(),uvDir.Y(), 0 );
     edge._len = uvLen;
 
-    // IMPORTANT to have src nodes NOT yet REPLACED by tgt nodes in shrinked faces
-    vector<const SMDS_MeshElement*> faces;
-    multimap< double, const SMDS_MeshNode* > proj2node;
-    SMDS_ElemIteratorPtr fIt = srcNode->GetInverseElementIterator(SMDSAbs_Face);
-    while ( fIt->more() )
-    {
-      const SMDS_MeshElement* f = fIt->next();
-      if ( faceSubMesh->Contains( f ))
-        faces.push_back( f );
-    }
-    for ( unsigned i = 0; i < faces.size(); ++i )
-    {
-      const int nbNodes = faces[i]->NbCornerNodes();
-      for ( int j = 0; j < nbNodes; ++j )
-      {
-        const SMDS_MeshNode* n = faces[i]->GetNode(j);
-        if ( n == srcNode ) continue;
-        if ( n->GetPosition()->GetTypeOfPosition() != SMDS_TOP_FACE &&
-             ( faces.size() > 1 || nbNodes > 3 ))
-          continue;
-        gp_Pnt2d uv = helper.GetNodeUV( F, n );
-        gp_Vec2d uvDirN( srcUV, uv );
-        double proj = uvDirN * uvDir;
-        proj2node.insert( make_pair( proj, n ));
-      }
-    }
-
-    multimap< double, const SMDS_MeshNode* >::iterator p2n = proj2node.begin(), p2nEnd;
-    const double       minProj = p2n->first;
-    const double projThreshold = 1.1 * uvLen;
-    if ( minProj > projThreshold )
-    {
-      // tgtNode is located so that it does not make faces with wrong orientation
-      return true;
-    }
     edge._pos.resize(1);
     edge._pos[0].SetCoord( tgtUV.X(), tgtUV.Y(), 0 );
 
-    // store most risky nodes in _simplices
-    p2nEnd = proj2node.lower_bound( projThreshold );
-    int nbSimpl = ( std::distance( p2n, p2nEnd ) + 1) / 2;
-    edge._simplices.resize( nbSimpl );
-    for ( int i = 0; i < nbSimpl; ++i )
-    {
-      edge._simplices[i]._nPrev = p2n->second;
-      if ( ++p2n != p2nEnd )
-        edge._simplices[i]._nNext = p2n->second;
-    }
     // set UV of source node to target node
     SMDS_FacePosition* pos = static_cast<SMDS_FacePosition*>( tgtNode->GetPosition() );
     pos->SetUParameter( srcUV.X() );
@@ -3849,7 +5520,7 @@ bool _ViscousBuilder::prepareEdgeToShrink( _LayerEdge&            edge,
   }
   else // _sWOL is TopAbs_EDGE
   {
-    TopoDS_Edge E = TopoDS::Edge( edge._sWOL);
+    const TopoDS_Edge&    E = TopoDS::Edge( edge._sWOL );
     SMESHDS_SubMesh* edgeSM = getMeshDS()->MeshElements( E );
     if ( !edgeSM || edgeSM->NbElements() == 0 )
       return error(SMESH_Comment("Not meshed EDGE ") << getMeshDS()->ShapeToIndex( E ));
@@ -3868,7 +5539,9 @@ bool _ViscousBuilder::prepareEdgeToShrink( _LayerEdge&            edge,
 
     double uSrc = helper.GetNodeU( E, srcNode, n2 );
     double uTgt = helper.GetNodeU( E, tgtNode, srcNode );
-    double u2   = helper.GetNodeU( E, n2,      srcNode );
+    double u2   = helper.GetNodeU( E, n2, srcNode );
+
+    edge._pos.clear();
 
     if ( fabs( uSrc-uTgt ) < 0.99 * fabs( uSrc-u2 ))
     {
@@ -3883,64 +5556,54 @@ bool _ViscousBuilder::prepareEdgeToShrink( _LayerEdge&            edge,
     edge._simplices.resize( 1 );
     edge._simplices[0]._nPrev = n2;
 
-    // set UV of source node to target node
+    // set U of source node to the target node
     SMDS_EdgePosition* pos = static_cast<SMDS_EdgePosition*>( tgtNode->GetPosition() );
     pos->SetUParameter( uSrc );
   }
   return true;
+}
 
-  //================================================================================
-  /*!
-   * \brief Compute positions (UV) to set to a node on edge moved during shrinking
-   */
-  //================================================================================
-  
-  // Compute UV to follow during shrinking
-
-//   const SMDS_MeshNode* srcNode = edge._nodes[0];
-//   const SMDS_MeshNode* tgtNode = edge._nodes.back();
-
-//   gp_XY srcUV = helper.GetNodeUV( F, srcNode );
-//   gp_XY tgtUV = helper.GetNodeUV( F, tgtNode );
-//   gp_Vec2d uvDir( srcUV, tgtUV );
-//   double uvLen = uvDir.Magnitude();
-//   uvDir /= uvLen;
-
-//   // Select shrinking step such that not to make faces with wrong orientation.
-//   // IMPORTANT to have src nodes NOT yet REPLACED by tgt nodes in shrinked faces
-//   const double minStepSize = uvLen / 20;
-//   double stepSize = uvLen;
-//   SMDS_ElemIteratorPtr fIt = srcNode->GetInverseElementIterator(SMDSAbs_Face);
-//   while ( fIt->more() )
-//   {
-//     const SMDS_MeshElement* f = fIt->next();
-//     if ( !faceSubMesh->Contains( f )) continue;
-//     const int nbNodes = f->NbCornerNodes();
-//     for ( int i = 0; i < nbNodes; ++i )
-//     {
-//       const SMDS_MeshNode* n = f->GetNode(i);
-//       if ( n->GetPosition()->GetTypeOfPosition() != SMDS_TOP_FACE || n == srcNode)
-//         continue;
-//       gp_XY uv = helper.GetNodeUV( F, n );
-//       gp_Vec2d uvDirN( srcUV, uv );
-//       double proj = uvDirN * uvDir;
-//       if ( proj < stepSize && proj > minStepSize )
-//         stepSize = proj;
-//     }
-//   }
-//   stepSize *= 0.8;
-
-//   const int nbSteps = ceil( uvLen / stepSize );
-//   gp_XYZ srcUV0( srcUV.X(), srcUV.Y(), 0 );
-//   gp_XYZ tgtUV0( tgtUV.X(), tgtUV.Y(), 0 );
-//   edge._pos.resize( nbSteps );
-//   edge._pos[0] = tgtUV0;
-//   for ( int i = 1; i < nbSteps; ++i )
-//   {
-//     double r = i / double( nbSteps );
-//     edge._pos[i] = (1-r) * tgtUV0 + r * srcUV0;
-//   }
-//   return true;
+//================================================================================
+/*!
+ * \brief Restore position of a sole node of a _LayerEdge based on _noShrinkShapes
+ */
+//================================================================================
+
+void _ViscousBuilder::restoreNoShrink( _LayerEdge& edge ) const
+{
+  if ( edge._nodes.size() == 1 )
+  {
+    edge._pos.clear();
+    edge._len = 0;
+
+    const SMDS_MeshNode* srcNode = edge._nodes[0];
+    TopoDS_Shape S = SMESH_MesherHelper::GetSubShapeByNode( srcNode, getMeshDS() );
+    if ( S.IsNull() ) return;
+
+    gp_Pnt p;
+
+    switch ( S.ShapeType() )
+    {
+    case TopAbs_EDGE:
+    {
+      double f,l;
+      TopLoc_Location loc;
+      Handle(Geom_Curve) curve = BRep_Tool::Curve( TopoDS::Edge( S ), loc, f, l );
+      if ( curve.IsNull() ) return;
+      SMDS_EdgePosition* ePos = static_cast<SMDS_EdgePosition*>( srcNode->GetPosition() );
+      p = curve->Value( ePos->GetUParameter() );
+      break;
+    }
+    case TopAbs_VERTEX:
+    {
+      p = BRep_Tool::Pnt( TopoDS::Vertex( S ));
+      break;
+    }
+    default: return;
+    }
+    getMeshDS()->MoveNode( srcNode, p.X(), p.Y(), p.Z() );
+    dumpMove( srcNode );
+  }
 }
 
 //================================================================================
@@ -3949,23 +5612,28 @@ bool _ViscousBuilder::prepareEdgeToShrink( _LayerEdge&            edge,
  */
 //================================================================================
 
-void _ViscousBuilder::fixBadFaces(const TopoDS_Face& F, SMESH_MesherHelper& helper)
+void _ViscousBuilder::fixBadFaces(const TopoDS_Face&          F,
+                                  SMESH_MesherHelper&         helper,
+                                  const bool                  is2D,
+                                  const int                   step,
+                                  set<const SMDS_MeshNode*> * involvedNodes)
 {
   SMESH::Controls::AspectRatio qualifier;
   SMESH::Controls::TSequenceOfXYZ points(3), points1(3), points2(3);
-  const double maxAspectRatio = 4.;
+  const double maxAspectRatio = is2D ? 4. : 2;
+  _NodeCoordHelper xyz( F, helper, is2D );
 
   // find bad triangles
 
   vector< const SMDS_MeshElement* > badTrias;
   vector< double >                  badAspects;
-  SMESHDS_SubMesh* sm = helper.GetMeshDS()->MeshElements( F );
+  SMESHDS_SubMesh*      sm = helper.GetMeshDS()->MeshElements( F );
   SMDS_ElemIteratorPtr fIt = sm->GetElements();
   while ( fIt->more() )
   {
     const SMDS_MeshElement * f = fIt->next();
     if ( f->NbCornerNodes() != 3 ) continue;
-    for ( int iP = 0; iP < 3; ++iP ) points(iP+1) = SMESH_TNodeXYZ( f->GetNode(iP));
+    for ( int iP = 0; iP < 3; ++iP ) points(iP+1) = xyz( f->GetNode(iP));
     double aspect = qualifier.GetValue( points );
     if ( aspect > maxAspectRatio )
     {
@@ -3973,6 +5641,18 @@ void _ViscousBuilder::fixBadFaces(const TopoDS_Face& F, SMESH_MesherHelper& help
       badAspects.push_back( aspect );
     }
   }
+  if ( step == 1 )
+  {
+    dumpFunction(SMESH_Comment("beforeSwapDiagonals_F")<<helper.GetSubShapeID());
+    SMDS_ElemIteratorPtr fIt = sm->GetElements();
+    while ( fIt->more() )
+    {
+      const SMDS_MeshElement * f = fIt->next();
+      if ( f->NbCornerNodes() == 3 )
+        dumpChangeNodes( f );
+    }
+    dumpFunctionEnd();
+  }
   if ( badTrias.empty() )
     return;
 
@@ -3988,9 +5668,10 @@ void _ViscousBuilder::fixBadFaces(const TopoDS_Face& F, SMESH_MesherHelper& help
     double  aspRatio [3];
     int i1, i2, i3;
 
-    involvedFaces.insert( badTrias[iTia] );
+    if ( !involvedFaces.insert( badTrias[iTia] ).second )
+      continue;
     for ( int iP = 0; iP < 3; ++iP )
-      points(iP+1) = SMESH_TNodeXYZ( badTrias[iTia]->GetNode(iP));
+      points(iP+1) = xyz( badTrias[iTia]->GetNode(iP));
 
     // find triangles adjacent to badTrias[iTia] with better aspect ratio after diag-swaping
     int bestCouple = -1;
@@ -4001,12 +5682,14 @@ void _ViscousBuilder::fixBadFaces(const TopoDS_Face& F, SMESH_MesherHelper& help
       trias [iSide].first  = badTrias[iTia];
       trias [iSide].second = SMESH_MeshAlgos::FindFaceInSet( n1, n2, emptySet, involvedFaces,
                                                              & i1, & i2 );
-      if ( ! trias[iSide].second || trias[iSide].second->NbCornerNodes() != 3 )
+      if (( ! trias[iSide].second ) ||
+          ( trias[iSide].second->NbCornerNodes() != 3 ) ||
+          ( ! sm->Contains( trias[iSide].second )))
         continue;
 
       // aspect ratio of an adjacent tria
       for ( int iP = 0; iP < 3; ++iP )
-        points2(iP+1) = SMESH_TNodeXYZ( trias[iSide].second->GetNode(iP));
+        points2(iP+1) = xyz( trias[iSide].second->GetNode(iP));
       double aspectInit = qualifier.GetValue( points2 );
 
       // arrange nodes as after diag-swaping
@@ -4023,6 +5706,12 @@ void _ViscousBuilder::fixBadFaces(const TopoDS_Face& F, SMESH_MesherHelper& help
       if ( aspRatio[ iSide ] > aspectInit + badAspects[ iTia ] )
         continue;
 
+      // prevent inversion of a triangle
+      gp_Vec norm1 = gp_Vec( points1(1), points1(3) ) ^ gp_Vec( points1(1), points1(2) );
+      gp_Vec norm2 = gp_Vec( points2(1), points2(3) ) ^ gp_Vec( points2(1), points2(2) );
+      if ( norm1 * norm2 < 0. && norm1.Angle( norm2 ) > 70./180.*M_PI )
+        continue;
+
       if ( bestCouple < 0 || aspRatio[ bestCouple ] > aspRatio[ iSide ] )
         bestCouple = iSide;
     }
@@ -4043,17 +5732,25 @@ void _ViscousBuilder::fixBadFaces(const TopoDS_Face& F, SMESH_MesherHelper& help
   // swap diagonals
 
   SMESH_MeshEditor editor( helper.GetMesh() );
-  dumpFunction(SMESH_Comment("beforeSwapDiagonals_F")<<helper.GetSubShapeID());
+  dumpFunction(SMESH_Comment("beforeSwapDiagonals_F")<<helper.GetSubShapeID()<<"_"<<step);
   for ( size_t i = 0; i < triaCouples.size(); ++i )
   {
     dumpChangeNodes( triaCouples[i].first );
     dumpChangeNodes( triaCouples[i].second );
     editor.InverseDiag( triaCouples[i].first, triaCouples[i].second );
   }
-  dumpFunctionEnd();
+
+  if ( involvedNodes )
+    for ( size_t i = 0; i < triaCouples.size(); ++i )
+    {
+      involvedNodes->insert( triaCouples[i].first->begin_nodes(),
+                             triaCouples[i].first->end_nodes() );
+      involvedNodes->insert( triaCouples[i].second->begin_nodes(),
+                             triaCouples[i].second->end_nodes() );
+    }
 
   // just for debug dump resulting triangles
-  dumpFunction(SMESH_Comment("swapDiagonals_F")<<helper.GetSubShapeID());
+  dumpFunction(SMESH_Comment("swapDiagonals_F")<<helper.GetSubShapeID()<<"_"<<step);
   for ( size_t i = 0; i < triaCouples.size(); ++i )
   {
     dumpChangeNodes( triaCouples[i].first );
@@ -4079,41 +5776,40 @@ bool _LayerEdge::SetNewLength2d( Handle(Geom_Surface)& surface,
   if ( _sWOL.ShapeType() == TopAbs_FACE )
   {
     gp_XY    curUV = helper.GetNodeUV( F, tgtNode );
-    gp_Pnt2d tgtUV( _pos[0].X(), _pos[0].Y());
+    gp_Pnt2d tgtUV( _pos[0].X(), _pos[0].Y() );
     gp_Vec2d uvDir( _normal.X(), _normal.Y() );
     const double uvLen = tgtUV.Distance( curUV );
+    const double kSafe = Max( 0.5, 1. - 0.1 * _simplices.size() );
 
     // Select shrinking step such that not to make faces with wrong orientation.
-    const double kSafe = 0.8;
-    const double minStepSize = uvLen / 10;
-    double stepSize = uvLen;
-    for ( unsigned i = 0; i < _simplices.size(); ++i )
+    double stepSize = 1e100;
+    for ( size_t i = 0; i < _simplices.size(); ++i )
     {
-      const SMDS_MeshNode* nn[2] = { _simplices[i]._nPrev, _simplices[i]._nNext };
-      for ( int j = 0; j < 2; ++j )
-        if ( const SMDS_MeshNode* n = nn[j] )
-        {
-          gp_XY uv = helper.GetNodeUV( F, n );
-          gp_Vec2d uvDirN( curUV, uv );
-          double proj = uvDirN * uvDir * kSafe;
-          if ( proj < stepSize && proj > minStepSize )
-            stepSize = proj;
-          else if ( proj < minStepSize )
-            stepSize = minStepSize;
-        }
+      // find intersection of 2 lines: curUV-tgtUV and that connecting simplex nodes
+      gp_XY uvN1 = helper.GetNodeUV( F, _simplices[i]._nPrev );
+      gp_XY uvN2 = helper.GetNodeUV( F, _simplices[i]._nNext );
+      gp_XY dirN = uvN2 - uvN1;
+      double det = uvDir.Crossed( dirN );
+      if ( Abs( det )  < std::numeric_limits<double>::min() ) continue;
+      gp_XY dirN2Cur = curUV - uvN1;
+      double step = dirN.Crossed( dirN2Cur ) / det;
+      if ( step > 0 )
+        stepSize = Min( step, stepSize );
     }
-
     gp_Pnt2d newUV;
-    if ( uvLen - stepSize < _len / 20. )
+    if ( uvLen <= stepSize )
     {
       newUV = tgtUV;
       _pos.clear();
     }
+    else if ( stepSize > 0 )
+    {
+      newUV = curUV + uvDir.XY() * stepSize * kSafe;
+    }
     else
     {
-      newUV = curUV + uvDir.XY() * stepSize;
+      return true;
     }
-
     SMDS_FacePosition* pos = static_cast<SMDS_FacePosition*>( tgtNode->GetPosition() );
     pos->SetUParameter( newUV.X() );
     pos->SetVParameter( newUV.Y() );
@@ -4126,11 +5822,11 @@ bool _LayerEdge::SetNewLength2d( Handle(Geom_Surface)& surface,
   }
   else // _sWOL is TopAbs_EDGE
   {
-    TopoDS_Edge E = TopoDS::Edge( _sWOL );
-    const SMDS_MeshNode* n2 = _simplices[0]._nPrev;
+    const TopoDS_Edge&      E = TopoDS::Edge( _sWOL );
+    const SMDS_MeshNode*   n2 = _simplices[0]._nPrev;
     SMDS_EdgePosition* tgtPos = static_cast<SMDS_EdgePosition*>( tgtNode->GetPosition() );
 
-    const double u2 = helper.GetNodeU( E, n2, tgtNode );
+    const double u2     = helper.GetNodeU( E, n2, tgtNode );
     const double uSrc   = _pos[0].Coord( U_SRC );
     const double lenTgt = _pos[0].Coord( LEN_TGT );
 
@@ -4177,30 +5873,24 @@ bool _SmoothNode::Smooth(int&                  badNb,
 
   // compute new UV for the node
   gp_XY newPos (0,0);
-/*  if ( how == ANGULAR && _simplices.size() == 4 )
+  if ( how == TFI && _simplices.size() == 4 )
   {
-    vector<gp_XY> corners; corners.reserve(4);
+    gp_XY corners[4];
     for ( size_t i = 0; i < _simplices.size(); ++i )
       if ( _simplices[i]._nOpp )
-        corners.push_back( helper.GetNodeUV( face, _simplices[i]._nOpp, _node ));
-    if ( corners.size() == 4 )
-    {
-      newPos = helper.calcTFI
-        ( 0.5, 0.5,
-          corners[0], corners[1], corners[2], corners[3],
-          uv[1], uv[2], uv[3], uv[0] );
-    }
-    // vector<gp_XY> p( _simplices.size() * 2 + 1 );
-    // p.clear();
-    // for ( size_t i = 0; i < _simplices.size(); ++i )
-    // {
-    //   p.push_back( uv[i] );
-    //   if ( _simplices[i]._nOpp )
-    //     p.push_back( helper.GetNodeUV( face, _simplices[i]._nOpp, _node ));
-    // }
-    // newPos = computeAngularPos( p, helper.GetNodeUV( face, _node ), refSign );
+        corners[i] = helper.GetNodeUV( face, _simplices[i]._nOpp, _node );
+      else
+        throw SALOME_Exception(LOCALIZED("TFI smoothing: _Simplex::_nOpp not set!"));
+
+    newPos = helper.calcTFI ( 0.5, 0.5,
+                              corners[0], corners[1], corners[2], corners[3],
+                              uv[1], uv[2], uv[3], uv[0] );
   }
-  else*/ if ( how == CENTROIDAL && _simplices.size() > 3 )
+  else if ( how == ANGULAR )
+  {
+    newPos = computeAngularPos( uv, helper.GetNodeUV( face, _node ), refSign );
+  }
+  else if ( how == CENTROIDAL && _simplices.size() > 3 )
   {
     // average centers of diagonals wieghted with their reciprocal lengths
     if ( _simplices.size() == 4 )
@@ -4231,7 +5921,6 @@ bool _SmoothNode::Smooth(int&                  badNb,
   else
   {
     // Laplacian smooth
-    //isCentroidal = false;
     for ( size_t i = 0; i < _simplices.size(); ++i )
       newPos += uv[i];
     newPos /= _simplices.size();
@@ -4240,17 +5929,15 @@ bool _SmoothNode::Smooth(int&                  badNb,
   // count quality metrics (orientation) of triangles around the node
   int nbOkBefore = 0;
   gp_XY tgtUV = helper.GetNodeUV( face, _node );
-  for ( unsigned i = 0; i < _simplices.size(); ++i )
+  for ( size_t i = 0; i < _simplices.size(); ++i )
     nbOkBefore += _simplices[i].IsForward( tgtUV, _node, face, helper, refSign );
 
   int nbOkAfter = 0;
-  for ( unsigned i = 0; i < _simplices.size(); ++i )
+  for ( size_t i = 0; i < _simplices.size(); ++i )
     nbOkAfter += _simplices[i].IsForward( newPos, _node, face, helper, refSign );
 
   if ( nbOkAfter < nbOkBefore )
   {
-    // if ( isCentroidal )
-    //   return Smooth( badNb, surface, helper, refSign, !isCentroidal, set3D );
     badNb += _simplices.size() - nbOkBefore;
     return false;
   }
@@ -4285,22 +5972,22 @@ gp_XY _SmoothNode::computeAngularPos(vector<gp_XY>& uv,
 {
   uv.push_back( uv.front() );
 
-  vector< gp_XY > edgeDir( uv.size() );
+  vector< gp_XY >  edgeDir ( uv.size() );
   vector< double > edgeSize( uv.size() );
   for ( size_t i = 1; i < edgeDir.size(); ++i )
   {
-    edgeDir[i-1] = uv[i] - uv[i-1];
+    edgeDir [i-1] = uv[i] - uv[i-1];
     edgeSize[i-1] = edgeDir[i-1].Modulus();
     if ( edgeSize[i-1] < numeric_limits<double>::min() )
       edgeDir[i-1].SetX( 100 );
     else
       edgeDir[i-1] /= edgeSize[i-1] * refSign;
   }
-  edgeDir.back() = edgeDir.front();
+  edgeDir.back()  = edgeDir.front();
   edgeSize.back() = edgeSize.front();
 
-  gp_XY newPos(0,0);
-  int nbEdges = 0;
+  gp_XY  newPos(0,0);
+  int    nbEdges = 0;
   double sumSize = 0;
   for ( size_t i = 1; i < edgeDir.size(); ++i )
   {
@@ -4320,7 +6007,7 @@ gp_XY _SmoothNode::computeAngularPos(vector<gp_XY>& uv,
     }
     bisec /= bisecSize;
 
-    gp_XY  dirToN = uvToFix - p;
+    gp_XY  dirToN  = uvToFix - p;
     double distToN = dirToN.Modulus();
     if ( bisec * dirToN < 0 )
       distToN = -distToN;
@@ -4341,7 +6028,7 @@ gp_XY _SmoothNode::computeAngularPos(vector<gp_XY>& uv,
 
 _SolidData::~_SolidData()
 {
-  for ( unsigned i = 0; i < _edges.size(); ++i )
+  for ( size_t i = 0; i < _edges.size(); ++i )
   {
     if ( _edges[i] && _edges[i]->_2neibors )
       delete _edges[i]->_2neibors;
@@ -4414,7 +6101,7 @@ void _Shrinker1D::AddEdge( const _LayerEdge* e, SMESH_MesherHelper& helper )
   {
     // remove target node of the _LayerEdge from _nodes
     int nbFound = 0;
-    for ( unsigned i = 0; i < _nodes.size(); ++i )
+    for ( size_t i = 0; i < _nodes.size(); ++i )
       if ( !_nodes[i] || _nodes[i] == tgtNode0 || _nodes[i] == tgtNode1 )
         _nodes[i] = 0, nbFound++;
     if ( nbFound == _nodes.size() )
@@ -4452,7 +6139,7 @@ void _Shrinker1D::Compute(bool set3D, SMESH_MesherHelper& helper)
       l = helper.GetNodeU( E, _edges[1]->_nodes.back(), _nodes.back() );
     double totLen = GCPnts_AbscissaPoint::Length( aCurve, f, l );
 
-    for ( unsigned i = 0; i < _nodes.size(); ++i )
+    for ( size_t i = 0; i < _nodes.size(); ++i )
     {
       if ( !_nodes[i] ) continue;
       double len = totLen * _normPar[i];
@@ -4474,7 +6161,7 @@ void _Shrinker1D::Compute(bool set3D, SMESH_MesherHelper& helper)
     if ( _edges[1] )
       l = helper.GetNodeU( E, _edges[1]->_nodes.back(), _nodes.back() );
     
-    for ( unsigned i = 0; i < _nodes.size(); ++i )
+    for ( size_t i = 0; i < _nodes.size(); ++i )
     {
       if ( !_nodes[i] ) continue;
       double u = f * ( 1-_normPar[i] ) + l * _normPar[i];
@@ -4493,7 +6180,7 @@ void _Shrinker1D::Compute(bool set3D, SMESH_MesherHelper& helper)
 void _Shrinker1D::RestoreParams()
 {
   if ( _done )
-    for ( unsigned i = 0; i < _nodes.size(); ++i )
+    for ( size_t i = 0; i < _nodes.size(); ++i )
     {
       if ( !_nodes[i] ) continue;
       SMDS_EdgePosition* pos = static_cast<SMDS_EdgePosition*>( _nodes[i]->GetPosition() );
@@ -4546,7 +6233,9 @@ bool _ViscousBuilder::addBoundaryElements()
 {
   SMESH_MesherHelper helper( *_mesh );
 
-  for ( unsigned i = 0; i < _sdVec.size(); ++i )
+  vector< const SMDS_MeshNode* > faceNodes;
+
+  for ( size_t i = 0; i < _sdVec.size(); ++i )
   {
     _SolidData& data = _sdVec[i];
     TopTools_IndexedMapOfShape geomEdges;
@@ -4554,6 +6243,8 @@ bool _ViscousBuilder::addBoundaryElements()
     for ( int iE = 1; iE <= geomEdges.Extent(); ++iE )
     {
       const TopoDS_Edge& E = TopoDS::Edge( geomEdges(iE));
+      if ( data._noShrinkShapes.count( getMeshDS()->ShapeToIndex( E )))
+        continue;
 
       // Get _LayerEdge's based on E
 
@@ -4586,8 +6277,13 @@ bool _ViscousBuilder::addBoundaryElements()
         if ( nbSharedPyram > 1 )
           continue; // not free border of the pyramid
 
-        if ( getMeshDS()->FindFace( ledges[0]->_nodes[0], ledges[0]->_nodes[1],
-                                    ledges[1]->_nodes[0], ledges[1]->_nodes[1]))
+        faceNodes.clear();
+        faceNodes.push_back( ledges[0]->_nodes[0] );
+        faceNodes.push_back( ledges[1]->_nodes[0] );
+        if ( ledges[0]->_nodes.size() > 1 ) faceNodes.push_back( ledges[0]->_nodes[1] );
+        if ( ledges[1]->_nodes.size() > 1 ) faceNodes.push_back( ledges[1]->_nodes[1] );
+
+        if ( getMeshDS()->FindElement( faceNodes, SMDSAbs_Face, /*noMedium=*/true))
           continue; // faces already created
       }
       for ( ++u2n; u2n != u2nodes.end(); ++u2n )
@@ -4617,7 +6313,7 @@ bool _ViscousBuilder::addBoundaryElements()
         {
           const TopoDS_Shape* pF = fIt->next();
           if ( helper.IsSubShape( *pF, data._solid) &&
-               !_ignoreShapeIds.count( e2f->first ))
+               !data._ignoreFaceIds.count( e2f->first ))
             F = *pF;
         }
       }
@@ -4633,16 +6329,37 @@ bool _ViscousBuilder::addBoundaryElements()
       // Make faces
       const int dj1 = reverse ? 0 : 1;
       const int dj2 = reverse ? 1 : 0;
-      for ( unsigned j = 1; j < ledges.size(); ++j )
+      for ( size_t j = 1; j < ledges.size(); ++j )
       {
         vector< const SMDS_MeshNode*>&  nn1 = ledges[j-dj1]->_nodes;
         vector< const SMDS_MeshNode*>&  nn2 = ledges[j-dj2]->_nodes;
-        if ( isOnFace )
-          for ( size_t z = 1; z < nn1.size(); ++z )
-            sm->AddElement( getMeshDS()->AddFace( nn1[z-1], nn2[z-1], nn2[z], nn1[z] ));
+        if ( nn1.size() == nn2.size() )
+        {
+          if ( isOnFace )
+            for ( size_t z = 1; z < nn1.size(); ++z )
+              sm->AddElement( getMeshDS()->AddFace( nn1[z-1], nn2[z-1], nn2[z], nn1[z] ));
+          else
+            for ( size_t z = 1; z < nn1.size(); ++z )
+              sm->AddElement( new SMDS_FaceOfNodes( nn1[z-1], nn2[z-1], nn2[z], nn1[z] ));
+        }
+        else if ( nn1.size() == 1 )
+        {
+          if ( isOnFace )
+            for ( size_t z = 1; z < nn2.size(); ++z )
+              sm->AddElement( getMeshDS()->AddFace( nn1[0], nn2[z-1], nn2[z] ));
+          else
+            for ( size_t z = 1; z < nn2.size(); ++z )
+              sm->AddElement( new SMDS_FaceOfNodes( nn1[0], nn2[z-1], nn2[z] ));
+        }
         else
-          for ( size_t z = 1; z < nn1.size(); ++z )
-            sm->AddElement( new SMDS_FaceOfNodes( nn1[z-1], nn2[z-1], nn2[z], nn1[z]));
+        {
+          if ( isOnFace )
+            for ( size_t z = 1; z < nn1.size(); ++z )
+              sm->AddElement( getMeshDS()->AddFace( nn1[z-1], nn2[0], nn1[z] ));
+          else
+            for ( size_t z = 1; z < nn1.size(); ++z )
+              sm->AddElement( new SMDS_FaceOfNodes( nn1[z-1], nn2[0], nn2[z] ));
+        }
       }
 
       // Make edges
@@ -4652,7 +6369,7 @@ bool _ViscousBuilder::addBoundaryElements()
         if ( !edge->_sWOL.IsNull() && edge->_sWOL.ShapeType() == TopAbs_EDGE )
         {
           vector< const SMDS_MeshNode*>&  nn = edge->_nodes;
-          if ( nn[1]->GetInverseElementIterator( SMDSAbs_Edge )->more() )
+          if ( nn.size() < 2 || nn[1]->GetInverseElementIterator( SMDSAbs_Edge )->more() )
             continue;
           helper.SetSubShape( edge->_sWOL );
           helper.SetElementsOnShape( true );