1 // Copyright (C) 2007-2023 CEA, EDF, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 // File : SMESHDS_GroupOnFilter.cxx
26 #include "SMESHDS_GroupOnFilter.hxx"
28 #include "SMDS_SetIterator.hxx"
29 #include "ObjectPool.hxx"
30 #include "SMESHDS_Mesh.hxx"
35 #include <boost/make_shared.hpp>
41 //=============================================================================
43 * Creates a group based on thePredicate
45 //=============================================================================
47 SMESHDS_GroupOnFilter::SMESHDS_GroupOnFilter (const int theID,
48 const SMESHDS_Mesh* theMesh,
49 const SMDSAbs_ElementType theType,
50 const SMESH_PredicatePtr& thePredicate)
51 : SMESHDS_GroupBase( theID, theMesh, theType ),
52 SMDS_ElementHolder( theMesh ),
53 myMeshInfo( SMDSEntity_Last, 0 ),
58 SetPredicate( thePredicate );
61 //================================================================================
63 * \brief Sets a new predicate
65 //================================================================================
67 void SMESHDS_GroupOnFilter::SetPredicate( const SMESH_PredicatePtr& thePredicate )
69 myPredicate = thePredicate;
73 myPredicate->SetMesh( GetMesh() );
76 //================================================================================
78 * \brief Returns nb of elements
80 //================================================================================
82 smIdType SMESHDS_GroupOnFilter::Extent() const
85 return std::accumulate( myMeshInfo.begin(), myMeshInfo.end(), 0 );
88 //================================================================================
90 * \brief Checks emptyness
92 //================================================================================
94 bool SMESHDS_GroupOnFilter::IsEmpty()
98 return ( Extent() == 0 );
100 else // not up-to-date
103 SMDS_ElemIteratorPtr okElemIt = GetElements();
104 if ( !okElemIt->more() )
106 // no satisfying elements
117 //================================================================================
119 * \brief Checks if the element belongs to the group
121 //================================================================================
123 bool SMESHDS_GroupOnFilter::Contains (const smIdType theID)
125 return myPredicate && myPredicate->IsSatisfy( theID );
128 //================================================================================
130 * \brief Checks if the element belongs to the group
132 //================================================================================
134 bool SMESHDS_GroupOnFilter::Contains (const SMDS_MeshElement* elem)
136 return myPredicate && myPredicate->IsSatisfy( elem->GetID() );
139 //================================================================================
140 namespace // Iterator
142 struct TIterator : public SMDS_ElemIterator
144 SMESH_PredicatePtr myPredicate;
145 SMDS_ElemIteratorPtr myElemIt;
146 const SMDS_MeshElement* myNextElem;
147 size_t myNbToFind, myNbFound, myTotalNb;
148 vector< const SMDS_MeshElement*>& myFoundElems;
149 bool & myFoundElemsOK;
150 bool myFoundElemsChecked;
152 TIterator( const SMESH_PredicatePtr& filter,
153 SMDS_ElemIteratorPtr& elems,
156 vector< const SMDS_MeshElement*>& foundElems,
157 bool & foundElemsOK):
158 myPredicate( filter ),
161 myNbToFind( nbToFind ),
163 myTotalNb( totalNb ),
164 myFoundElems( foundElems ),
165 myFoundElemsOK( foundElemsOK ),
166 myFoundElemsChecked( false )
168 myFoundElemsOK = false;
173 if ( !myFoundElemsChecked && !myFoundElemsOK )
174 clearVector( myFoundElems );
180 virtual const SMDS_MeshElement* next()
182 const SMDS_MeshElement* res = myNextElem;
183 myNbFound += bool( res );
185 if ( myNbFound < myNbToFind )
187 while ( myElemIt->more() && !myNextElem )
189 myNextElem = myElemIt->next();
190 if ( !myPredicate->IsSatisfy( myNextElem->GetID() ))
194 myFoundElems.push_back( myNextElem );
196 keepOrClearElemVec();
200 keepOrClearElemVec();
204 void keepOrClearElemVec()
206 if ( myNbFound == myTotalNb )
208 myFoundElemsOK = false; // all elems are OK, no need to keep them
212 // nb of bytes used for myFoundElems
213 size_t vecMemSize = myFoundElems.size() * sizeof( SMDS_MeshElement* ) / sizeof(char);
214 size_t aMB = 1024 * 1024;
215 if ( vecMemSize < aMB )
217 myFoundElemsOK = true; // < 1 MB - do not clear
221 int freeRamMB = SMDS_Mesh::CheckMemory( /*doNotRaise=*/true );
223 myFoundElemsOK = true; // hope it's OK
225 myFoundElemsOK = ( freeRamMB * aMB > 10 * vecMemSize );
228 if ( !myFoundElemsOK )
229 clearVector( myFoundElems );
231 myFoundElemsChecked = true; // in destructor: not to clearVector() which may already die
235 struct TEmptyIterator : public SMDS_ElemIterator
237 virtual bool more() { return false; }
238 virtual const SMDS_MeshElement* next() { return 0; }
242 //================================================================================
244 * \brief Return iterator on all elements
246 //================================================================================
248 SMDS_ElemIteratorPtr SMESHDS_GroupOnFilter::GetElements() const
250 size_t nbToFind = std::numeric_limits<size_t>::max();
251 size_t totalNb = GetMesh()->GetMeshInfo().NbElements( GetType() );
253 SMDS_ElemIteratorPtr elemIt; // iterator on all elements to initialize TIterator
256 myPredicate->SetMesh( GetMesh() ); // hope myPredicate updates self here if necessary
261 elemIt = GetMesh()->elementsIterator( GetType() );
265 return SMDS_ElemIteratorPtr( new SMDS_ElementVectorIterator( myElements.begin(),
268 if ( nbToFind == totalNb )
269 return elemIt; // all elements are OK
270 for ( size_t i = 0; i < myNbElemToSkip; ++i )
271 elemIt->next(); // skip w/o check
276 elemIt = SMDS_ElemIteratorPtr( new TEmptyIterator );
279 // the iterator fills myElements if all elements are checked
280 SMESHDS_GroupOnFilter* me = const_cast<SMESHDS_GroupOnFilter*>( this );
281 return SMDS_ElemIteratorPtr
282 ( new TIterator( myPredicate, elemIt, nbToFind, totalNb, me->myElements, me->myElementsOK ));
285 //================================================================================
287 * \brief Return info on sub-types of elements
289 //================================================================================
291 std::vector< smIdType > SMESHDS_GroupOnFilter::GetMeshInfo() const
297 //================================================================================
299 * \brief Fill ids of elements. And return their number.
300 * \a ids must be pre-allocated using nb of elements of type == GetType()
302 //================================================================================
304 int SMESHDS_GroupOnFilter::getElementIds( void* ids, size_t idSize ) const
306 SMESHDS_GroupOnFilter* me = const_cast<SMESHDS_GroupOnFilter*>( this );
311 char* curID = (char*) ids;
312 SMDS_ElemIteratorPtr elIt = GetElements();
317 for ( ; elIt->more(); curID += idSize )
318 (*(smIdType*) curID) = elIt->next()->GetID();
322 // find out nb of elements to skip w/o check before the 1st OK element
323 const SMDS_MeshElement* firstOkElem = me->setNbElemToSkip( elIt );
325 me->myMeshInfo.assign( SMDSEntity_Last, 0 );
326 me->myMeshInfo[ firstOkElem->GetEntityType() ]++;
328 (*(smIdType*) curID) = firstOkElem->GetID();
329 for ( curID += idSize; elIt->more(); curID += idSize )
331 const SMDS_MeshElement* e = elIt->next();
332 (*(smIdType*) curID) = e->GetID();
333 me->myMeshInfo[ e->GetEntityType() ]++;
337 me->setChanged( false );
339 return ( curID - (char*)ids ) / idSize;
342 //================================================================================
344 * \brief Return a value allowing to find out if a group has changed or not
346 //================================================================================
348 int SMESHDS_GroupOnFilter::GetTic() const
350 return GetMesh()->GetMTime() * myPredicateTic;
353 //================================================================================
355 * \brief Return false if update() is needed
357 //================================================================================
359 bool SMESHDS_GroupOnFilter::IsUpToDate() const
361 return !( myMeshModifTime < GetMesh()->GetMTime() );
364 //================================================================================
366 * \brief Updates myElements if necessary
368 //================================================================================
370 void SMESHDS_GroupOnFilter::update() const
372 SMESHDS_GroupOnFilter* me = const_cast<SMESHDS_GroupOnFilter*>( this );
376 if ( !updateParallel() )
378 SMDS_ElemIteratorPtr elIt = GetElements();
379 if ( elIt->more() ) {
380 // find out nb of elements to skip w/o check before the 1st OK element
381 const SMDS_MeshElement* e = me->setNbElemToSkip( elIt );
382 ++me->myMeshInfo[ e->GetEntityType() ];
383 while ( elIt->more() )
384 ++me->myMeshInfo[ elIt->next()->GetEntityType() ];
387 me->setChanged( false );
391 //================================================================================
393 * \brief Updates myElements in parallel
395 //================================================================================
399 // See https://docs.microsoft.com/en-gb/cpp/porting/modifying-winver-and-win32-winnt?view=vs-2019
400 // Windows 10 = 0x0A00
401 #define WINVER 0x0A00
402 #define _WIN32_WINNT 0x0A00
406 #include <tbb/parallel_for.h>
407 #include "tbb/enumerable_thread_specific.h"
409 // a predicate per a thread
410 typedef tbb::enumerable_thread_specific<SMESH_PredicatePtr> TLocalPredicat;
412 struct IsSatisfyParallel
414 vector< char >& myIsElemOK;
415 SMESH_PredicatePtr myPredicate;
416 TLocalPredicat& myLocalPredicates;
417 IsSatisfyParallel( SMESH_PredicatePtr mainPred, TLocalPredicat& locPred, vector< char >& isOk )
418 : myIsElemOK(isOk), myPredicate( mainPred ), myLocalPredicates( locPred )
420 void operator() ( const tbb::blocked_range<size_t>& r ) const
422 SMESH_PredicatePtr& pred = myLocalPredicates.local();
425 if ( r.begin() == 0 )
428 pred.reset( myPredicate->clone() );
430 for ( size_t i = r.begin(); i != r.end(); ++i )
431 myIsElemOK[ i ] = char( pred->IsSatisfy( i ));
435 bool SMESHDS_GroupOnFilter::updateParallel() const
437 // if ( !getenv("updateParallel"))
439 size_t nbElemsOfType = GetMesh()->GetMeshInfo().NbElements( GetType() );
440 if ( nbElemsOfType == 0 )
442 if ( nbElemsOfType < 1000000 )
443 return false; // no sense in parallel work
445 SMDS_ElemIteratorPtr elemIt = GetMesh()->elementsIterator( GetType() );
446 const smIdType minID = elemIt->next()->GetID();
447 myPredicate->IsSatisfy( minID ); // make myPredicate fully initialized for clone()
448 SMESH_PredicatePtr clone( myPredicate->clone() );
452 TLocalPredicat threadPredicates;
453 threadPredicates.local() = clone;
455 smIdType maxID = ( GetType() == SMDSAbs_Node ) ? GetMesh()->MaxNodeID() : GetMesh()->MaxElementID();
456 vector< char > isElemOK( 1 + maxID );
458 tbb::parallel_for ( tbb::blocked_range<size_t>( 0, isElemOK.size() ),
459 IsSatisfyParallel( myPredicate, threadPredicates, isElemOK ),
460 tbb::simple_partitioner());
462 SMESHDS_GroupOnFilter* me = const_cast<SMESHDS_GroupOnFilter*>( this );
465 for ( size_t i = minID; i < isElemOK.size(); ++i )
466 nbOkElems += ( isElemOK[ i ]);
467 me->myElements.resize( nbOkElems );
469 const SMDS_MeshElement* e;
471 if ( GetType() == SMDSAbs_Node )
473 for ( size_t i = minID; i < isElemOK.size(); ++i )
474 if (( isElemOK[ i ] ) &&
475 ( e = GetMesh()->FindNode( i )))
477 me->myElements[ iElem++ ] = e;
479 me->myMeshInfo[ SMDSEntity_Node ] = myElements.size();
483 for ( size_t i = minID; i < isElemOK.size(); ++i )
484 if (( isElemOK[ i ] ) &&
485 ( e = GetMesh()->FindElement( i )) &&
486 ( e->GetType() == GetType() ))
488 me->myElements[ iElem++ ] = e;
489 ++me->myMeshInfo[ e->GetEntityType() ];
492 me->myElementsOK = ( iElem < nbElemsOfType );
494 clearVector( me->myElements ); // all elements satisfy myPredicate
496 me->myElements.resize( iElem );
498 me->setChanged( false );
503 bool SMESHDS_GroupOnFilter::updateParallel() const
510 //================================================================================
512 * \brief Sets myMeshModifTime and clear fields according to modification state
514 //================================================================================
516 void SMESHDS_GroupOnFilter::setChanged(bool changed)
518 myMeshModifTime = GetMesh()->GetMTime();
519 if ( changed && myMeshModifTime != 0 )
522 clearVector( myElements );
523 myElementsOK = false;
525 myMeshInfo.assign( SMDSEntity_Last, 0 );
529 //================================================================================
531 * \brief Sets myNbElemToSkip
532 * \param okElemIt - iterator on OK elements
533 * \retval const SMDS_MeshElement* - the first OK element
535 //================================================================================
537 const SMDS_MeshElement*
538 SMESHDS_GroupOnFilter::setNbElemToSkip( SMDS_ElemIteratorPtr& okElemIt )
540 // find out nb of elements to skip w/o check before the 1st OK element
541 const SMDS_MeshElement* firstOkElem = okElemIt->next();
542 if ( myNbElemToSkip == 0 )
544 SMDS_ElemIteratorPtr elemIt = GetMesh()->elementsIterator( GetType() );
546 while ( elemIt->next() != firstOkElem )
552 //================================================================================
554 * \brief Return elements before mesh compacting
556 //================================================================================
558 SMDS_ElemIteratorPtr SMESHDS_GroupOnFilter::getElements()
560 return boost::make_shared< SMDS_ElementVectorIterator >( myElements.begin(), myElements.end() );
563 //================================================================================
565 * \brief clear myElements before re-filling after mesh compacting
567 //================================================================================
569 void SMESHDS_GroupOnFilter::tmpClear()
571 std::vector< const SMDS_MeshElement*> newElems( myElements.size() );
572 myElements.swap( newElems );
576 //================================================================================
578 * \brief Re-fill myElements after mesh compacting
580 //================================================================================
582 void SMESHDS_GroupOnFilter::add( const SMDS_MeshElement* element )
584 myElements.push_back( element );