Salome HOME
SHAPERSTUDY: Break Link when geometry not changed
[modules/smesh.git] / src / SMESH_I / SMESH_Mesh_i.cxx
index 04e0371bc953e94c1595f6badd1be3c982a3f1d3..e21d35e17ef1d724706680b68bc5fb773339662f 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2007-2019  CEA/DEN, EDF R&D, OPEN CASCADE
+// Copyright (C) 2007-2020  CEA/DEN, EDF R&D, OPEN CASCADE
 //
 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
@@ -114,6 +114,7 @@ SMESH_Mesh_i::SMESH_Mesh_i( PortableServer::POA_ptr thePOA,
   _impl          = NULL;
   _gen_i         = gen_i;
   _id            = _idGenerator++;
+  _nbInvalidHypos= -1;
   _editor        = NULL;
   _previewEditor = NULL;
   _preMeshInfo   = NULL;
@@ -257,38 +258,145 @@ GEOM::GEOM_Object_ptr SMESH_Mesh_i::GetShapeToMesh()
 
 //================================================================================
 /*!
-* \brief Replaces a shape in the mesh
+* \brief Replace a shape in the mesh
 */
 //================================================================================
+
 void SMESH_Mesh_i::ReplaceShape(GEOM::GEOM_Object_ptr theNewGeom)
   throw (SALOME::SALOME_Exception)
 {
+  // check if geometry changed
+  bool geomChanged = true;
+  GEOM::GEOM_Object_var oldGeom = GetShapeToMesh();
+  if ( !theNewGeom->_is_nil() && !oldGeom->_is_nil() )
+    geomChanged = ( //oldGeom->_is_equivalent( theNewGeom ) ||
+                   oldGeom->GetTick() < theNewGeom->GetTick() );
+
   TopoDS_Shape S = _impl->GetShapeToMesh();
   GEOM_Client* geomClient = _gen_i->GetShapeReader();
   TCollection_AsciiString aIOR;
-  if (geomClient->Find(S, aIOR)) {
-    geomClient->RemoveShapeFromBuffer(aIOR);
+  CORBA::String_var ior;
+  if ( geomClient->Find( S, aIOR ))
+    geomClient->RemoveShapeFromBuffer( aIOR );
+
+  // clear buffer also for sub-groups
+  const std::set<SMESHDS_GroupBase*>& groups = _impl->GetMeshDS()->GetGroups();
+  std::set<SMESHDS_GroupBase*>::const_iterator g = groups.begin();
+  for (; g != groups.end(); ++g)
+    if (const SMESHDS_GroupOnGeom* group = dynamic_cast<SMESHDS_GroupOnGeom*>(*g))
+    {
+      const TopoDS_Shape& s = group->GetShape();
+      if ( geomClient->Find( s, aIOR ))
+        geomClient->RemoveShapeFromBuffer( aIOR );
+    }
+
+  typedef struct {
+    int shapeID, fromID, toID; // indices of elements of a sub-mesh
+  } TRange;
+  std::vector< TRange > elemRanges, nodeRanges; // elements of sub-meshes
+  std::vector< SMDS_PositionPtr > positions; // node positions
+  SMESHDS_Mesh* meshDS = _impl->GetMeshDS();
+  if ( !geomChanged )
+  {
+    // store positions of elements on geometry
+    Load();
+    if ( meshDS->MaxNodeID()    > meshDS->NbNodes() ||
+         meshDS->MaxElementID() > meshDS->NbElements() )
+    {
+      meshDS->Modified();
+      meshDS->CompactMesh();
+    }
+    positions.resize( meshDS->NbNodes() + 1 );
+    for ( SMDS_NodeIteratorPtr nodeIt = meshDS->nodesIterator(); nodeIt->more(); )
+    {
+      const SMDS_MeshNode* n = nodeIt->next();
+      positions[ n->GetID() ] = n->GetPosition();
+    }
+
+    // remove elements from sub-meshes to avoid their removal at hypotheses addition
+    for ( int isNode = 0; isNode < 2; ++isNode )
+    {
+      std::vector< TRange > & ranges = isNode ? nodeRanges : elemRanges;
+      ranges.reserve( meshDS->MaxShapeIndex() + 10 );
+      ranges.push_back( TRange{ 0,0,0 });
+      SMDS_ElemIteratorPtr elemIt = meshDS->elementsIterator( isNode ? SMDSAbs_Node : SMDSAbs_All );
+      while ( elemIt->more() )
+      {
+        const SMDS_MeshElement* e = elemIt->next();
+        const int          elemID = e->GetID();
+        const int         shapeID = e->GetShapeID();
+        TRange &        lastRange = ranges.back();
+        if ( lastRange.shapeID != shapeID ||
+             lastRange.toID    != elemID )
+          ranges.push_back( TRange{ shapeID, elemID, elemID + 1 });
+        else
+          lastRange.toID = elemID + 1;
+
+        if ( SMESHDS_SubMesh* sm = meshDS->MeshElements( shapeID ))
+        {
+          if ( isNode ) sm->RemoveNode( static_cast< const SMDS_MeshNode *>( e ));
+          else          sm->RemoveElement( e );
+        }
+      }
+    }
   }
 
-  // re-assign global hypotheses to the new shape
-  _mainShapeTick = -1;
-  CheckGeomModif( true );
 
   // update the reference to theNewGeom (needed for correct execution of a dumped python script)
-  SALOMEDS::SObject_var aSO = _gen_i->ObjectToSObject(_this());
-  if (!aSO->_is_nil()) {
-    SALOMEDS::SObject_var aShapeRefSO;
-    if (aSO->FindSubObject(1, aShapeRefSO)) {
-      _gen_i->getStudyServant()->NewBuilder()->Addreference(
-        aShapeRefSO, _gen_i->getStudyServant()->FindObjectID(theNewGeom->GetStudyEntry()));
+  SMESH::SMESH_Mesh_var   me = _this();
+  SALOMEDS::SObject_wrap aSO = _gen_i->ObjectToSObject( me );
+  CORBA::String_var    entry = theNewGeom->GetStudyEntry();
+  if ( !aSO->_is_nil() )
+  {
+    SALOMEDS::SObject_wrap aShapeRefSO;
+    if ( aSO->FindSubObject( _gen_i->GetRefOnShapeTag(), aShapeRefSO.inout() ))
+    {
+      SALOMEDS::SObject_wrap    aShapeSO = _gen_i->getStudyServant()->FindObjectID( entry );
+      SALOMEDS::StudyBuilder_var builder = _gen_i->getStudyServant()->NewBuilder();
+      builder->Addreference( aShapeRefSO, aShapeSO );
     }
   }
 
-  TPythonDump() <<  SMESH::SMESH_Mesh_var(_this()) << ".ReplaceShape( "
-    << theNewGeom->GetStudyEntry() << " )";
+  // re-assign global hypotheses to the new shape
+  _mainShapeTick = geomChanged ? -1 : theNewGeom->GetTick();
+  CheckGeomModif( true );
+
+  if ( !geomChanged )
+  {
+    // restore positions of elements on geometry
+    for ( int isNode = 0; isNode < 2; ++isNode )
+    {
+      std::vector< TRange > & ranges = isNode ? nodeRanges : elemRanges;
+      for ( size_t i = 1; i < ranges.size(); ++i )
+      {
+        int elemID = ranges[ i ].fromID;
+        int   toID = ranges[ i ].toID;
+        SMESHDS_SubMesh * smDS = meshDS->NewSubMesh( ranges[ i ].shapeID );
+        if ( isNode )
+          for ( ; elemID < toID; ++elemID )
+            smDS->AddNode( meshDS->FindNode( elemID ));
+        else
+          for ( ; elemID < toID; ++elemID )
+            smDS->AddElement( meshDS->FindElement( elemID ));
+
+        if ( SMESH_subMesh* sm = _impl->GetSubMeshContaining( ranges[ i ].shapeID ))
+          sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
+      }
+    }
+    for ( unsigned int nodeID = 1; nodeID < positions.size(); ++nodeID )
+      if ( positions[ nodeID ])
+        if ( SMDS_MeshNode* n = const_cast< SMDS_MeshNode*>( meshDS->FindNode( nodeID )))
+          n->SetPosition( positions[ nodeID ], n->GetShapeID() );
+
+    // restore icons
+    _gen_i->UpdateIcons( SMESH::SMESH_Mesh_var( _this() ));
+  }
 
   TPythonDump() << "SHAPERSTUDY.breakLinkForSubElements(salome.ObjectToSObject("
-    << SMESH::SMESH_Mesh_var(_this()) <<".GetMesh()), " << theNewGeom->GetStudyEntry() << ")";
+                << me <<".GetMesh()), " << entry.in() << ")";
+
+  TPythonDump() <<  me << ".ReplaceShape( " << entry.in() << " )";
+
 }
 
 //================================================================================
@@ -684,6 +792,8 @@ SMESH_Mesh_i::AddHypothesis(GEOM::GEOM_Object_ptr       aSubShape,
   if ( _preMeshInfo )
     _preMeshInfo->ForgetOrLoad();
 
+  const int prevNbMeshEnt = _impl->NbNodes() + _impl->GetMeshDS()->NbElements();
+
   std::string error;
   SMESH_Hypothesis::Hypothesis_Status status = addHypothesis( aSubShape, anHyp, &error );
   anErrorText = error.c_str();
@@ -692,7 +802,10 @@ SMESH_Mesh_i::AddHypothesis(GEOM::GEOM_Object_ptr       aSubShape,
   if ( !SMESH_Hypothesis::IsStatusFatal(status) )
   {
     _gen_i->AddHypothesisToShape( mesh, aSubShape, anHyp );
-    _gen_i->UpdateIcons( mesh );
+
+    int newNbMeshEnt = _impl->NbNodes() + _impl->GetMeshDS()->NbElements();
+    if ( newNbMeshEnt != prevNbMeshEnt )
+      _gen_i->UpdateIcons( mesh );
   }
   if(MYDEBUG) MESSAGE( " AddHypothesis(): status = " << status );
 
@@ -936,13 +1049,15 @@ SMESH::SMESH_subMesh_ptr SMESH_Mesh_i::GetSubMesh(GEOM::GEOM_Object_ptr aSubShap
 
     //Get or Create the SMESH_subMesh object implementation
 
-    int subMeshId = _impl->GetMeshDS()->ShapeToIndex( myLocSubShape );
-
-    if ( !subMeshId && ! _impl->GetMeshDS()->IsGroupOfSubShapes( myLocSubShape ))
+    TopoDS_Iterator it( myLocSubShape );
+    int   subMeshId = _impl->GetMeshDS()->ShapeToIndex( myLocSubShape );
+    bool isValidSub = ( subMeshId || _impl->GetMeshDS()->IsGroupOfSubShapes( myLocSubShape ));
+    if ( isValidSub && myLocSubShape.ShapeType() == TopAbs_COMPOUND )
+      isValidSub = !it.Value().IsSame( _impl->GetShapeToMesh() );
+    if ( !isValidSub )
     {
-      TopoDS_Iterator it( myLocSubShape );
       if ( it.More() )
-        THROW_SALOME_CORBA_EXCEPTION("not sub-shape of the main shape", SALOME::BAD_PARAM);
+        THROW_SALOME_CORBA_EXCEPTION("Not a sub-shape of the main shape", SALOME::BAD_PARAM);
     }
     subMesh = getSubMesh( subMeshId );
 
@@ -2017,9 +2132,9 @@ void SMESH_Mesh_i::addGeomGroupData(GEOM::GEOM_Object_ptr theGeomObj,
   if ( groupSO->_is_nil() )
     return;
   // group indices
-  GEOM::GEOM_Gen_var geomGen = _gen_i->GetGeomEngine( theGeomObj );
-  GEOM::GEOM_IGroupOperations_ptr groupOp = geomGen->GetIGroupOperations();
-  GEOM::ListOfLong_var ids = groupOp->GetObjects( theGeomObj );
+  GEOM::GEOM_Gen_var               geomGen = _gen_i->GetGeomEngine( theGeomObj );
+  GEOM::GEOM_IGroupOperations_wrap groupOp = geomGen->GetIGroupOperations();
+  GEOM::ListOfLong_var                 ids = groupOp->GetObjects( theGeomObj );
 
   // store data
   _geomGroupData.push_back( TGeomGroupData() );
@@ -2065,57 +2180,82 @@ enum { ONLY_IF_CHANGED, IS_BREAK_LINK, MAIN_TRANSFORMED };
 TopoDS_Shape SMESH_Mesh_i::newGroupShape( TGeomGroupData & groupData, int how )
 {
   TopoDS_Shape newShape;
+  SALOMEDS::SObject_wrap groupSO;
 
   if ( how == IS_BREAK_LINK )
   {
     SALOMEDS::SObject_wrap meshSO = _gen_i->ObjectToSObject( groupData._smeshObject );
-    SALOMEDS::SObject_wrap geomRefSO, geomSO;
+    SALOMEDS::SObject_wrap geomRefSO;
     if ( !meshSO->_is_nil() &&
-         meshSO->FindSubObject( SMESH::Tag_RefOnShape, geomRefSO.inout() ) &&
-         geomRefSO->ReferencedObject( geomSO.inout() ))
+         meshSO->FindSubObject( SMESH::Tag_RefOnShape, geomRefSO.inout() ))
     {
-      CORBA::Object_var  geomObj = _gen_i->SObjectToObject( geomSO );
-      GEOM::GEOM_Object_var geom = GEOM::GEOM_Object::_narrow( geomObj );
-      newShape = _gen_i->GeomObjectToShape( geom );
+      geomRefSO->ReferencedObject( groupSO.inout() );
     }
   }
   else
   {
     // get geom group
-    SALOMEDS::SObject_wrap groupSO = SMESH_Gen_i::getStudyServant()->FindObjectID( groupData._groupEntry.c_str() );
-    if ( !groupSO->_is_nil() )
+    groupSO = _gen_i->getStudyServant()->FindObjectID( groupData._groupEntry.c_str() );
+  }
+
+  if ( groupSO->_is_nil() )
+    return newShape;
+
+  CORBA::Object_var      groupObj = _gen_i->SObjectToObject( groupSO );
+  GEOM::GEOM_Object_var geomGroup = GEOM::GEOM_Object::_narrow( groupObj );
+  if ( geomGroup->_is_nil() )
+    return newShape;
+
+  // get indices of group items
+  set<int> curIndices;
+  GEOM::GEOM_Gen_var               geomGen = _gen_i->GetGeomEngine( geomGroup );
+  GEOM::GEOM_IGroupOperations_wrap groupOp = geomGen->GetIGroupOperations();
+  GEOM::ListOfLong_var                 ids = groupOp->GetObjects( geomGroup );
+  for ( CORBA::ULong i = 0; i < ids->length(); ++i )
+    curIndices.insert( ids[i] );
+
+  bool sameIndices = ( groupData._indices == curIndices );
+  if ( how == ONLY_IF_CHANGED && sameIndices )
+    return newShape; // group not changed
+
+  // update data
+  CORBA::String_var entry = geomGroup->GetStudyEntry();
+  groupData._groupEntry = entry.in();
+  groupData._indices = curIndices;
+
+  newShape = _gen_i->GeomObjectToShape( geomGroup );
+
+  // check if newShape is up-to-date
+  if ( !newShape.IsNull() && ids->length() > 0 )
+  {
+    bool toUpdate = ! _impl->GetMeshDS()->IsGroupOfSubShapes( newShape );
+    if ( !toUpdate )
+    {
+      TopExp_Explorer exp( newShape, (TopAbs_ShapeEnum)( groupOp->GetType( geomGroup )));
+      for ( ; exp.More() && !toUpdate; exp.Next() )
+      {
+        int ind = _impl->GetMeshDS()->ShapeToIndex( exp.Current() );
+        toUpdate = ( curIndices.erase( ind ) == 0 );
+      }
+      if ( !curIndices.empty() )
+        toUpdate = true;
+    }
+    if ( toUpdate )
     {
-      CORBA::Object_var groupObj = _gen_i->SObjectToObject( groupSO );
-      if ( CORBA::is_nil( groupObj )) return newShape;
-      GEOM::GEOM_Object_var geomGroup = GEOM::GEOM_Object::_narrow( groupObj );
-
-      // get indices of group items
-      set<int> curIndices;
-      GEOM::GEOM_Gen_var              geomGen = _gen_i->GetGeomEngine( geomGroup );
-      GEOM::GEOM_IGroupOperations_ptr groupOp = geomGen->GetIGroupOperations();
-      GEOM::ListOfLong_var                ids = groupOp->GetObjects( geomGroup );
-      for ( CORBA::ULong i = 0; i < ids->length(); ++i )
-        curIndices.insert( ids[i] );
-
-      if ( how == ONLY_IF_CHANGED && groupData._indices == curIndices )
-        return newShape; // group not changed
-
-      // update data
-      groupData._indices = curIndices;
-
-      GEOM_Client* geomClient = _gen_i->GetShapeReader();
-      if ( !geomClient ) return newShape;
+      GEOM_Client*    geomClient = _gen_i->GetShapeReader();
       CORBA::String_var groupIOR = geomGen->GetStringFromIOR( geomGroup );
       geomClient->RemoveShapeFromBuffer( groupIOR.in() );
       newShape = _gen_i->GeomObjectToShape( geomGroup );
     }
   }
-  if ( newShape.IsNull() ) {
+  else
+  {
     // geom group becomes empty - return empty compound
     TopoDS_Compound compound;
     BRep_Builder().MakeCompound(compound);
     newShape = compound;
   }
+
   return newShape;
 }
 
@@ -2208,7 +2348,7 @@ namespace
  */
 //=============================================================================
 
-void SMESH_Mesh_i::CheckGeomModif( bool isBreakLink )
+void SMESH_Mesh_i::CheckGeomModif( bool theIsBreakLink )
 {
   SMESH::SMESH_Mesh_var me = _this();
   GEOM::GEOM_Object_var mainGO = GetShapeToMesh();
@@ -2266,25 +2406,35 @@ void SMESH_Mesh_i::CheckGeomModif( bool isBreakLink )
 
   // Update after group modification
 
-  if ( mainGO->GetType() == GEOM_GROUP ||    // is group or not modified
-       mainGO->GetTick() == _mainShapeTick )
-  {
-    int nb = NbNodes() + NbElements();
-    CheckGeomGroupModif();
-    if ( nb != NbNodes() + NbElements() ) // something removed due to hypotheses change
-      _gen_i->UpdateIcons( me );
-    return;
-  }
+  const bool geomChanged = ( mainGO->GetTick() != _mainShapeTick );
+  if ( !theIsBreakLink )
+    if ( mainGO->GetType() == GEOM_GROUP || !geomChanged )  // is group or not modified
+    {
+      int nb = NbNodes() + NbElements();
+      CheckGeomGroupModif();
+      if ( nb != NbNodes() + NbElements() ) // something removed due to hypotheses change
+        _gen_i->UpdateIcons( me );
+      return;
+    }
 
-  // Update after shape modification
+  // Update after shape modification or breakLink w/o geometry change
 
   GEOM_Client* geomClient = _gen_i->GetShapeReader();
   if ( !geomClient ) return;
   GEOM::GEOM_Gen_var geomGen = _gen_i->GetGeomEngine( mainGO );
   if ( geomGen->_is_nil() ) return;
+  CORBA::String_var geomComponentType = geomGen->ComponentDataType();
+  bool isShaper = ( strcmp( geomComponentType.in(), "SHAPERSTUDY" ) == 0 );
 
-  CORBA::String_var ior = geomGen->GetStringFromIOR( mainGO );
-  geomClient->RemoveShapeFromBuffer( ior.in() );
+  SMESHDS_Mesh * meshDS = _impl->GetMeshDS();
+
+  TopoDS_Shape newShape = _gen_i->GeomObjectToShape( mainGO );
+  if ( meshDS->ShapeToIndex( newShape ) == 1 ) // not yet updated
+  {
+    CORBA::String_var ior = geomGen->GetStringFromIOR( mainGO );
+    geomClient->RemoveShapeFromBuffer( ior.in() );
+    newShape = _gen_i->GeomObjectToShape( mainGO );
+  }
 
   // Update data taking into account that if topology doesn't change
   // all sub-shapes change but IDs of sub-shapes remain (except for geom groups)
@@ -2292,18 +2442,14 @@ void SMESH_Mesh_i::CheckGeomModif( bool isBreakLink )
   if ( _preMeshInfo )
     _preMeshInfo->ForgetAllData();
 
-  
-  if (isBreakLink)
+  if ( geomChanged || !isShaper )
     _impl->Clear();
-  TopoDS_Shape newShape = _gen_i->GeomObjectToShape( mainGO );
   if ( newShape.IsNull() )
     return;
 
   _mainShapeTick = mainGO->GetTick();
 
-  SMESHDS_Mesh * meshDS = _impl->GetMeshDS();
-
-  // store data of groups on geometry
+  // store data of groups on geometry including new TopoDS_Shape's
   std::vector< TGroupOnGeomData > groupsData;
   const std::set<SMESHDS_GroupBase*>& groups = meshDS->GetGroups();
   groupsData.reserve( groups.size() );
@@ -2324,7 +2470,10 @@ void SMESH_Mesh_i::CheckGeomModif( bool isBreakLink )
       GEOM::GEOM_Object_var geom;
       if ( !gog->_is_nil() )
       {
-        if ( isBreakLink )
+        if ( !theIsBreakLink )
+          geom = gog->GetShape();
+
+        if ( theIsBreakLink || geom->_is_nil() )
         {
           SALOMEDS::SObject_wrap grpSO = _gen_i->ObjectToSObject( gog );
           SALOMEDS::SObject_wrap geomRefSO, geomSO;
@@ -2336,22 +2485,23 @@ void SMESH_Mesh_i::CheckGeomModif( bool isBreakLink )
             geom = GEOM::GEOM_Object::_narrow( geomObj );
           }
         }
-        else
-        {
-          geom = gog->GetShape();
-        }
       }
-      if ( !geom->_is_nil() )
+      if ( old2newShapeMap.IsBound( group->GetShape() ))
       {
-        CORBA::String_var ior = geomGen->GetStringFromIOR( geom );
-        geomClient->RemoveShapeFromBuffer( ior.in() );
-        groupsData.back()._shape = _gen_i->GeomObjectToShape( geom );
-        old2newShapeMap.Bind( group->GetShape(), groupsData.back()._shape );
+        groupsData.back()._shape = old2newShapeMap( group->GetShape() );
       }
-      else if ( old2newShapeMap.IsBound( group->GetShape() ))
+      else if ( !geom->_is_nil() )
       {
-        groupsData.back()._shape = old2newShapeMap( group->GetShape() );
+        groupsData.back()._shape = _gen_i->GeomObjectToShape( geom );
+        if ( meshDS->IsGroupOfSubShapes( groupsData.back()._shape ))
+        {
+          CORBA::String_var ior = geomGen->GetStringFromIOR( geom );
+          geomClient->RemoveShapeFromBuffer( ior.in() );
+          groupsData.back()._shape = _gen_i->GeomObjectToShape( geom );
+        }
+        old2newShapeMap.Bind( group->GetShape(), groupsData.back()._shape );
       }
+      
     }
   }
   // store assigned hypotheses
@@ -2364,7 +2514,7 @@ void SMESH_Mesh_i::CheckGeomModif( bool isBreakLink )
     ids2Hyps.push_back( make_pair( meshDS->ShapeToIndex( s ), hyps ));
   }
 
-  std::map< std::set<int>, int > ii2iMap; // group sub-ids to group id in SMESHDS
+  std::multimap< std::set<int>, int > ii2iMap; // group sub-ids to group id in SMESHDS
 
   // count shapes excluding compounds corresponding to geom groups
   int oldNbSubShapes = meshDS->MaxShapeIndex();
@@ -2398,19 +2548,43 @@ void SMESH_Mesh_i::CheckGeomModif( bool isBreakLink )
   }
 
   // re-add shapes (compounds) of geom groups
+  typedef std::map< std::vector< int >, TGeomGroupData* > TIndices2GroupData;
+  TIndices2GroupData ii2grData;
+  std::vector< int > ii;
   std::map< int, int > old2newIDs; // group IDs
-  std::list<TGeomGroupData>::iterator data = _geomGroupData.begin();
-  for ( ; data != _geomGroupData.end(); ++data )
+  std::list<TGeomGroupData>::iterator dataIt = _geomGroupData.begin();
+  for ( ; dataIt != _geomGroupData.end(); ++dataIt )
   {
+    TGeomGroupData* data = &(*dataIt);
+    ii.reserve( data->_indices.size() );
+    ii.assign( data->_indices.begin(), data->_indices.end() );
+    TIndices2GroupData::iterator ii2gd = ii2grData.insert( std::make_pair( ii, data )).first;
+    if ( ii2gd->second != data )
+    {
+      data->_groupEntry = ii2gd->second->_groupEntry;
+      data->_indices    = ii2gd->second->_indices;
+      continue;
+    }
+    const int  oldNbSub = data->_indices.size();
+    const int soleOldID = oldNbSub == 1 ? *data->_indices.begin() : 0;
     int oldID = 0;
-    std::map< std::set<int>, int >::iterator ii2i = ii2iMap.find( data->_indices );
+    std::multimap< std::set<int>, int >::iterator ii2i = ii2iMap.find( data->_indices );
     if ( ii2i != ii2iMap.end() )
+    {
       oldID = ii2i->second;
+      ii2iMap.erase( ii2i );
+    }
+    if ( !oldID && oldNbSub == 1 )
+      oldID = soleOldID;
+    if ( old2newIDs.count( oldID ))
+      continue;
+
+    int how = ( theIsBreakLink || !sameTopology ) ? IS_BREAK_LINK : MAIN_TRANSFORMED;
+    newShape = newGroupShape( *data, how );
 
-    TopoDS_Shape newShape = newGroupShape( *data, isBreakLink ? IS_BREAK_LINK : MAIN_TRANSFORMED );
     if ( !newShape.IsNull() )
     {
-      if ( meshDS->ShapeToIndex( newShape ) > 0 ) // a group reduced to one sub-shape
+      if ( oldNbSub > 1 && meshDS->ShapeToIndex( newShape ) > 0 ) // group reduced to one sub-shape
       {
         TopoDS_Compound compound;
         BRep_Builder().MakeCompound( compound );
@@ -2418,33 +2592,34 @@ void SMESH_Mesh_i::CheckGeomModif( bool isBreakLink )
         newShape = compound;
       }
       int newID = _impl->GetSubMesh( newShape )->GetId();
-      if ( oldID && oldID != newID )
+      if ( oldID /*&& oldID != newID*/ )
         old2newIDs.insert( std::make_pair( oldID, newID ));
+      if ( oldNbSub == 1 )
+        old2newIDs.insert( std::make_pair( soleOldID, newID ));
     }
   }
 
   // re-assign hypotheses
   for ( size_t i = 0; i < ids2Hyps.size(); ++i )
   {
-    if ( !sameTopology && ids2Hyps[i].first != 1 )
-      continue; // assign only global hypos
     int sID = ids2Hyps[i].first;
-    std::map< int, int >::iterator o2n = old2newIDs.find( sID );
-    if ( o2n != old2newIDs.end() )
-      sID = o2n->second;
+    if ( sID != 1 )
+    {
+      std::map< int, int >::iterator o2n = old2newIDs.find( sID );
+      if ( o2n != old2newIDs.end() )
+        sID = o2n->second;
+      else if ( !sameTopology )
+        continue;
+    }
     const TopoDS_Shape& s = meshDS->IndexToShape( sID );
-    const THypList&  hyps = ids2Hyps[i].second;
+    if ( s.IsNull() )
+      continue;
+    const THypList& hyps = ids2Hyps[i].second;
     THypList::const_iterator h = hyps.begin();
     for ( ; h != hyps.end(); ++h )
       _impl->AddHypothesis( s, (*h)->GetID() );
   }
 
-  if ( !sameTopology )
-  {
-    // remove invalid study sub-objects
-    CheckGeomGroupModif();
-  }
-  else
   {
     // restore groups on geometry
     for ( size_t i = 0; i < groupsData.size(); ++i )
@@ -2466,19 +2641,26 @@ void SMESH_Mesh_i::CheckGeomModif( bool isBreakLink )
         g->GetGroupDS()->SetColor( data._color );
     }
 
-    std::map< int, int >::iterator o2n = old2newIDs.begin();
-    for ( ; o2n != old2newIDs.end(); ++o2n )
+    if ( !sameTopology )
     {
-      int newID = o2n->second, oldID = o2n->first;
-      if ( !_mapSubMesh.count( oldID ))
-        continue;
-      _mapSubMesh   [ newID ] = _impl->GetSubMeshContaining( newID );
-      _mapSubMesh_i [ newID ] = _mapSubMesh_i [ oldID ];
-      _mapSubMeshIor[ newID ] = _mapSubMeshIor[ oldID ];
-      _mapSubMesh.   erase(oldID);
-      _mapSubMesh_i. erase(oldID);
-      _mapSubMeshIor.erase(oldID);
-      _mapSubMesh_i [ newID ]->changeLocalId( newID );
+      std::map< int, int >::iterator o2n = old2newIDs.begin();
+      for ( ; o2n != old2newIDs.end(); ++o2n )
+      {
+        int newID = o2n->second, oldID = o2n->first;
+        if ( newID == oldID || !_mapSubMesh.count( oldID ))
+          continue;
+        if ( newID > 0 )
+        {
+          _mapSubMesh   [ newID ] = _impl->GetSubMeshContaining( newID );
+          _mapSubMesh_i [ newID ] = _mapSubMesh_i [ oldID ];
+          _mapSubMeshIor[ newID ] = _mapSubMeshIor[ oldID ];
+        }
+        _mapSubMesh.   erase(oldID);
+        _mapSubMesh_i. erase(oldID);
+        _mapSubMeshIor.erase(oldID);
+        if ( newID > 0 )
+          _mapSubMesh_i [ newID ]->changeLocalId( newID );
+      }
     }
 
     // update _mapSubMesh
@@ -2487,9 +2669,15 @@ void SMESH_Mesh_i::CheckGeomModif( bool isBreakLink )
       i_sm->second = _impl->GetSubMesh( meshDS->IndexToShape( i_sm->first ));
   }
 
-  _gen_i->UpdateIcons( SMESH::SMESH_Mesh_var( _this() ));
+  if ( !sameTopology )
+  {
+    // remove invalid study sub-objects
+    CheckGeomGroupModif();
+  }
 
-  if ( !isBreakLink )
+  _gen_i->UpdateIcons( me );
+
+  if ( !theIsBreakLink && isShaper )
   {
     SALOMEDS::SObject_wrap meshSO = _gen_i->ObjectToSObject( me );
     if ( !meshSO->_is_nil() )
@@ -2565,13 +2753,21 @@ void SMESH_Mesh_i::CheckGeomGroupModif()
   {
     SMESH::SMESH_GroupBase_ptr group = i_gr->second;
     ++i_gr;
-    SALOMEDS::SObject_wrap        groupSO = _gen_i->ObjectToSObject( group ), refSO;
+    SALOMEDS::SObject_wrap        groupSO = _gen_i->ObjectToSObject( group ), refSO, geomSO;
     SMESH::SMESH_GroupOnGeom_var   onGeom = SMESH::SMESH_GroupOnGeom::_narrow  ( group );
     SMESH::SMESH_GroupOnFilter_var onFilt = SMESH::SMESH_GroupOnFilter::_narrow( group );
     bool isValidGeom = false;
     if ( !onGeom->_is_nil() )
     {
-      isValidGeom = ( ! GEOM::GEOM_Object_var( onGeom->GetShape() )->_is_nil() );
+      isValidGeom = ( ! GEOM::GEOM_Object_var( onGeom->GetShape() )->_is_nil() ); // check TopoDS
+      if ( !isValidGeom ) // check reference
+      {
+        isValidGeom = ( ! groupSO->_is_nil() &&
+                        groupSO->FindSubObject( SMESH::Tag_RefOnShape, refSO.inout() ) &&
+                        refSO->ReferencedObject( geomSO.inout() ) &&
+                        ! geomSO->_is_nil() &&
+                        !CORBA::is_nil( CORBA::Object_var( geomSO->GetObject() )));
+      }
     }
     else if ( !onFilt->_is_nil() )
     {
@@ -3223,40 +3419,55 @@ namespace
     SMESH_Mesh_i* _mesh;
     TCallUp_i(SMESH_Mesh_i* mesh):_mesh(mesh) {}
     virtual void RemoveGroup (const int theGroupID) { _mesh->removeGroup( theGroupID ); }
-    virtual void HypothesisModified (int theHypID)  { _mesh->onHypothesisModified( theHypID ); }
+    virtual void HypothesisModified( int hypID,
+                                     bool updIcons) { _mesh->onHypothesisModified( hypID,
+                                                                                   updIcons ); }
     virtual void Load ()                            { _mesh->Load(); }
+    virtual bool IsLoaded()                         { return _mesh->IsLoaded(); }
   };
 }
 
 //================================================================================
 /*!
- * \brief callback from _impl to forget not loaded mesh data (issue 0021208)
+ * \brief callback from _impl to
+ *     1) forget not loaded mesh data (issue 0021208)
+ *     2) mark hypothesis as valid
  */
 //================================================================================
 
-void SMESH_Mesh_i::onHypothesisModified(int theHypID)
+void SMESH_Mesh_i::onHypothesisModified(int theHypID, bool theUpdateIcons)
 {
   if ( _preMeshInfo )
     _preMeshInfo->ForgetOrLoad();
 
-  SMESH::SMESH_Mesh_var mesh = _this();
-  _gen_i->UpdateIcons( mesh );
+  if ( theUpdateIcons )
+  {
+    SMESH::SMESH_Mesh_var mesh = _this();
+    _gen_i->UpdateIcons( mesh );
+  }
 
-  // mark a hypothesis as valid after edition
-  SALOMEDS::SComponent_wrap smeshComp = _gen_i->PublishComponent();
-  SALOMEDS::SObject_wrap hypRoot;
-  if ( !smeshComp->_is_nil() && 
-       smeshComp->FindSubObject( _gen_i->GetHypothesisRootTag(), hypRoot.inout() ))
+  if ( _nbInvalidHypos != 0 )
   {
-    SALOMEDS::ChildIterator_wrap anIter = _gen_i->getStudyServant()->NewChildIterator( hypRoot );
-    for ( ; anIter->More(); anIter->Next() )
+    // mark a hypothesis as valid after edition
+    int nbInvalid = 0;
+    SALOMEDS::SComponent_wrap smeshComp = _gen_i->PublishComponent();
+    SALOMEDS::SObject_wrap hypRoot;
+    if ( !smeshComp->_is_nil() &&
+         smeshComp->FindSubObject( _gen_i->GetHypothesisRootTag(), hypRoot.inout() ))
     {
-      SALOMEDS::SObject_wrap    hypSO = anIter->Value();
-      CORBA::Object_var           obj = _gen_i->SObjectToObject( hypSO );
-      SMESH::SMESH_Hypothesis_var hyp = SMESH::SMESH_Hypothesis::_narrow( obj );
-      if ( !hyp->_is_nil() && hyp->GetId() == theHypID )
-        _gen_i->HighLightInvalid( hyp, false );
+      SALOMEDS::ChildIterator_wrap anIter = _gen_i->getStudyServant()->NewChildIterator( hypRoot );
+      for ( ; anIter->More(); anIter->Next() )
+      {
+        SALOMEDS::SObject_wrap    hypSO = anIter->Value();
+        CORBA::Object_var           obj = _gen_i->SObjectToObject( hypSO );
+        SMESH::SMESH_Hypothesis_var hyp = SMESH::SMESH_Hypothesis::_narrow( obj );
+        if ( !hyp->_is_nil() && hyp->GetId() == theHypID )
+          _gen_i->HighLightInvalid( hyp, false );
+        else
+          nbInvalid += _gen_i->IsInvalid( hypSO );
+      }
     }
+    _nbInvalidHypos = nbInvalid;
   }
 }