Salome HOME
23418: [OCC] Mesh: Minimization of memory usage of SMESH
[modules/smesh.git] / src / SMESHDS / SMESHDS_GroupOnFilter.cxx
index 80082af0d2f7b7cd94f1fe87373dabbb35590c29..e5b2fb736bd3d75362f0f6aae9a85343fc3cf8bf 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2007-2012  CEA/DEN, EDF R&D, OPEN CASCADE
+// Copyright (C) 2007-2016  CEA/DEN, EDF R&D, OPEN CASCADE
 //
 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
@@ -6,7 +6,7 @@
 // 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
 //
 #include "SMESHDS_GroupOnFilter.hxx"
 
-#include "SMESHDS_Mesh.hxx"
 #include "SMDS_SetIterator.hxx"
+#include "ObjectPool.hxx"
+#include "SMESHDS_Mesh.hxx"
 
 #include <numeric>
 #include <limits>
 
+#include <boost/make_shared.hpp>
+
 using namespace std;
 
+//#undef WITH_TBB
+
 //=============================================================================
 /*!
  * Creates a group based on thePredicate
@@ -43,11 +48,12 @@ SMESHDS_GroupOnFilter::SMESHDS_GroupOnFilter (const int                 theID,
                                               const SMESHDS_Mesh*       theMesh,
                                               const SMDSAbs_ElementType theType,
                                               const SMESH_PredicatePtr& thePredicate)
-  : SMESHDS_GroupBase(theID,theMesh,theType),
+  : SMESHDS_GroupBase( theID, theMesh, theType ),
+    SMDS_ElementHolder( theMesh ),
     myMeshInfo( SMDSEntity_Last, 0 ),
-    myMeshModifTime(0),
-    myPredicateTic(0),
-    myNbElemToSkip(0)
+    myMeshModifTime( 0 ),
+    myPredicateTic( 0 ),
+    myNbElemToSkip( 0 )
 {
   SetPredicate( thePredicate );
 }
@@ -116,7 +122,7 @@ bool SMESHDS_GroupOnFilter::IsEmpty()
 
 bool SMESHDS_GroupOnFilter::Contains (const int theID)
 {
-  return myPredicate ? myPredicate->IsSatisfy( theID ) : false;
+  return myPredicate && myPredicate->IsSatisfy( theID );
 }
 
 //================================================================================
@@ -127,7 +133,7 @@ bool SMESHDS_GroupOnFilter::Contains (const int theID)
 
 bool SMESHDS_GroupOnFilter::Contains (const SMDS_MeshElement* elem)
 {
-  return myPredicate ? myPredicate->IsSatisfy( elem->GetID() ) : false;
+  return myPredicate && myPredicate->IsSatisfy( elem->GetID() );
 }
 
 //================================================================================
@@ -135,21 +141,36 @@ namespace // Iterator
 {
   struct TIterator : public SMDS_ElemIterator
   {
-    SMESH_PredicatePtr      myPredicate;
-    SMDS_ElemIteratorPtr    myElemIt;
-    const SMDS_MeshElement* myNextElem;
-    size_t                  myNbToFind, myNbFound;
-    TIterator( const SMESH_PredicatePtr& filter,
-               SMDS_ElemIteratorPtr&     elems,
-               size_t                    nbToFind):
+    SMESH_PredicatePtr                myPredicate;
+    SMDS_ElemIteratorPtr              myElemIt;
+    const SMDS_MeshElement*           myNextElem;
+    size_t                            myNbToFind, myNbFound, myTotalNb;
+    vector< const SMDS_MeshElement*>& myFoundElems;
+    bool &                            myFoundElemsOK;
+
+    TIterator( const SMESH_PredicatePtr&         filter,
+               SMDS_ElemIteratorPtr&             elems,
+               size_t                            nbToFind,
+               size_t                            totalNb,
+               vector< const SMDS_MeshElement*>& foundElems,
+               bool &                            foundElemsOK):
       myPredicate( filter ),
       myElemIt( elems ),
       myNextElem( 0 ),
       myNbToFind( nbToFind ),
-      myNbFound( 0 )
+      myNbFound( 0 ),
+      myTotalNb( totalNb ),
+      myFoundElems( foundElems ),
+      myFoundElemsOK( foundElemsOK )
     {
+      myFoundElemsOK = false;
       next();
     }
+    ~TIterator()
+    {
+      if ( !myFoundElemsOK )
+        clearVector( myFoundElems );
+    }
     virtual bool more()
     {
       return myNextElem;
@@ -160,14 +181,51 @@ namespace // Iterator
       myNbFound += bool( res );
       myNextElem = 0;
       if ( myNbFound < myNbToFind )
+      {
         while ( myElemIt->more() && !myNextElem )
         {
           myNextElem = myElemIt->next();
           if ( !myPredicate->IsSatisfy( myNextElem->GetID() ))
             myNextElem = 0;
         }
+        if ( myNextElem )
+          myFoundElems.push_back( myNextElem );
+        else
+          keepOrClearElemVec();
+      }
+      else
+      {
+        keepOrClearElemVec();
+      }
       return res;
     }
+    void keepOrClearElemVec()
+    {
+      if ( myNbFound == myTotalNb )
+      {
+        myFoundElemsOK = false; // all elems are OK, no need to keep them
+      }
+      else
+      {
+        // nb of bytes used for myFoundElems
+        size_t vecMemSize = myFoundElems.size() * sizeof( SMDS_MeshElement* ) / sizeof(char);
+        size_t aMB = 1024 * 1024;
+        if ( vecMemSize < aMB )
+        {
+          myFoundElemsOK = true; // < 1 MB - do not clear
+        }
+        else
+        {
+          int freeRamMB = SMDS_Mesh::CheckMemory( /*doNotRaise=*/true );
+          if ( freeRamMB < 0 )
+            myFoundElemsOK = true; // hope it's OK
+          else
+            myFoundElemsOK = ( freeRamMB * aMB > 10 * vecMemSize );
+        }
+      }
+      if ( !myFoundElemsOK )
+        clearVector( myFoundElems );
+    }
   };
 
   struct TEmptyIterator : public SMDS_ElemIterator
@@ -186,17 +244,24 @@ namespace // Iterator
 SMDS_ElemIteratorPtr SMESHDS_GroupOnFilter::GetElements() const
 {
   size_t nbToFind = std::numeric_limits<size_t>::max();
+  size_t totalNb  = GetMesh()->GetMeshInfo().NbElements( GetType() );
 
-  SMDS_ElemIteratorPtr elemIt;
+  SMDS_ElemIteratorPtr elemIt; // iterator on all elements to initialize TIterator
   if ( myPredicate )
   {
     myPredicate->SetMesh( GetMesh() ); // hope myPredicate updates self here if necessary
 
+    if ( !IsUpToDate() )
+      updateParallel();
+
     elemIt = GetMesh()->elementsIterator( GetType() );
     if ( IsUpToDate() )
     {
+      if ( myElementsOK )
+        return SMDS_ElemIteratorPtr( new SMDS_ElementVectorIterator( myElements.begin(),
+                                                                     myElements.end() ));
       nbToFind = Extent();
-      if ( nbToFind == GetMesh()->GetMeshInfo().NbElements( GetType() ))
+      if ( nbToFind == totalNb )
         return elemIt; // all elements are OK
       for ( size_t i = 0; i < myNbElemToSkip; ++i )
         elemIt->next(); // skip w/o check
@@ -206,7 +271,11 @@ SMDS_ElemIteratorPtr SMESHDS_GroupOnFilter::GetElements() const
   {
     elemIt = SMDS_ElemIteratorPtr( new TEmptyIterator );
   }
-  return SMDS_ElemIteratorPtr ( new TIterator( myPredicate, elemIt, nbToFind ));
+
+  // the iterator fills myElements if all elements are checked
+  SMESHDS_GroupOnFilter* me = const_cast<SMESHDS_GroupOnFilter*>( this );
+  return SMDS_ElemIteratorPtr
+    ( new TIterator( myPredicate, elemIt, nbToFind, totalNb, me->myElements, me->myElementsOK ));
 }
 
 //================================================================================
@@ -232,6 +301,9 @@ int SMESHDS_GroupOnFilter::getElementIds( void* ids, size_t idSize ) const
 {
   SMESHDS_GroupOnFilter* me = const_cast<SMESHDS_GroupOnFilter*>( this );
 
+  if ( !IsUpToDate() )
+    me->setChanged();
+    
   char* curID = (char*) ids;
   SMDS_ElemIteratorPtr elIt = GetElements();
   if ( elIt->more() )
@@ -243,8 +315,6 @@ int SMESHDS_GroupOnFilter::getElementIds( void* ids, size_t idSize ) const
     }
     else
     {
-      me->setChanged();
-
       // find out nb of elements to skip w/o check before the 1st OK element
       const SMDS_MeshElement* firstOkElem = me->setNbElemToSkip( elIt );
 
@@ -299,18 +369,132 @@ void SMESHDS_GroupOnFilter::update() const
   if ( !IsUpToDate() )
   {
     me->setChanged();
-    SMDS_ElemIteratorPtr elIt = GetElements();
-    if ( elIt->more() ) {
-      // find out nb of elements to skip w/o check before the 1st OK element
-      const SMDS_MeshElement* e = me->setNbElemToSkip( elIt );
-      ++me->myMeshInfo[ e->GetEntityType() ];
-      while ( elIt->more() )
-        ++me->myMeshInfo[ elIt->next()->GetEntityType() ];
+    if ( !updateParallel() )
+    {
+      SMDS_ElemIteratorPtr elIt = GetElements();
+      if ( elIt->more() ) {
+        // find out nb of elements to skip w/o check before the 1st OK element
+        const SMDS_MeshElement* e = me->setNbElemToSkip( elIt );
+        ++me->myMeshInfo[ e->GetEntityType() ];
+        while ( elIt->more() )
+          ++me->myMeshInfo[ elIt->next()->GetEntityType() ];
+      }
     }
     me->setChanged( false );
   }
 }
 
+//================================================================================
+/*!
+ * \brief Updates myElements in parallel
+ */
+//================================================================================
+#ifdef WITH_TBB
+
+#include <tbb/parallel_for.h>
+#include "tbb/enumerable_thread_specific.h"
+
+// a predicate per a thread
+typedef tbb::enumerable_thread_specific<SMESH_PredicatePtr> TLocalPredicat;
+
+struct IsSatisfyParallel
+{
+  vector< char >&     myIsElemOK;
+  SMESH_PredicatePtr  myPredicate;
+  TLocalPredicat&     myLocalPredicates;
+  IsSatisfyParallel( SMESH_PredicatePtr mainPred, TLocalPredicat& locPred, vector< char >& isOk )
+    : myIsElemOK(isOk), myPredicate( mainPred ), myLocalPredicates( locPred )
+  {}
+  void operator() ( const tbb::blocked_range<size_t>& r ) const
+  {
+    SMESH_PredicatePtr& pred = myLocalPredicates.local();
+    if ( !pred )
+    {
+      if ( r.begin() == 0 )
+        pred = myPredicate;
+      else
+        pred.reset( myPredicate->clone() );
+    }
+    for ( size_t i = r.begin(); i != r.end(); ++i )
+      myIsElemOK[ i ] = char( pred->IsSatisfy( i ));
+  }
+};
+
+bool SMESHDS_GroupOnFilter::updateParallel() const
+{
+  // if ( !getenv("updateParallel"))
+  //   return false;
+  size_t nbElemsOfType = GetMesh()->GetMeshInfo().NbElements( GetType() );
+  if ( nbElemsOfType == 0 )
+    return true;
+  if ( nbElemsOfType < 1000000 )
+    return false; // no sense in parallel work
+
+  SMDS_ElemIteratorPtr elemIt = GetMesh()->elementsIterator( GetType() );
+  const int minID = elemIt->next()->GetID();
+  myPredicate->IsSatisfy( minID ); // make myPredicate fully initialized for clone()
+  SMESH_PredicatePtr clone( myPredicate->clone() );
+  if ( !clone )
+    return false;
+
+  TLocalPredicat threadPredicates;
+  threadPredicates.local() = clone;
+
+  int maxID = ( GetType() == SMDSAbs_Node ) ? GetMesh()->MaxNodeID() : GetMesh()->MaxElementID();
+  vector< char > isElemOK( 1 + maxID );
+
+  tbb::parallel_for ( tbb::blocked_range<size_t>( 0, isElemOK.size() ),
+                      IsSatisfyParallel( myPredicate, threadPredicates, isElemOK ),
+                      tbb::simple_partitioner());
+
+  SMESHDS_GroupOnFilter* me = const_cast<SMESHDS_GroupOnFilter*>( this );
+
+  int nbOkElems = 0;
+  for ( size_t i = minID; i < isElemOK.size(); ++i )
+    nbOkElems += ( isElemOK[ i ]);
+  me->myElements.resize( nbOkElems );
+
+  const SMDS_MeshElement* e;
+  size_t iElem = 0;
+  if ( GetType() == SMDSAbs_Node )
+  {
+    for ( size_t i = minID; i < isElemOK.size(); ++i )
+      if (( isElemOK[ i ] ) &&
+          ( e = GetMesh()->FindNode( i )))
+      {
+        me->myElements[ iElem++ ] = e;
+      }
+    me->myMeshInfo[ SMDSEntity_Node ] = myElements.size();
+  }
+  else
+  {
+    for ( size_t i = minID; i < isElemOK.size(); ++i )
+      if (( isElemOK[ i ] ) &&
+          ( e = GetMesh()->FindElement( i )) &&
+          ( e->GetType() == GetType() ))
+      {
+        me->myElements[ iElem++ ] = e;
+        ++me->myMeshInfo[ e->GetEntityType() ];
+      }
+  }
+  me->myElementsOK = ( iElem < nbElemsOfType );
+  if ( !myElementsOK )
+    clearVector( me->myElements ); // all elements satisfy myPredicate
+  else
+    me->myElements.resize( iElem );
+
+  me->setChanged( false );
+  return true;
+}
+#else
+
+bool SMESHDS_GroupOnFilter::updateParallel() const
+{
+  return false;
+}
+
+#endif
+
 //================================================================================
 /*!
  * \brief Sets myMeshModifTime and clear fields according to modification state
@@ -323,6 +507,8 @@ void SMESHDS_GroupOnFilter::setChanged(bool changed)
   if ( changed && myMeshModifTime != 0 )
     --myMeshModifTime;
   if ( changed ) {
+    clearVector( myElements );
+    myElementsOK = false;
     myNbElemToSkip = 0;
     myMeshInfo.assign( SMDSEntity_Last, 0 );
   }
@@ -350,3 +536,38 @@ SMESHDS_GroupOnFilter::setNbElemToSkip( SMDS_ElemIteratorPtr& okElemIt )
   }
   return firstOkElem;
 }
+
+//================================================================================
+/*!
+ * \brief Return elements before mesh compacting
+ */
+//================================================================================
+
+SMDS_ElemIteratorPtr SMESHDS_GroupOnFilter::getElements()
+{
+  return boost::make_shared< SMDS_ElementVectorIterator >( myElements.begin(), myElements.end() );
+}
+
+//================================================================================
+/*!
+ * \brief clear myElements before re-filling after mesh compacting
+ */
+//================================================================================
+
+void SMESHDS_GroupOnFilter::tmpClear()
+{
+  std::vector< const SMDS_MeshElement*> newElems( myElements.size() );
+  myElements.swap( newElems );
+  myElements.clear();
+}
+
+//================================================================================
+/*!
+ * \brief Re-fill myElements after mesh compacting
+ */
+//================================================================================
+
+void SMESHDS_GroupOnFilter::add( const SMDS_MeshElement* element )
+{
+  myElements.push_back( element );
+}