// Copyright (C) 2007-2022  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
//
// 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, 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
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
//
// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
//
//  File   : SMESH_Mesh_i.cxx
//  Author : Paul RASCLE, EDF
//  Module : SMESH

#include "SMESH_Mesh_i.hxx"

#include "DriverMED_R_SMESHDS_Mesh.h"
#include "DriverMED_W_Field.h"
#include "DriverMED_W_SMESHDS_Mesh.h"
#include "MED_Factory.hxx"
#include "SMDS_LinearEdge.hxx"
#include "SMDS_EdgePosition.hxx"
#include "SMDS_ElemIterator.hxx"
#include "SMDS_FacePosition.hxx"
#include "SMDS_IteratorOnIterators.hxx"
#include "SMDS_MeshGroup.hxx"
#include "SMDS_SetIterator.hxx"
#include "SMDS_StdIterator.hxx"
#include "SMDS_VolumeTool.hxx"
#include "SMESHDS_Command.hxx"
#include "SMESHDS_CommandType.hxx"
#include "SMESHDS_Group.hxx"
#include "SMESHDS_GroupOnGeom.hxx"
#include "SMESH_Controls.hxx"
#include "SMESH_File.hxx"
#include "SMESH_Filter_i.hxx"
#include "SMESH_Gen_i.hxx"
#include "SMESH_Group.hxx"
#include "SMESH_Group_i.hxx"
#include "SMESH_Mesh.hxx"
#include "SMESH_MeshAlgos.hxx"
#include "SMESH_MeshEditor.hxx"
#include "SMESH_MeshEditor_i.hxx"
#include "SMESH_MeshPartDS.hxx"
#include "SMESH_MesherHelper.hxx"
#include "SMESH_PreMeshInfo.hxx"
#include "SMESH_PythonDump.hxx"
#include "SMESH_subMesh_i.hxx"

#include <SALOMEDS_Attributes_wrap.hxx>
#include <SALOMEDS_wrap.hxx>
#include <Utils_ExceptHandlers.hxx>
#include <utilities.h>

#include <GEOMImpl_Types.hxx>
#include <GEOM_wrap.hxx>

// OCCT Includes
#include <BRep_Builder.hxx>
#include <Standard_ErrorHandler.hxx>
#include <TColStd_MapOfInteger.hxx>
#include <TopExp.hxx>
#include <TopExp_Explorer.hxx>
#include <TopTools_DataMapOfShapeShape.hxx>
#include <TopTools_MapIteratorOfMapOfShape.hxx>
#include <TopTools_MapOfShape.hxx>
#include <TopoDS_Compound.hxx>

// STL Includes
#include <algorithm>
#include <iostream>
#include <sstream>

#include <vtkUnstructuredGridWriter.h>

// to pass CORBA exception and TooLargeForExport exception through SMESH_TRY
#define SMY_OWN_CATCH                                                                           \
  catch( SALOME::SALOME_Exception& se ) { throw se; }                                           \
  catch( ::SMESH_Mesh::TooLargeForExport& ex )                                                  \
  { SALOME::ExceptionStruct se = {                                                              \
      SALOME::COMM,                                                                             \
      CORBA::string_dup(SMESH_Comment("Mesh is too large for export in format ") << ex.what()), \
      CORBA::string_dup(SMESH_Comment("format=") <<  ex.what() ), 0 };                          \
    throw SALOME::SALOME_Exception( se );  }

#include "SMESH_TryCatch.hxx" // include after OCCT headers!

#ifdef _DEBUG_
static int MYDEBUG = 0;
#else
static int MYDEBUG = 0;
#endif

using namespace std;
using SMESH::TPythonDump;
using SMESH::TVar;

int SMESH_Mesh_i::_idGenerator = 0;

//=============================================================================
/*!
 *  Constructor
 */
//=============================================================================

SMESH_Mesh_i::SMESH_Mesh_i( PortableServer::POA_ptr thePOA,
                            SMESH_Gen_i*            gen_i )
: SALOME::GenericObj_i( thePOA )
{
  _impl          = NULL;
  _gen_i         = gen_i;
  _id            = _idGenerator++;
  _nbInvalidHypos= -1;
  _editor        = NULL;
  _previewEditor = NULL;
  _preMeshInfo   = NULL;
  _mainShapeTick = 0;
}

//=============================================================================
/*!
 *  Destructor
 */
//=============================================================================

SMESH_Mesh_i::~SMESH_Mesh_i()
{
  // destroy groups
  map<int, SMESH::SMESH_GroupBase_ptr>::iterator itGr;
  for (itGr = _mapGroups.begin(); itGr != _mapGroups.end(); itGr++)
    if (SMESH_GroupBase_i* aGroup = SMESH::DownCast<SMESH_GroupBase_i*>(itGr->second))
    {
      aGroup->UnRegister();
      SMESH::SMESH_GroupBase_var( itGr->second );
    }
  _mapGroups.clear();

  // destroy submeshes
  map<int, SMESH::SMESH_subMesh_ptr>::iterator itSM;
  for ( itSM = _mapSubMeshIor.begin(); itSM != _mapSubMeshIor.end(); itSM++ )
    if ( SMESH_subMesh_i* aSubMesh = SMESH::DownCast<SMESH_subMesh_i*>( itSM->second ))
    {
      aSubMesh->UnRegister();
      SMESH::SMESH_subMesh_var( itSM->second );
    }
  _mapSubMeshIor.clear();

  // destroy hypotheses. _mapHypo contains all hyps ever been assigned
  map<int, SMESH::SMESH_Hypothesis_ptr>::iterator itH;
  for ( itH = _mapHypo.begin(); itH != _mapHypo.end(); itH++ ) {
    if ( SMESH_Hypothesis_i* hyp_i = SMESH::DownCast<SMESH_Hypothesis_i*>( itH->second ))
      if ( SMESH_Hypothesis * smHyp = _impl->GetHypothesis( itH->first ))
        if ( _impl->GetMeshDS()->IsUsedHypothesis( smHyp ))
          hyp_i->UnRegister();

    SMESH::SMESH_Hypothesis_var( itH->second ); // decref CORBA object
  }
  _mapHypo.clear();

  // clear cached shapes if no more meshes remain; (the cache is blame,
  // together with publishing, of spent time increasing in issue 22874)
  if ( _impl->NbMeshes() == 1 )
    _gen_i->GetShapeReader()->ClearClientBuffer();

  delete _editor; _editor = NULL;
  delete _previewEditor; _previewEditor = NULL;
  delete _impl; _impl = NULL;
  delete _preMeshInfo; _preMeshInfo = NULL;
}

//=============================================================================
/*!
 *  SetShape
 *
 *  Associate <this> mesh with <theShape> and put a reference
 *  to <theShape> into the current study;
 *  the previous shape is substituted by the new one.
 */
//=============================================================================

void SMESH_Mesh_i::SetShape( GEOM::GEOM_Object_ptr theShapeObject )
{
  Unexpect aCatch(SALOME_SalomeException);
  try {
    _impl->ShapeToMesh( _gen_i->GeomObjectToShape( theShapeObject ));
  }
  catch(SALOME_Exception & S_ex) {
    THROW_SALOME_CORBA_EXCEPTION(S_ex.what(), SALOME::BAD_PARAM);
  }
  // to track changes of GEOM groups
  SMESH::SMESH_Mesh_var mesh = _this();
  addGeomGroupData( theShapeObject, mesh );
  if ( !CORBA::is_nil( theShapeObject ))
    _mainShapeTick = theShapeObject->GetTick();
}

//================================================================================
/*!
 * \brief Return true if mesh has a shape to build a shape on
 */
//================================================================================

CORBA::Boolean SMESH_Mesh_i::HasShapeToMesh()
{
  Unexpect aCatch(SALOME_SalomeException);
  bool res = false;
  try {
    res = _impl->HasShapeToMesh();
  }
  catch(SALOME_Exception & S_ex) {
    THROW_SALOME_CORBA_EXCEPTION(S_ex.what(), SALOME::BAD_PARAM);
  }
  return res;
}

//================================================================================
/*!
 * \brief Return the shape to mesh
 */
//================================================================================

GEOM::GEOM_Object_ptr SMESH_Mesh_i::GetShapeToMesh()
{
  Unexpect aCatch(SALOME_SalomeException);
  GEOM::GEOM_Object_var aShapeObj;
  try {
    TopoDS_Shape S = _impl->GetMeshDS()->ShapeToMesh();
    if ( !S.IsNull() )
    {
      aShapeObj = _gen_i->ShapeToGeomObject( S );
      if ( aShapeObj->_is_nil() )
      {
        // S was removed from GEOM_Client by newGroupShape() called by other mesh;
        // find GEOM_Object by entry (IPAL52735)
        list<TGeomGroupData>::iterator data = _geomGroupData.begin();
        for ( ; data != _geomGroupData.end(); ++data )
          if ( data->_smeshObject->_is_equivalent( _this() ))
          {
            SALOMEDS::SObject_wrap so = _gen_i->getStudyServant()->FindObjectID( data->_groupEntry.c_str() );
            CORBA::Object_var     obj = _gen_i->SObjectToObject( so );
            aShapeObj = GEOM::GEOM_Object::_narrow( obj );
            break;
          }
      }
    }
  }
  catch(SALOME_Exception & S_ex) {
    THROW_SALOME_CORBA_EXCEPTION(S_ex.what(), SALOME::BAD_PARAM);
  }
  return aShapeObj._retn();
}

//================================================================================
/*!
 * \brief Return false if the mesh is not yet fully loaded from the study file
 */
//================================================================================

CORBA::Boolean SMESH_Mesh_i::IsLoaded()
{
  Unexpect aCatch(SALOME_SalomeException);
  return !_preMeshInfo;
}

//================================================================================
/*!
 * \brief Load full mesh data from the study file
 */
//================================================================================

void SMESH_Mesh_i::Load()
{
  Unexpect aCatch(SALOME_SalomeException);
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();
}

//================================================================================
/*!
 * \brief Remove all nodes and elements
 */
//================================================================================

void SMESH_Mesh_i::Clear()
{
  Unexpect aCatch(SALOME_SalomeException);
  if ( _preMeshInfo )
    _preMeshInfo->ForgetOrLoad(); // load in case if !HasShapeToMesh()

  try {
    _impl->Clear();
    //CheckGeomGroupModif(); // issue 20145
  }
  catch(SALOME_Exception & S_ex) {
    THROW_SALOME_CORBA_EXCEPTION(S_ex.what(), SALOME::BAD_PARAM);
  }

  TPythonDump() <<  SMESH::SMESH_Mesh_var(_this()) << ".Clear()";

  SMESH::SMESH_Mesh_var mesh = _this();
  _gen_i->UpdateIcons( mesh );
}

//================================================================================
/*!
 * \brief Remove all nodes and elements for indicated shape
 */
//================================================================================

void SMESH_Mesh_i::ClearSubMesh(CORBA::Long ShapeID)
{
  Unexpect aCatch(SALOME_SalomeException);
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  try {
    _impl->ClearSubMesh( ShapeID );
  }
  catch(SALOME_Exception & S_ex) {
    THROW_SALOME_CORBA_EXCEPTION(S_ex.what(), SALOME::BAD_PARAM);
  }
  _impl->GetMeshDS()->Modified();

  TPythonDump() <<  SMESH::SMESH_Mesh_var(_this()) << ".ClearSubMesh( " << ShapeID << " )";
}

//=============================================================================
/*!
 * Convert enum Driver_Mesh::Status to SMESH::DriverMED_ReadStatus
 */
//=============================================================================

static SMESH::DriverMED_ReadStatus ConvertDriverMEDReadStatus (int theStatus)
{
  SMESH::DriverMED_ReadStatus res;
  switch (theStatus)
  {
  case DriverMED_R_SMESHDS_Mesh::DRS_OK:
    res = SMESH::DRS_OK; break;
  case DriverMED_R_SMESHDS_Mesh::DRS_EMPTY:
    res = SMESH::DRS_EMPTY; break;
  case DriverMED_R_SMESHDS_Mesh::DRS_WARN_RENUMBER:
    res = SMESH::DRS_WARN_RENUMBER; break;
  case DriverMED_R_SMESHDS_Mesh::DRS_WARN_SKIP_ELEM:
    res = SMESH::DRS_WARN_SKIP_ELEM; break;
  case DriverMED_R_SMESHDS_Mesh::DRS_WARN_DESCENDING:
    res = SMESH::DRS_WARN_DESCENDING; break;
  case DriverMED_R_SMESHDS_Mesh::DRS_FAIL:
  default:
    res = SMESH::DRS_FAIL; break;
  }
  return res;
}

//=============================================================================
/*!
 * Convert ::SMESH_ComputeError to SMESH::ComputeError
 */
//=============================================================================

static SMESH::ComputeError* ConvertComputeError( SMESH_ComputeErrorPtr errorPtr )
{
  SMESH::ComputeError_var errVar = new SMESH::ComputeError();
  errVar->subShapeID = -1;
  errVar->hasBadMesh = false;

  if ( !errorPtr || errorPtr->IsOK() )
  {
    errVar->code = SMESH::COMPERR_OK;
  }
  else
  {
    errVar->code    = ConvertDriverMEDReadStatus( errorPtr->myName );
    errVar->comment = errorPtr->myComment.c_str();
  }
  return errVar._retn();
}

//=============================================================================
/*!
 *  ImportMEDFile
 *
 *  Import mesh data from MED file
 */
//=============================================================================

SMESH::DriverMED_ReadStatus
SMESH_Mesh_i::ImportMEDFile( const char* theFileName, const char* theMeshName )
{
  Unexpect aCatch(SALOME_SalomeException);
  int status;
  try {
    status = _impl->MEDToMesh( theFileName, theMeshName );
  }
  catch( SALOME_Exception& S_ex ) {
    THROW_SALOME_CORBA_EXCEPTION(S_ex.what(), SALOME::BAD_PARAM);
  }
  catch ( ... ) {
    THROW_SALOME_CORBA_EXCEPTION("ImportMEDFile(): unknown exception", SALOME::BAD_PARAM);
  }

  CreateGroupServants();

  int major, minor, release;
  major = minor = release = 0;
  MED::GetMEDVersion(theFileName, major, minor, release);
  _medFileInfo           = new SMESH::MedFileInfo();
  _medFileInfo->fileName = theFileName;
  _medFileInfo->fileSize = 0;
  _medFileInfo->major    = major;
  _medFileInfo->minor    = minor;
  _medFileInfo->release  = release;
  _medFileInfo->fileSize = SMESH_File( theFileName ).size();

  return ConvertDriverMEDReadStatus(status);
}

//================================================================================
/*!
 * \brief Import mesh data from the CGNS file
 */
//================================================================================

SMESH::DriverMED_ReadStatus SMESH_Mesh_i::ImportCGNSFile( const char*  theFileName,
                                                          const int    theMeshIndex,
                                                          std::string& theMeshName )
{
  Unexpect aCatch(SALOME_SalomeException);
  int status;
  try {
    status = _impl->CGNSToMesh( theFileName, theMeshIndex, theMeshName );
  }
  catch( SALOME_Exception& S_ex ) {
    THROW_SALOME_CORBA_EXCEPTION(S_ex.what(), SALOME::BAD_PARAM);
  }
  catch ( ... ) {
    THROW_SALOME_CORBA_EXCEPTION("ImportCGNSFile(): unknown exception", SALOME::BAD_PARAM);
  }

  CreateGroupServants();

  _medFileInfo           = new SMESH::MedFileInfo();
  _medFileInfo->fileName = theFileName;
  _medFileInfo->major    = 0;
  _medFileInfo->minor    = 0;
  _medFileInfo->release  = 0;
  _medFileInfo->fileSize = SMESH_File( theFileName ).size();

  return ConvertDriverMEDReadStatus(status);
}

//================================================================================
/*!
 * \brief Return string representation of a MED file version comprising nbDigits
 */
//================================================================================

char* SMESH_Mesh_i::GetVersionString(CORBA::Long minor, CORBA::Short nbDigits)
{
  string ver = DriverMED_W_SMESHDS_Mesh::GetVersionString(minor, nbDigits);
  return CORBA::string_dup( ver.c_str() );
}

//================================================================================
/*!
 *  Return the list of med versions compatibles for write/append,
 *  encoded in 10*major+minor (for instance, code for med 3.2.1 is 32)
 */
//================================================================================

SMESH::long_array* SMESH_Mesh_i::GetMEDVersionsCompatibleForAppend()
{
  SMESH::long_array_var aResult = new SMESH::long_array();
  std::vector<int> mvok = MED::GetMEDVersionsAppendCompatible();
  long nbver = mvok.size();
  aResult->length( nbver );
  for ( int i = 0; i < nbver; i++ )
    aResult[i] = mvok[i];
  return aResult._retn();
}

//=============================================================================
/*!
 *  ImportUNVFile
 *
 *  Import mesh data from MED file
 */
//=============================================================================

int SMESH_Mesh_i::ImportUNVFile( const char* theFileName )
{
  SMESH_TRY;

  // Read mesh with name = <theMeshName> into SMESH_Mesh
  _impl->UNVToMesh( theFileName );

  CreateGroupServants();

  _medFileInfo           = new SMESH::MedFileInfo();
  _medFileInfo->fileName = theFileName;
  _medFileInfo->major    = 0;
  _medFileInfo->minor    = 0;
  _medFileInfo->release  = 0;
  _medFileInfo->fileSize = SMESH_File( theFileName ).size();

  SMESH_CATCH( SMESH::throwCorbaException );

  return 1;
}

//=============================================================================
/*!
 *  ImportSTLFile
 *
 *  Import mesh data from STL file
 */
//=============================================================================

int SMESH_Mesh_i::ImportSTLFile( const char* theFileName )
{
  SMESH_TRY;

  // Read mesh with name = <theMeshName> into SMESH_Mesh
  std::string name = _impl->STLToMesh( theFileName );
  if ( !name.empty() )
  {
    SALOMEDS::SObject_wrap meshSO = _gen_i->ObjectToSObject( _this() );
    _gen_i->SetName( meshSO, name.c_str() );
  }
  _medFileInfo           = new SMESH::MedFileInfo();
  _medFileInfo->fileName = theFileName;
  _medFileInfo->major    = 0;
  _medFileInfo->minor    = 0;
  _medFileInfo->release  = 0;
  _medFileInfo->fileSize = SMESH_File( theFileName ).size();

  SMESH_CATCH( SMESH::throwCorbaException );

  return 1;
}

//================================================================================
/*!
 * \brief Function used in SMESH_CATCH by ImportGMFFile()
 */
//================================================================================

namespace
{
  SMESH_ComputeErrorPtr exceptionToComputeError(const char* excText)
  {
    return SMESH_ComputeError::New( Driver_Mesh::DRS_FAIL, excText );
  }
}

//================================================================================
/*!
 * \brief Import data from a GMF file and return an error description
 */
//================================================================================

SMESH::ComputeError* SMESH_Mesh_i::ImportGMFFile( const char* theFileName,
                                                  bool        theMakeRequiredGroups )
{
  SMESH_ComputeErrorPtr error;

#undef SMESH_CAUGHT
#define SMESH_CAUGHT error =
  SMESH_TRY;

  error = _impl->GMFToMesh( theFileName, theMakeRequiredGroups );

  _medFileInfo           = new SMESH::MedFileInfo();
  _medFileInfo->fileName = theFileName;
  _medFileInfo->major    = 0;
  _medFileInfo->minor    = 0;
  _medFileInfo->release  = 0;
  _medFileInfo->fileSize = SMESH_File( theFileName ).size();

  SMESH_CATCH( exceptionToComputeError );
#undef SMESH_CAUGHT
#define SMESH_CAUGHT

  CreateGroupServants();

  return ConvertComputeError( error );
}

//=============================================================================
/*!
 * \brief Convert SMESH_Hypothesis::Hypothesis_Status into SMESH::Hypothesis_Status
 */
//=============================================================================

#define RETURNCASE(hyp_stat) case SMESH_Hypothesis::hyp_stat: return SMESH::hyp_stat;

SMESH::Hypothesis_Status SMESH_Mesh_i::ConvertHypothesisStatus
                         (SMESH_Hypothesis::Hypothesis_Status theStatus)
{
  switch (theStatus) {
  RETURNCASE( HYP_OK            );
  RETURNCASE( HYP_MISSING       );
  RETURNCASE( HYP_CONCURRENT    );
  RETURNCASE( HYP_BAD_PARAMETER );
  RETURNCASE( HYP_HIDDEN_ALGO   );
  RETURNCASE( HYP_HIDING_ALGO   );
  RETURNCASE( HYP_UNKNOWN_FATAL );
  RETURNCASE( HYP_INCOMPATIBLE  );
  RETURNCASE( HYP_NOTCONFORM    );
  RETURNCASE( HYP_ALREADY_EXIST );
  RETURNCASE( HYP_BAD_DIM       );
  RETURNCASE( HYP_BAD_SUBSHAPE  );
  RETURNCASE( HYP_BAD_GEOMETRY  );
  RETURNCASE( HYP_NEED_SHAPE    );
  RETURNCASE( HYP_INCOMPAT_HYPS );
  default:;
  }
  return SMESH::HYP_UNKNOWN_FATAL;
}

//=============================================================================
/*!
 *  AddHypothesis
 *
 *  Call internal addHypothesis() and then add a reference to <anHyp> under
 *  the SObject actually having a reference to <aSubShape>.
 *  NB: For this method to work, it is necessary to add a reference to sub-shape first.
 */
//=============================================================================

SMESH::Hypothesis_Status
SMESH_Mesh_i::AddHypothesis(GEOM::GEOM_Object_ptr       aSubShape,
                            SMESH::SMESH_Hypothesis_ptr anHyp,
                            CORBA::String_out           anErrorText)
{
  Unexpect aCatch(SALOME_SalomeException);

  const smIdType prevNbMeshEnt = NbNodes() + NbElements();

  if ( _preMeshInfo )
    _preMeshInfo->ForgetOrLoad();

  std::string error;
  SMESH_Hypothesis::Hypothesis_Status status = addHypothesis( aSubShape, anHyp, &error );
  anErrorText = error.c_str();

  SMESH::SMESH_Mesh_var mesh( _this() );
  if ( !SMESH_Hypothesis::IsStatusFatal(status) )
  {
    _gen_i->AddHypothesisToShape( mesh, aSubShape, anHyp );

    //int newNbMeshEnt = _impl->NbNodes() + _impl->GetMeshDS()->NbElements();
    if ( prevNbMeshEnt > 0 /*newNbMeshEnt != prevNbMeshEnt*/ )
      _gen_i->UpdateIcons( mesh );
  }
  if(MYDEBUG) MESSAGE( " AddHypothesis(): status = " << status );

  // Update Python script
  TPythonDump() << "status = " << mesh << ".AddHypothesis( "
                << aSubShape << ", " << anHyp << " )";

  return ConvertHypothesisStatus(status);
}

//================================================================================
/*!
 * \brief Create  a sub-mesh and add a hypothesis to it
 */
//================================================================================

SMESH_Hypothesis::Hypothesis_Status
SMESH_Mesh_i::addHypothesis(GEOM::GEOM_Object_ptr       aSubShape,
                            SMESH::SMESH_Hypothesis_ptr anHyp,
                            std::string*                anErrorText)
{
  if(MYDEBUG) MESSAGE("addHypothesis");

  if (CORBA::is_nil( aSubShape ) && HasShapeToMesh())
    THROW_SALOME_CORBA_EXCEPTION("bad Sub-shape reference",SALOME::BAD_PARAM);

  if (CORBA::is_nil( anHyp ))
    THROW_SALOME_CORBA_EXCEPTION("bad hypothesis reference",SALOME::BAD_PARAM);

  SMESH_Hypothesis::Hypothesis_Status status = SMESH_Hypothesis::HYP_OK;
  try
  {
    TopoDS_Shape myLocSubShape;
    //use PseudoShape in case if mesh has no shape
    if(HasShapeToMesh())
      myLocSubShape = _gen_i->GeomObjectToShape( aSubShape);
    else
      myLocSubShape = _impl->GetShapeToMesh();

    const int hypId = anHyp->GetId();
    std::string error;
    status = _impl->AddHypothesis( myLocSubShape, hypId, &error );
    if ( !SMESH_Hypothesis::IsStatusFatal(status) )
    {
      _mapHypo[hypId] = SMESH::SMESH_Hypothesis::_duplicate( anHyp );
      anHyp->Register();
      // assure there is a corresponding submesh
      if ( !_impl->IsMainShape( myLocSubShape )) {
        int shapeId = _impl->GetMeshDS()->ShapeToIndex( myLocSubShape );
        if ( _mapSubMesh_i.find( shapeId ) == _mapSubMesh_i.end() )
          SMESH::SMESH_subMesh_var( createSubMesh( aSubShape ));
      }
    }
    else if ( anErrorText )
    {
      *anErrorText = error;
    }
  }
  catch(SALOME_Exception & S_ex)
  {
    THROW_SALOME_CORBA_EXCEPTION(S_ex.what(), SALOME::BAD_PARAM);
  }
  return status;
}

//================================================================================
/*!
 * \brief Un-assign a hypothesis from a sub-mesh dedicate to the given sub-shape
 */
//================================================================================

SMESH::Hypothesis_Status SMESH_Mesh_i::RemoveHypothesis(GEOM::GEOM_Object_ptr       aSubShape,
                                                        SMESH::SMESH_Hypothesis_ptr anHyp)
{
  Unexpect aCatch(SALOME_SalomeException);
  if ( _preMeshInfo )
    _preMeshInfo->ForgetOrLoad();

  SMESH_Hypothesis::Hypothesis_Status status = removeHypothesis( aSubShape, anHyp );
  SMESH::SMESH_Mesh_var mesh = _this();

  if ( !SMESH_Hypothesis::IsStatusFatal(status) )
  {
    _gen_i->RemoveHypothesisFromShape( mesh, aSubShape, anHyp );
    _gen_i->UpdateIcons( mesh );
  }
  // Update Python script
  if(_impl->HasShapeToMesh())
    TPythonDump() << "status = " << mesh << ".RemoveHypothesis( "
                  << aSubShape << ", " << anHyp << " )";
  else
    TPythonDump() << "status = " << mesh << ".RemoveHypothesis( "
                  << anHyp << " )";

  return ConvertHypothesisStatus(status);
}

//=============================================================================
/*!
 * \brief Un-assign a hypothesis from a sub-mesh dedicate to the given sub-shape
 */
//=============================================================================

SMESH_Hypothesis::Hypothesis_Status
SMESH_Mesh_i::removeHypothesis(GEOM::GEOM_Object_ptr       aSubShape,
                               SMESH::SMESH_Hypothesis_ptr anHyp)
{
  if(MYDEBUG) MESSAGE("removeHypothesis()");

  if (CORBA::is_nil( aSubShape ) && HasShapeToMesh())
    THROW_SALOME_CORBA_EXCEPTION("bad Sub-shape reference", SALOME::BAD_PARAM);

  if (CORBA::is_nil( anHyp ))
    THROW_SALOME_CORBA_EXCEPTION("bad hypothesis reference", SALOME::BAD_PARAM);

  if ( _preMeshInfo )
    _preMeshInfo->ForgetOrLoad();

  SMESH_Hypothesis::Hypothesis_Status status = SMESH_Hypothesis::HYP_OK;
  try
  {
    TopoDS_Shape myLocSubShape;
    //use PseudoShape in case if mesh has no shape
    if( _impl->HasShapeToMesh() )
      myLocSubShape = _gen_i->GeomObjectToShape( aSubShape );
    else
      myLocSubShape = _impl->GetShapeToMesh();

    const int hypId = anHyp->GetId();
    status = _impl->RemoveHypothesis(myLocSubShape, hypId);
    if ( !SMESH_Hypothesis::IsStatusFatal(status) )
    {
      // _mapHypo.erase( hypId ); EAP: hyp can be used on many sub-shapes
      anHyp->UnRegister();
    }
  }
  catch(SALOME_Exception & S_ex)
  {
    THROW_SALOME_CORBA_EXCEPTION(S_ex.what(), SALOME::BAD_PARAM);
  }
  return status;
}

//================================================================================
/*!
 * \brief Return hypotheses assigned to a given sub-shape
 */
//================================================================================

SMESH::ListOfHypothesis *
SMESH_Mesh_i::GetHypothesisList(GEOM::GEOM_Object_ptr aSubShape)
{
  Unexpect aCatch(SALOME_SalomeException);
  if (MYDEBUG) MESSAGE("GetHypothesisList");
  if (_impl->HasShapeToMesh() && CORBA::is_nil(aSubShape))
    THROW_SALOME_CORBA_EXCEPTION("bad Sub-shape reference", SALOME::BAD_PARAM);

  SMESH::ListOfHypothesis_var aList = new SMESH::ListOfHypothesis();

  try {
    TopoDS_Shape myLocSubShape = _gen_i->GeomObjectToShape(aSubShape);
    if ( myLocSubShape.IsNull() && !_impl->HasShapeToMesh() )
      myLocSubShape = _impl->GetShapeToMesh();
    const list<const SMESHDS_Hypothesis*>& aLocalList = _impl->GetHypothesisList( myLocSubShape );
    int i = 0, n = aLocalList.size();
    aList->length( n );

    list<const SMESHDS_Hypothesis*>::const_iterator aHyp = aLocalList.begin();
    std::map<int, SMESH::SMESH_Hypothesis_ptr>::iterator id_hypptr;
    for ( ; i < n && aHyp != aLocalList.end(); aHyp++ )
    {
      id_hypptr = _mapHypo.find( (*aHyp)->GetID() );
      if ( id_hypptr != _mapHypo.end() )
        aList[i++] = SMESH::SMESH_Hypothesis::_narrow( id_hypptr->second );
    }
    aList->length( i );
  }
  catch(SALOME_Exception & S_ex) {
    THROW_SALOME_CORBA_EXCEPTION(S_ex.what(), SALOME::BAD_PARAM);
  }

  return aList._retn();
}

//================================================================================
/*!
 * \brief Return sub-meshes
 */
//================================================================================

SMESH::submesh_array* SMESH_Mesh_i::GetSubMeshes()
{
  Unexpect aCatch(SALOME_SalomeException);
  if (MYDEBUG) MESSAGE("GetSubMeshes");

  SMESH::submesh_array_var aList = new SMESH::submesh_array();

  // Python Dump
  TPythonDump aPythonDump;
  if ( !_mapSubMeshIor.empty() )
    aPythonDump << "[ ";

  try {
    aList->length( _mapSubMeshIor.size() );
    int i = 0;
    map<int, SMESH::SMESH_subMesh_ptr>::iterator it = _mapSubMeshIor.begin();
    for ( ; it != _mapSubMeshIor.end(); it++ ) {
      if ( CORBA::is_nil( it->second )) continue;
      aList[i++] = SMESH::SMESH_subMesh::_duplicate( it->second );
      // Python Dump
      if (i > 1) aPythonDump << ", ";
      aPythonDump << it->second;
    }
    aList->length( i );
  }
  catch(SALOME_Exception & S_ex) {
    THROW_SALOME_CORBA_EXCEPTION(S_ex.what(), SALOME::BAD_PARAM);
  }

  // Update Python script
  if ( !_mapSubMeshIor.empty() )
    aPythonDump << " ] = " << SMESH::SMESH_Mesh_var( _this() ) << ".GetSubMeshes()";

  return aList._retn();
}

//================================================================================
/*!
 * \brief Create and return a sub-mesh on the given sub-shape
 */
//================================================================================

SMESH::SMESH_subMesh_ptr SMESH_Mesh_i::GetSubMesh(GEOM::GEOM_Object_ptr aSubShape,
                                                  const char*           theName )
{
  Unexpect aCatch(SALOME_SalomeException);
  if (CORBA::is_nil(aSubShape))
    THROW_SALOME_CORBA_EXCEPTION("bad Sub-shape reference", SALOME::BAD_PARAM);

  SMESH::SMESH_subMesh_var subMesh;
  SMESH::SMESH_Mesh_var    aMesh = _this();
  try {
    TopoDS_Shape myLocSubShape = _gen_i->GeomObjectToShape(aSubShape);

    //Get or Create the SMESH_subMesh object implementation

    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 )
    {
      if ( it.More() )
        THROW_SALOME_CORBA_EXCEPTION("Not a sub-shape of the main shape", SALOME::BAD_PARAM);
    }
    subMesh = getSubMesh( subMeshId );

    // create a new subMesh object servant if there is none for the shape
    if ( subMesh->_is_nil() )
      subMesh = createSubMesh( aSubShape );
    if ( _gen_i->CanPublishInStudy( subMesh ))
    {
      SALOMEDS::SObject_wrap aSO =
        _gen_i->PublishSubMesh( aMesh, subMesh, aSubShape, theName );
      if ( !aSO->_is_nil()) {
        // Update Python script
        TPythonDump() << aSO << " = " << aMesh << ".GetSubMesh( "
                      << aSubShape << ", '" << theName << "' )";
      }
    }
  }
  catch(SALOME_Exception & S_ex) {
    THROW_SALOME_CORBA_EXCEPTION(S_ex.what(), SALOME::BAD_PARAM);
  }
  return subMesh._retn();
}

//================================================================================
/*!
 * \brief Remove a sub-mesh
 */
//================================================================================

void SMESH_Mesh_i::RemoveSubMesh( SMESH::SMESH_subMesh_ptr theSubMesh )
{
  SMESH_TRY;

  if ( theSubMesh->_is_nil() )
    return;

  GEOM::GEOM_Object_var aSubShape;
  // Remove submesh's SObject
  SALOMEDS::SObject_wrap anSO = _gen_i->ObjectToSObject( theSubMesh );
  if ( !anSO->_is_nil() ) {
    long aTag = SMESH_Gen_i::GetRefOnShapeTag();
    SALOMEDS::SObject_wrap anObj, aRef;
    if ( anSO->FindSubObject( aTag, anObj.inout() ) &&
         anObj->ReferencedObject( aRef.inout() ))
    {
      CORBA::Object_var obj = aRef->GetObject();
      aSubShape = GEOM::GEOM_Object::_narrow( obj );
    }
    // if ( aSubShape->_is_nil() ) // not published shape (IPAL13617)
    //   aSubShape = theSubMesh->GetSubShape();

    SALOMEDS::StudyBuilder_var builder = _gen_i->getStudyServant()->NewBuilder();
    builder->RemoveObjectWithChildren( anSO );

    // Update Python script
    TPythonDump() << SMESH::SMESH_Mesh_var( _this() ) << ".RemoveSubMesh( " << anSO << " )";
  }

  if ( removeSubMesh( theSubMesh, aSubShape.in() ))
    if ( _preMeshInfo )
      _preMeshInfo->ForgetOrLoad();

  SMESH_CATCH( SMESH::throwCorbaException );
}

//================================================================================
/*!
 * \brief Create a standalone group
 */
//================================================================================

SMESH::SMESH_Group_ptr SMESH_Mesh_i::CreateGroup( SMESH::ElementType theElemType,
                                                  const char*        theName )
{
  Unexpect aCatch(SALOME_SalomeException);
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  SMESH::SMESH_Group_var aNewGroup =
    SMESH::SMESH_Group::_narrow( createGroup( theElemType, theName ));

  if ( _gen_i->CanPublishInStudy( aNewGroup ) )
  {
    SMESH::SMESH_Mesh_var mesh = _this();
    SALOMEDS::SObject_wrap aSO =
      _gen_i->PublishGroup( mesh, aNewGroup, GEOM::GEOM_Object::_nil(), theName);
    if ( !aSO->_is_nil())
      // Update Python script
      TPythonDump() << aSO << " = " << mesh << ".CreateGroup( "
                    << theElemType << ", '" << theName << "' )";
  }
  return aNewGroup._retn();
}

//================================================================================
/*!
 * \brief Create a group based on the given geometry
 */
//================================================================================

SMESH::SMESH_GroupOnGeom_ptr
SMESH_Mesh_i::CreateGroupFromGEOM (SMESH::ElementType    theElemType,
                                   const char*           theName,
                                   GEOM::GEOM_Object_ptr theGeomObj)
{
  Unexpect aCatch(SALOME_SalomeException);
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  SMESH::SMESH_GroupOnGeom_var aNewGroup;

  TopoDS_Shape aShape = _gen_i->GeomObjectToShape( theGeomObj );
  if ( !aShape.IsNull() )
  {
    aNewGroup =
      SMESH::SMESH_GroupOnGeom::_narrow( createGroup( theElemType, theName, /*id=*/-1, aShape ));

    if ( _gen_i->CanPublishInStudy( aNewGroup ) )
    {
      SMESH::SMESH_Mesh_var mesh = _this();
      SALOMEDS::SObject_wrap aSO =
        _gen_i->PublishGroup( mesh, aNewGroup, theGeomObj, theName );
      if ( !aSO->_is_nil())
        TPythonDump() << aSO << " = " << mesh << ".CreateGroupFromGEOM( "
                      << theElemType << ", '" << theName << "', " << theGeomObj << " )";
    }
  }

  return aNewGroup._retn();
}

//================================================================================
/*!
 * \brief Creates a group whose contents is defined by filter
 *  \param theElemType - group type
 *  \param theName - group name
 *  \param theFilter - the filter
 *  \retval SMESH::SMESH_GroupOnFilter_ptr - group defined by filter
 */
//================================================================================

SMESH::SMESH_GroupOnFilter_ptr
SMESH_Mesh_i::CreateGroupFromFilter(SMESH::ElementType theElemType,
                                    const char*        theName,
                                    SMESH::Filter_ptr  theFilter )
{
  Unexpect aCatch(SALOME_SalomeException);
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  if ( CORBA::is_nil( theFilter ))
    THROW_SALOME_CORBA_EXCEPTION("NULL filter", SALOME::BAD_PARAM);

  SMESH_PredicatePtr predicate = SMESH_GroupOnFilter_i::GetPredicate( theFilter );
  if ( !predicate )
    THROW_SALOME_CORBA_EXCEPTION("Invalid filter", SALOME::BAD_PARAM);

  SMESH::SMESH_GroupOnFilter_var aNewGroup = SMESH::SMESH_GroupOnFilter::_narrow
    ( createGroup( theElemType, theName, /*id=*/-1, TopoDS_Shape(), predicate ));

  TPythonDump pd;
  if ( !aNewGroup->_is_nil() )
    aNewGroup->SetFilter( theFilter );

  if ( _gen_i->CanPublishInStudy( aNewGroup ) )
  {
    SMESH::SMESH_Mesh_var mesh = _this();
    SALOMEDS::SObject_wrap aSO =
      _gen_i->PublishGroup( mesh, aNewGroup, GEOM::GEOM_Object::_nil(), theName );

    if ( !aSO->_is_nil())
      pd << aSO << " = " << mesh << ".CreateGroupFromFilter( "
         << theElemType << ", '" << theName << "', " << theFilter << " )";
  }
  return aNewGroup._retn();
}

//================================================================================
/*!
 * \brief Remove a group
 */
//================================================================================

void SMESH_Mesh_i::RemoveGroup( SMESH::SMESH_GroupBase_ptr theGroup )
{
  if ( theGroup->_is_nil() )
    return;

  SMESH_TRY;

  SMESH_GroupBase_i* aGroup = SMESH::DownCast<SMESH_GroupBase_i*>( theGroup );
  if ( !aGroup )
    return;

  if ( aGroup->GetMeshServant() != this )
    THROW_SALOME_CORBA_EXCEPTION( "RemoveGroup(): group does not belong to this mesh",
                                  SALOME::BAD_PARAM );

  SALOMEDS::SObject_wrap aGroupSO = _gen_i->ObjectToSObject( theGroup );
  if ( !aGroupSO->_is_nil() )
  {
    // Update Python script
    TPythonDump() << SMESH::SMESH_Mesh_var(_this()) << ".RemoveGroup( " << aGroupSO << " )";

    // Remove group's SObject
    SALOMEDS::StudyBuilder_var builder = SMESH_Gen_i::GetSMESHGen()->getStudyServant()->NewBuilder();
    builder->RemoveObjectWithChildren( aGroupSO );
  }
  aGroup->Modified(/*removed=*/true); // notify dependent Filter with FT_BelongToMeshGroup criterion

  // Remove the group from SMESH data structures
  removeGroup( aGroup->GetLocalID() );

  SMESH_CATCH( SMESH::throwCorbaException );
}

//=============================================================================
/*!
 *  Remove group with its contents
 */
//=============================================================================

void SMESH_Mesh_i::RemoveGroupWithContents( SMESH::SMESH_GroupBase_ptr theGroup )
{
  SMESH_TRY;
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  if ( theGroup->_is_nil() )
    return;

  SMESH_GroupBase_i* groupImpl = SMESH::DownCast< SMESH_GroupBase_i* >( theGroup );
  if ( !groupImpl || groupImpl->GetMeshServant() != this )
    THROW_SALOME_CORBA_EXCEPTION( "RemoveGroupWithContents(): group does not belong to this mesh",
                                  SALOME::BAD_PARAM);

  vector<smIdType> nodeIds; // to remove nodes becoming free
  bool isNodal = ( theGroup->GetType() == SMESH::NODE );
  if ( !isNodal && !theGroup->IsEmpty() )
  {
    SMESH::smIdType elemID = theGroup->GetID( 1 );
    int nbElemNodes = GetElemNbNodes( elemID );
    if ( nbElemNodes > 0 )
      nodeIds.reserve( theGroup->Size() * nbElemNodes );
  }

  // Retrieve contents
  SMESH::SMESH_IDSource_var idSrc = SMESH::SMESH_IDSource::_narrow( theGroup );
  SMDS_ElemIteratorPtr     elemIt = GetElements( idSrc, theGroup->GetType() );
  SMDS_StdIterator< const SMDS_MeshElement*, SMDS_ElemIteratorPtr > elemBeg( elemIt ), elemEnd;
  std::vector< const SMDS_MeshElement* > elems( theGroup->Size() );
  elems.assign( elemBeg, elemEnd );

  TPythonDump pyDump; // Suppress dump from RemoveGroup()

  // Remove group
  RemoveGroup( theGroup );

  // Remove contents
  for ( size_t i = 0; i < elems.size(); ++i )
  {
    // if ( !_impl->GetMeshDS()->Contains( elems[i] ))
    //   continue;
    if ( !isNodal )
    {
      for ( SMDS_ElemIteratorPtr nIt = elems[i]->nodesIterator(); nIt->more(); )
        nodeIds.push_back( nIt->next()->GetID() );

      _impl->GetMeshDS()->RemoveFreeElement( elems[i], /*sm=*/0 );
    }
    else
    {
      _impl->GetMeshDS()->RemoveElement( elems[i] );
    }
  }

  // Remove free nodes
  for ( size_t i = 0 ; i < nodeIds.size(); ++i )
    if ( const SMDS_MeshNode* n = _impl->GetMeshDS()->FindNode( nodeIds[i] ))
      if ( n->NbInverseElements() == 0 )
        _impl->GetMeshDS()->RemoveFreeNode( n, /*sm=*/0 );

  _impl->GetMeshDS()->Modified();
  _impl->SetIsModified( true );

  // Update Python script (theGroup must be alive for this)
  pyDump << SMESH::SMESH_Mesh_var(_this())
         << ".RemoveGroupWithContents( " << theGroup << " )";

  SMESH_CATCH( SMESH::throwCorbaException );
}

//================================================================================
/*!
 * \brief Get the list of groups existing in the mesh
 *  \retval SMESH::ListOfGroups * - list of groups
 */
//================================================================================

SMESH::ListOfGroups * SMESH_Mesh_i::GetGroups()
{
  Unexpect aCatch(SALOME_SalomeException);
  if (MYDEBUG) MESSAGE("GetGroups");

  SMESH::ListOfGroups_var aList = new SMESH::ListOfGroups();

  // Python Dump
  TPythonDump aPythonDump;
  if ( !_mapGroups.empty() )
  {
    aPythonDump << "[ ";
    try {
      aList->length( _mapGroups.size() );
      int i = 0;
      map<int, SMESH::SMESH_GroupBase_ptr>::iterator it = _mapGroups.begin();
      for ( ; it != _mapGroups.end(); it++ ) {
        if ( CORBA::is_nil( it->second )) continue;
        aList[i++] = SMESH::SMESH_GroupBase::_duplicate( it->second );
        // Python Dump
        if (i > 1) aPythonDump << ", ";
        aPythonDump << it->second;
      }
      aList->length( i );
    }
    catch(SALOME_Exception & S_ex) {
      THROW_SALOME_CORBA_EXCEPTION(S_ex.what(), SALOME::BAD_PARAM);
    }
    aPythonDump << " ] = " << SMESH::SMESH_Mesh_var(_this()) << ".GetGroups()";
  }
  return aList._retn();
}

//=============================================================================
/*!
 *  Get number of groups existing in the mesh
 */
//=============================================================================

CORBA::Long SMESH_Mesh_i::NbGroups()
{
  Unexpect aCatch(SALOME_SalomeException);
  return _mapGroups.size();
}

//=============================================================================
/*!
 * New group including all mesh elements present in initial groups is created.
 */
//=============================================================================

SMESH::SMESH_Group_ptr SMESH_Mesh_i::UnionGroups( SMESH::SMESH_GroupBase_ptr theGroup1,
                                                  SMESH::SMESH_GroupBase_ptr theGroup2,
                                                  const char*                theName )
{
  SMESH::SMESH_Group_var aResGrp;

  SMESH_TRY;
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  if ( theGroup1->_is_nil() || theGroup2->_is_nil() )
    THROW_SALOME_CORBA_EXCEPTION("UnionGroups(): NULL Group",
                                 SALOME::BAD_PARAM);
  if ( theGroup1->GetType() != theGroup2->GetType() )
    THROW_SALOME_CORBA_EXCEPTION("UnionGroups(): different group types",
                                 SALOME::BAD_PARAM);
  TPythonDump pyDump;

  // Create Union
  aResGrp = CreateGroup( theGroup1->GetType(), theName );
  if ( aResGrp->_is_nil() )
    return SMESH::SMESH_Group::_nil();

  aResGrp->AddFrom( theGroup1 );
  aResGrp->AddFrom( theGroup2 );

  // Update Python script
  pyDump << aResGrp << " = " << SMESH::SMESH_Mesh_var(_this())
         << ".UnionGroups( " << theGroup1 << ", " << theGroup2 << ", '" << theName << "' )";

  SMESH_CATCH( SMESH::throwCorbaException );

  return aResGrp._retn();
}

//=============================================================================
/*!
 * \brief New group including all mesh elements present in initial groups is created.
 *  \param theGroups list of groups
 *  \param theName name of group to be created
 *  \return pointer to the new group
 */
//=============================================================================

SMESH::SMESH_Group_ptr SMESH_Mesh_i::UnionListOfGroups(const SMESH::ListOfGroups& theGroups,
                                                       const char*                theName )
{
  SMESH::SMESH_Group_var aResGrp;

  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  if ( !theName )
    return SMESH::SMESH_Group::_nil();

  SMESH_TRY;

  // check types
  SMESH::ElementType aType = SMESH::ALL;
  for ( int g = 0, n = theGroups.length(); g < n; g++ )
  {
    SMESH::SMESH_GroupBase_var aGrp = theGroups[ g ];
    if ( CORBA::is_nil( aGrp ) )
      continue;
    if ( aType == SMESH::ALL )
      aType = aGrp->GetType();
    else if ( aType != aGrp->GetType() )
      THROW_SALOME_CORBA_EXCEPTION("UnionListOfGroups(): different group types",
                                   SALOME::BAD_PARAM);
  }
  if ( aType == SMESH::ALL )
    return SMESH::SMESH_Group::_nil();

  TPythonDump pyDump;

  // Create Union
  aResGrp = CreateGroup( aType, theName );
  if ( aResGrp->_is_nil() )
    return SMESH::SMESH_Group::_nil();

  pyDump << aResGrp << " = " << SMESH::SMESH_Mesh_var(_this()) << ".UnionListOfGroups([ ";
  for ( int g = 0, n = theGroups.length(); g < n; g++ )
  {
    SMESH::SMESH_GroupBase_var aGrp = theGroups[ g ];
    if ( !CORBA::is_nil( aGrp ) )
    {
      aResGrp->AddFrom( aGrp );
      if ( g > 0 ) pyDump << ", ";
      pyDump << aGrp;
    }
  }
  pyDump << " ], '" << theName << "' )";

  SMESH_CATCH( SMESH::throwCorbaException );

  return aResGrp._retn();
}

//=============================================================================
/*!
 *  New group is created. All mesh elements that are
 *  present in both initial groups are added to the new one.
 */
//=============================================================================

SMESH::SMESH_Group_ptr SMESH_Mesh_i::IntersectGroups( SMESH::SMESH_GroupBase_ptr theGroup1,
                                                      SMESH::SMESH_GroupBase_ptr theGroup2,
                                                      const char*                theName )
{
  SMESH::SMESH_Group_var aResGrp;

  SMESH_TRY;

  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  if ( theGroup1->_is_nil() || theGroup2->_is_nil() )
    THROW_SALOME_CORBA_EXCEPTION("IntersectGroups(): NULL Group",
                                 SALOME::BAD_PARAM);
  if ( theGroup1->GetType() != theGroup2->GetType() )
    THROW_SALOME_CORBA_EXCEPTION("IntersectGroups(): different group types",
                                 SALOME::BAD_PARAM);
  TPythonDump pyDump;

  // Create Intersection
  aResGrp = CreateGroup( theGroup1->GetType(), theName );
  if ( aResGrp->_is_nil() )
    return aResGrp._retn();

  SMESHDS_GroupBase* groupDS1 = 0;
  if ( SMESH_GroupBase_i* grp_i = SMESH::DownCast< SMESH_GroupBase_i* >( theGroup1 ))
    groupDS1 = grp_i->GetGroupDS();

  SMESHDS_GroupBase* groupDS2 = 0;
  if ( SMESH_GroupBase_i* grp_i = SMESH::DownCast< SMESH_GroupBase_i* >( theGroup2 ))
    groupDS2 = grp_i->GetGroupDS();

  SMESHDS_Group* resGroupDS = 0;
  if ( SMESH_GroupBase_i* grp_i = SMESH::DownCast< SMESH_GroupBase_i* >( aResGrp ))
    resGroupDS = dynamic_cast<SMESHDS_Group*>( grp_i->GetGroupDS() );

  if ( groupDS1 && groupDS2 && resGroupDS && !groupDS2->IsEmpty() )
  {
    SMDS_ElemIteratorPtr elemIt1 = groupDS1->GetElements();
    while ( elemIt1->more() )
    {
      const SMDS_MeshElement* e = elemIt1->next();
      if ( groupDS2->Contains( e ))
        resGroupDS->SMDSGroup().Add( e );
    }
  }
  // Update Python script
  pyDump << aResGrp << " = " << SMESH::SMESH_Mesh_var(_this()) << ".IntersectGroups( "
         << theGroup1 << ", " << theGroup2 << ", '" << theName << "')";

  SMESH_CATCH( SMESH::throwCorbaException );

  return aResGrp._retn();
}

//=============================================================================
/*!
  \brief Intersect list of groups. New group is created. All mesh elements that
  are present in all initial groups simultaneously are added to the new one.
  \param theGroups list of groups
  \param theName name of group to be created
  \return pointer on the group
*/
//=============================================================================

SMESH::SMESH_Group_ptr
SMESH_Mesh_i::IntersectListOfGroups(const SMESH::ListOfGroups& theGroups,
                                    const char*                theName )
{
  SMESH::SMESH_Group_var aResGrp;

  SMESH_TRY;

  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  if ( !theName )
    return SMESH::SMESH_Group::_nil();

  // check types and get SMESHDS_GroupBase's
  SMESH::ElementType aType = SMESH::ALL;
  vector< SMESHDS_GroupBase* > groupVec;
  for ( int g = 0, n = theGroups.length(); g < n; g++ )
  {
    SMESH::SMESH_GroupBase_var aGrp = theGroups[ g ];
    if ( CORBA::is_nil( aGrp ) )
      continue;
    if ( aType == SMESH::ALL )
      aType = aGrp->GetType();
    else if ( aType != aGrp->GetType() )
      THROW_SALOME_CORBA_EXCEPTION("IntersectListOfGroups(): different group types",
                                   SALOME::BAD_PARAM);

    if ( SMESH_GroupBase_i* grp_i = SMESH::DownCast< SMESH_GroupBase_i* >( aGrp ))
      if ( SMESHDS_GroupBase* grpDS = grp_i->GetGroupDS() )
      {
        if ( grpDS->IsEmpty() )
        {
          groupVec.clear();
          break;
        }
        groupVec.push_back( grpDS );
      }
  }
  if ( aType == SMESH::ALL ) // all groups are nil
    return SMESH::SMESH_Group::_nil();

  TPythonDump pyDump;

  // Create a group
  aResGrp = CreateGroup( aType, theName );

  SMESHDS_Group* resGroupDS = 0;
  if ( SMESH_GroupBase_i* grp_i = SMESH::DownCast< SMESH_GroupBase_i* >( aResGrp ))
    resGroupDS = dynamic_cast<SMESHDS_Group*>( grp_i->GetGroupDS() );
  if ( !resGroupDS || groupVec.empty() )
    return aResGrp._retn();

  // Fill the group
  size_t i, nb = groupVec.size();
  SMDS_ElemIteratorPtr elemIt1 = groupVec[0]->GetElements();
  while ( elemIt1->more() )
  {
    const SMDS_MeshElement* e = elemIt1->next();
    bool inAll = true;
    for ( i = 1; ( i < nb && inAll ); ++i )
      inAll = groupVec[i]->Contains( e );

    if ( inAll )
      resGroupDS->SMDSGroup().Add( e );
  }

  // Update Python script
  pyDump << aResGrp << " = " << SMESH::SMESH_Mesh_var( _this() )
         << ".IntersectListOfGroups( " << theGroups << ", '" << theName << "' )";

  SMESH_CATCH( SMESH::throwCorbaException );

  return aResGrp._retn();
}

//=============================================================================
/*!
 *  New group is created. All mesh elements that are present in
 *  a main group but is not present in a tool group are added to the new one
 */
//=============================================================================

SMESH::SMESH_Group_ptr SMESH_Mesh_i::CutGroups( SMESH::SMESH_GroupBase_ptr theGroup1,
                                                SMESH::SMESH_GroupBase_ptr theGroup2,
                                                const char*                theName )
{
  SMESH::SMESH_Group_var aResGrp;

  SMESH_TRY;

  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  if ( theGroup1->_is_nil() || theGroup2->_is_nil() )
    THROW_SALOME_CORBA_EXCEPTION("CutGroups(): NULL Group",
                                 SALOME::BAD_PARAM);
  if ( theGroup1->GetType() != theGroup2->GetType() )
    THROW_SALOME_CORBA_EXCEPTION("CutGroups(): different group types",
                                 SALOME::BAD_PARAM);
  TPythonDump pyDump;

  aResGrp = CreateGroup( theGroup1->GetType(), theName );
  if ( aResGrp->_is_nil() )
    return aResGrp._retn();

  SMESHDS_GroupBase* groupDS1 = 0;
  if ( SMESH_GroupBase_i* grp_i = SMESH::DownCast< SMESH_GroupBase_i* >( theGroup1 ))
    groupDS1 = grp_i->GetGroupDS();

  SMESHDS_GroupBase* groupDS2 = 0;
  if ( SMESH_GroupBase_i* grp_i = SMESH::DownCast< SMESH_GroupBase_i* >( theGroup2 ))
    groupDS2 = grp_i->GetGroupDS();

  SMESHDS_Group* resGroupDS = 0;
  if ( SMESH_GroupBase_i* grp_i = SMESH::DownCast< SMESH_GroupBase_i* >( aResGrp ))
    resGroupDS = dynamic_cast<SMESHDS_Group*>( grp_i->GetGroupDS() );

  if ( groupDS1 && groupDS2 && resGroupDS )
  {
    SMDS_ElemIteratorPtr elemIt1 = groupDS1->GetElements();
    while ( elemIt1->more() )
    {
      const SMDS_MeshElement* e = elemIt1->next();
      if ( !groupDS2->Contains( e ))
        resGroupDS->SMDSGroup().Add( e );
    }
  }
  // Update Python script
  pyDump << aResGrp << " = " << SMESH::SMESH_Mesh_var(_this()) << ".CutGroups( "
         << theGroup1 << ", " << theGroup2 << ", '" << theName << "')";

  SMESH_CATCH( SMESH::throwCorbaException );

  return aResGrp._retn();
}

//=============================================================================
/*!
  \brief Cut lists of groups. New group is created. All mesh elements that are
  present in main groups but do not present in tool groups are added to the new one
  \param theMainGroups list of main groups
  \param theToolGroups list of tool groups
  \param theName name of group to be created
  \return pointer on the group
*/
//=============================================================================

SMESH::SMESH_Group_ptr
SMESH_Mesh_i::CutListOfGroups(const SMESH::ListOfGroups& theMainGroups,
                              const SMESH::ListOfGroups& theToolGroups,
                              const char*                theName )
{
  SMESH::SMESH_Group_var aResGrp;

  SMESH_TRY;

  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  if ( !theName )
    return SMESH::SMESH_Group::_nil();

  // check types and get SMESHDS_GroupBase's
  SMESH::ElementType aType = SMESH::ALL;
  vector< SMESHDS_GroupBase* >   toolGroupVec;
  vector< SMDS_ElemIteratorPtr > mainIterVec;

  for ( int g = 0, n = theMainGroups.length(); g < n; g++ )
  {
    SMESH::SMESH_GroupBase_var aGrp = theMainGroups[ g ];
    if ( CORBA::is_nil( aGrp ) )
      continue;
    if ( aType == SMESH::ALL )
      aType = aGrp->GetType();
    else if ( aType != aGrp->GetType() )
      THROW_SALOME_CORBA_EXCEPTION("CutListOfGroups(): different group types",
                                   SALOME::BAD_PARAM);
    if ( SMESH_GroupBase_i* grp_i = SMESH::DownCast< SMESH_GroupBase_i* >( aGrp ))
      if ( SMESHDS_GroupBase* grpDS = grp_i->GetGroupDS() )
        if ( !grpDS->IsEmpty() )
          mainIterVec.push_back( grpDS->GetElements() );
  }
  if ( aType == SMESH::ALL ) // all main groups are nil
    return SMESH::SMESH_Group::_nil();
  if ( mainIterVec.empty() ) // all main groups are empty
    return aResGrp._retn();

  for ( int g = 0, n = theToolGroups.length(); g < n; g++ )
  {
    SMESH::SMESH_GroupBase_var aGrp = theToolGroups[ g ];
    if ( CORBA::is_nil( aGrp ) )
      continue;
    if ( aType != aGrp->GetType() )
      THROW_SALOME_CORBA_EXCEPTION("CutListOfGroups(): different group types",
                                   SALOME::BAD_PARAM);
    if ( SMESH_GroupBase_i* grp_i = SMESH::DownCast< SMESH_GroupBase_i* >( aGrp ))
      if ( SMESHDS_GroupBase* grpDS = grp_i->GetGroupDS() )
        toolGroupVec.push_back( grpDS );
  }

  TPythonDump pyDump;

  // Create a group
  aResGrp = CreateGroup( aType, theName );

  SMESHDS_Group* resGroupDS = 0;
  if ( SMESH_GroupBase_i* grp_i = SMESH::DownCast< SMESH_GroupBase_i* >( aResGrp ))
    resGroupDS = dynamic_cast<SMESHDS_Group*>( grp_i->GetGroupDS() );
  if ( !resGroupDS )
    return aResGrp._retn();

  // Fill the group
  size_t i, nb = toolGroupVec.size();
  SMDS_ElemIteratorPtr mainElemIt
    ( new SMDS_IteratorOnIterators
      < const SMDS_MeshElement*, vector< SMDS_ElemIteratorPtr > >( mainIterVec ));
  while ( mainElemIt->more() )
  {
    const SMDS_MeshElement* e = mainElemIt->next();
    bool isIn = false;
    for ( i = 0; ( i < nb && !isIn ); ++i )
      isIn = toolGroupVec[i]->Contains( e );

    if ( !isIn )
      resGroupDS->SMDSGroup().Add( e );
  }

  // Update Python script
  pyDump << aResGrp << " = " << SMESH::SMESH_Mesh_var( _this() )
         << ".CutListOfGroups( " << theMainGroups << ", "
         << theToolGroups << ", '" << theName << "' )";

  SMESH_CATCH( SMESH::throwCorbaException );

  return aResGrp._retn();
}

namespace // functions making checks according to SMESH::NB_COMMON_NODES_ENUM
{
  bool isAllNodesCommon(int nbChecked, int nbCommon, int nbNodes, int /*nbCorners*/,
                        bool & toStopChecking )
  {
    toStopChecking = ( nbCommon < nbChecked );
    return nbCommon == nbNodes;
  }
  bool isMainNodesCommon(int nbChecked, int nbCommon, int /*nbNodes*/, int nbCorners,
                         bool & toStopChecking )
  {
    toStopChecking = ( nbCommon < nbChecked || nbChecked >= nbCorners );
    return nbCommon == nbCorners;
  }
  bool isAtLeastOneNodeCommon(int /*nbChecked*/, int nbCommon, int /*nbNodes*/, int /*nbCorners*/,
                              bool & /*toStopChecking*/ )
  {
    return nbCommon > 0;
  }
  bool isMajorityOfNodesCommon(int /*nbChecked*/, int nbCommon, int nbNodes, int /*nbCorners*/,
                               bool & /*toStopChecking*/ )
  {
    return nbCommon >= (nbNodes+1) / 2;
  }
}

//=============================================================================
/*!
 * Create a group of entities basing on nodes of other groups.
 *  \param [in] theGroups - list of either groups, sub-meshes or filters.
 *  \param [in] anElemType - a type of elements to include to the new group.
 *  \param [in] theName - a name of the new group.
 *  \param [in] theNbCommonNodes - criterion of inclusion of an element to the new group.
 *  \param [in] theUnderlyingOnly - if \c True, an element is included to the
 *         new group provided that it is based on nodes of an element of \a aListOfGroups
 *  \return SMESH_Group - the created group
*/
// IMP 19939, bug 22010, IMP 22635
//=============================================================================

SMESH::SMESH_Group_ptr
SMESH_Mesh_i::CreateDimGroup(const SMESH::ListOfIDSources& theGroups,
                             SMESH::ElementType            theElemType,
                             const char*                   theName,
                             SMESH::NB_COMMON_NODES_ENUM   theNbCommonNodes,
                             CORBA::Boolean                theUnderlyingOnly)
{
  SMESH::SMESH_Group_var aResGrp;

  SMESH_TRY;
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  SMESHDS_Mesh* aMeshDS = _impl->GetMeshDS();

  if ( !theName || !aMeshDS )
    return SMESH::SMESH_Group::_nil();

  SMDSAbs_ElementType anElemType = (SMDSAbs_ElementType)theElemType;

  bool (*isToInclude)(int nbChecked, int nbCommon, int nbNodes, int nbCorners, bool & toStop);
  SMESH_Comment nbCoNoStr( "SMESH.");
  switch ( theNbCommonNodes ) {
  case SMESH::ALL_NODES   : isToInclude = isAllNodesCommon;        nbCoNoStr<<"ALL_NODES"   ;break;
  case SMESH::MAIN        : isToInclude = isMainNodesCommon;       nbCoNoStr<<"MAIN"        ;break;
  case SMESH::AT_LEAST_ONE: isToInclude = isAtLeastOneNodeCommon;  nbCoNoStr<<"AT_LEAST_ONE";break;
  case SMESH::MAJORITY    : isToInclude = isMajorityOfNodesCommon; nbCoNoStr<<"MAJORITY"    ;break;
  default: return aResGrp._retn();
  }
  int nbChecked, nbCommon, nbNodes, nbCorners;

  // Create a group

  TPythonDump pyDump;

  aResGrp = CreateGroup( theElemType, theName );
  if ( aResGrp->_is_nil() )
    return SMESH::SMESH_Group::_nil();

  SMESHDS_GroupBase* groupBaseDS =
    SMESH::DownCast<SMESH_GroupBase_i*>( aResGrp )->GetGroupDS();
  SMDS_MeshGroup& resGroupCore = static_cast< SMESHDS_Group* >( groupBaseDS )->SMDSGroup();

  vector<bool> isNodeInGroups;

  for ( int g = 0, n = theGroups.length(); g < n; g++ ) // loop on theGroups
  {
    SMESH::SMESH_IDSource_var aGrp = theGroups[ g ];
    if ( CORBA::is_nil( aGrp ) )
      continue;
    SMESH::SMESH_Mesh_var mesh = aGrp->GetMesh();
    if ( mesh->_is_nil() || mesh->GetId() != this->GetId() )
      continue;

    SMDS_ElemIteratorPtr elIt = GetElements( aGrp, SMESH::ALL );
    if ( !elIt ) continue;

    if ( theElemType == SMESH::NODE ) // get all nodes of elements
    {
      while ( elIt->more() ) {
        const SMDS_MeshElement* el = elIt->next();
        SMDS_ElemIteratorPtr nIt = el->nodesIterator();
        while ( nIt->more() )
          resGroupCore.Add( nIt->next() );
      }
    }
    // get elements of theElemType based on nodes of every element of group
    else if ( theUnderlyingOnly )
    {
      while ( elIt->more() )
      {
        const SMDS_MeshElement* el = elIt->next(); // an element of ref group
        TIDSortedElemSet elNodes( el->begin_nodes(), el->end_nodes() );
        TIDSortedElemSet checkedElems;
        SMDS_NodeIteratorPtr nIt = el->nodeIterator();
        while ( nIt->more() )
        {
          const SMDS_MeshNode* n = nIt->next();
          SMDS_ElemIteratorPtr elOfTypeIt = n->GetInverseElementIterator( anElemType );
          // check nodes of elements of theElemType around el
          while ( elOfTypeIt->more() )
          {
            const SMDS_MeshElement* elOfType = elOfTypeIt->next();
            if ( !checkedElems.insert( elOfType ).second ) continue;
            nbNodes   = elOfType->NbNodes();
            nbCorners = elOfType->NbCornerNodes();
            nbCommon  = 0;
            bool toStopChecking = false;
            SMDS_ElemIteratorPtr nIt2 = elOfType->nodesIterator();
            for ( nbChecked = 1; nIt2->more() && !toStopChecking; ++nbChecked )
              if ( elNodes.count( nIt2->next() ) &&
                   isToInclude( nbChecked, ++nbCommon, nbNodes, nbCorners, toStopChecking ))
              {
                resGroupCore.Add( elOfType );
                break;
              }
          }
        }
      }
    }
    // get all nodes of elements of groups
    else
    {
      while ( elIt->more() )
      {
        const SMDS_MeshElement* el = elIt->next(); // an element of group
        SMDS_NodeIteratorPtr nIt = el->nodeIterator();
        while ( nIt->more() )
        {
          const SMDS_MeshNode* n = nIt->next();
          if ( n->GetID() >= (int) isNodeInGroups.size() )
            isNodeInGroups.resize( n->GetID() + 1, false );
          isNodeInGroups[ n->GetID() ] = true;
        }
      }
    }
  }

  // Get elements of theElemType based on a certain number of nodes of elements of groups
  if ( !theUnderlyingOnly && !isNodeInGroups.empty() )
  {
    const SMDS_MeshNode* n;
    vector<bool> isElemChecked( aMeshDS->MaxElementID() + 1 );
    const int isNodeInGroupsSize = isNodeInGroups.size();
    for ( int iN = 0; iN < isNodeInGroupsSize; ++iN )
    {
      if ( !isNodeInGroups[ iN ] ||
           !( n = aMeshDS->FindNode( iN )))
        continue;

      // check nodes of elements of theElemType around n
      SMDS_ElemIteratorPtr elOfTypeIt = n->GetInverseElementIterator( anElemType );
      while ( elOfTypeIt->more() )
      {
        const SMDS_MeshElement*  elOfType = elOfTypeIt->next();
        vector<bool>::reference isChecked = isElemChecked[ elOfType->GetID() ];
        if ( isChecked )
          continue;
        isChecked = true;

        nbNodes   = elOfType->NbNodes();
        nbCorners = elOfType->NbCornerNodes();
        nbCommon  = 0;
        bool toStopChecking = false;
        SMDS_ElemIteratorPtr nIt = elOfType->nodesIterator();
        for ( nbChecked = 1; nIt->more() && !toStopChecking; ++nbChecked )
        {
          const smIdType nID = nIt->next()->GetID();
          if ( nID < isNodeInGroupsSize && isNodeInGroups[ nID ] &&
               isToInclude( nbChecked, ++nbCommon, nbNodes, nbCorners, toStopChecking ))
          {
            resGroupCore.Add( elOfType );
            break;
          }
        }
      }
    }
  }

  // Update Python script
  pyDump << aResGrp << " = " << SMESH::SMESH_Mesh_var( _this())
         << ".CreateDimGroup( "
         << theGroups << ", " << theElemType << ", '" << theName << "', "
         << nbCoNoStr << ", " << theUnderlyingOnly << ")";

  SMESH_CATCH( SMESH::throwCorbaException );

  return aResGrp._retn();
}

//================================================================================
/*!
 * \brief Distribute all faces of the mesh between groups using sharp edges and optionally
 *        existing 1D elements as group boundaries.
 *  \param [in] theSharpAngle - edge is considered sharp if an angle between normals of
 *              adjacent faces is more than \a sharpAngle in degrees.
 *  \param [in] theCreateEdges - to create 1D elements for detected sharp edges.
 *  \param [in] theUseExistingEdges - to use existing edges as group boundaries
 *  \return ListOfGroups - the created groups
 */
//================================================================================

SMESH::ListOfGroups*
SMESH_Mesh_i::FaceGroupsSeparatedByEdges( CORBA::Double  theSharpAngle,
                                          CORBA::Boolean theCreateEdges,
                                          CORBA::Boolean theUseExistingEdges )
{
  if ( theSharpAngle < 0 || theSharpAngle > 180 )
    THROW_SALOME_CORBA_EXCEPTION("Invalid sharp angle, it must be between 0 and 180 degrees",
                                 SALOME::BAD_PARAM);

  SMESH::ListOfGroups_var resultGroups = new SMESH::ListOfGroups;

  TPythonDump pyDump;

  SMESH_TRY;
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  SMESHDS_Mesh* meshDS = _impl->GetMeshDS();

  std::vector< SMESH_MeshAlgos::Edge > edges =
    SMESH_MeshAlgos::FindSharpEdges( meshDS, theSharpAngle, theUseExistingEdges );

  if ( theCreateEdges )
  {
    std::vector<const SMDS_MeshNode *> nodes(2);
    for ( size_t i = 0; i < edges.size(); ++i )
    {
      nodes[0] = edges[i]._node1;
      nodes[1] = edges[i]._node2;
      if ( meshDS->FindElement( nodes, SMDSAbs_Edge ))
        continue;
      if ( edges[i]._medium )
        meshDS->AddEdge( edges[i]._node1, edges[i]._node2, edges[i]._medium );
      else
        meshDS->AddEdge( edges[i]._node1, edges[i]._node2 );
    }
  }

  std::vector< std::vector< const SMDS_MeshElement* > > faceGroups =
    SMESH_MeshAlgos::SeparateFacesByEdges( meshDS, edges );

  SMESH::SMESH_MeshEditor_var editor = GetMeshEditor(); // create _editor

  resultGroups->length( faceGroups.size() );
  for ( size_t iG = 0; iG < faceGroups.size(); ++iG )
  {
    SMESH::SMESH_Group_var group = CreateGroup( SMESH::FACE,
                                                _editor->GenerateGroupName("Group").c_str());
    resultGroups[iG] = SMESH::SMESH_Group::_duplicate( group );

    SMESHDS_GroupBase* groupBaseDS =
      SMESH::DownCast<SMESH_GroupBase_i*>( group )->GetGroupDS();
    SMDS_MeshGroup& groupCore = static_cast< SMESHDS_Group* >( groupBaseDS )->SMDSGroup();

    std::vector< const SMDS_MeshElement* >& faces = faceGroups[ iG ];
    for ( size_t i = 0; i < faces.size(); ++i )
      groupCore.Add( faces[i] );
  }

  pyDump << resultGroups << " = " << SMESH::SMESH_Mesh_var(_this())
         << ".FaceGroupsSeparatedByEdges( "
         << TVar( theSharpAngle ) << ", "
         << theCreateEdges << ", "
         << theUseExistingEdges << " )";

  SMESH_CATCH( SMESH::throwCorbaException );
  return resultGroups._retn();

}

//================================================================================
/*!
 * \brief Remember GEOM group data
 */
//================================================================================

void SMESH_Mesh_i::addGeomGroupData(GEOM::GEOM_Object_ptr theGeomObj,
                                    CORBA::Object_ptr     theSmeshObj)
{
  if ( CORBA::is_nil( theGeomObj ) || theGeomObj->GetType() != GEOM_GROUP )
    return;
  // group SO
  SALOMEDS::SObject_wrap groupSO = _gen_i->ObjectToSObject( theGeomObj );
  if ( groupSO->_is_nil() )
    return;
  // group indices
  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() );
  TGeomGroupData & groupData = _geomGroupData.back();
  // entry
  CORBA::String_var entry = groupSO->GetID();
  groupData._groupEntry = entry.in();
  // indices
  for ( CORBA::ULong i = 0; i < ids->length(); ++i )
    groupData._indices.insert( ids[i] );
  // SMESH object
  groupData._smeshObject = CORBA::Object::_duplicate( theSmeshObj );
  // shape index in SMESHDS
  // TopoDS_Shape shape = _gen_i->GeomObjectToShape( theGeomObj );
  // groupData._dsID = shape.IsNull() ? 0 : _impl->GetSubMesh( shape )->GetId();
}

//================================================================================
/*!
 * Remove GEOM group data relating to removed smesh object
 */
//================================================================================

void SMESH_Mesh_i::removeGeomGroupData(CORBA::Object_ptr theSmeshObj)
{
  list<TGeomGroupData>::iterator
    data = _geomGroupData.begin(), dataEnd = _geomGroupData.end();
  for ( ; data != dataEnd; ++data ) {
    if ( theSmeshObj->_is_equivalent( data->_smeshObject )) {
      _geomGroupData.erase( data );
      return;
    }
  }
}

//================================================================================
/*!
* \brief Replace a shape in the mesh upon Break Link
*/
//================================================================================

void SMESH_Mesh_i::ReplaceShape(GEOM::GEOM_Object_ptr theNewGeom)
{
  // check if geometry changed
  bool geomChanged = true;
  GEOM::GEOM_Object_var oldGeom = GetShapeToMesh();
  if ( !theNewGeom->_is_nil() && !oldGeom->_is_nil() )
    geomChanged = ( 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 );

  // clear buffer also for sub-groups
  SMESHDS_Mesh* meshDS = _impl->GetMeshDS();
  const std::set<SMESHDS_GroupBase*>& groups = meshDS->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 );
    }

  // clear buffer also for sub-meshes
  std::map<int, SMESH_subMesh_i*>::const_iterator aSubMeshIter = _mapSubMesh_i.cbegin();
  for(; aSubMeshIter != _mapSubMesh_i.cend(); aSubMeshIter++) {
    int aShapeID = aSubMeshIter->first;
    const TopoDS_Shape& aSubShape = meshDS->IndexToShape(aShapeID);
    TCollection_AsciiString aShapeIOR;
    if ( geomClient->Find( aSubShape, aShapeIOR ))
      geomClient->RemoveShapeFromBuffer( aShapeIOR );
  }

  typedef struct {
    int shapeID; smIdType 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
  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 smIdType     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 );
        }
      }
    }
  }


  // update the reference to theNewGeom (needed for correct execution of a dumped python script)
  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 );
    }
  }

  // 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("
                << me <<".GetMesh()), " << entry.in() << ")";

  TPythonDump() <<  me << ".ReplaceShape( " << entry.in() << " )";

}

//================================================================================
/*!
 * \brief Return new group contents if it has been changed and update group data
 */
//================================================================================

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;
    if ( !meshSO->_is_nil() &&
         meshSO->FindSubObject( SMESH::Tag_RefOnShape, geomRefSO.inout() ))
    {
      geomRefSO->ReferencedObject( groupSO.inout() );
    }
  }
  else
  {
    // get geom group
    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 )
    {
      GEOM_Client*    geomClient = _gen_i->GetShapeReader();
      CORBA::String_var groupIOR = geomGen->GetStringFromIOR( geomGroup );
      geomClient->RemoveShapeFromBuffer( groupIOR.in() );
      newShape = _gen_i->GeomObjectToShape( geomGroup );
    }
  }
  else
  {
    // geom group becomes empty - return empty compound
    TopoDS_Compound compound;
    BRep_Builder().MakeCompound(compound);
    newShape = compound;
  }

  return newShape;
}

namespace
{
  //-----------------------------------------------------------------------------
  /*!
   * \brief Storage of shape and index used in CheckGeomGroupModif()
   */
  struct TIndexedShape
  {
    int          _index;
    TopoDS_Shape _shape;
    TIndexedShape( int i, const TopoDS_Shape& s ):_index(i), _shape(s) {}
  };
  //-----------------------------------------------------------------------------
  /*!
   * \brief Data to re-create a group on geometry
   */
  struct TGroupOnGeomData
  {
    int                 _oldID;
    TopoDS_Shape        _shape;
    SMDSAbs_ElementType _type;
    std::string         _name;
    Quantity_Color      _color;

    TGroupOnGeomData( const SMESHDS_GroupOnGeom* group )
    {
      _oldID = group->GetID();
      _type  = group->GetType();
      _name  = group->GetStoreName();
      _color = group->GetColor();
    }
  };

  //-----------------------------------------------------------------------------
  /*!
   * \brief Check if a filter is still valid after geometry removal
   */
  bool isValidGeomFilter( SMESH::Filter_var theFilter )
  {
    if ( theFilter->_is_nil() )
      return false;
    SMESH::Filter::Criteria_var criteria;
    theFilter->GetCriteria( criteria.out() );

    for ( CORBA::ULong iCr = 0; iCr < criteria->length(); ++iCr )
    {
      const char* thresholdID = criteria[ iCr ].ThresholdID.in();
      std::string entry;
      switch ( criteria[ iCr ].Type )
      {
      case SMESH::FT_BelongToGeom:
      case SMESH::FT_BelongToPlane:
      case SMESH::FT_BelongToCylinder:
      case SMESH::FT_BelongToGenSurface:
      case SMESH::FT_LyingOnGeom:
        entry = thresholdID;
        break;
      case SMESH::FT_ConnectedElements:
        if ( thresholdID )
        {
          entry = thresholdID;
          break;
        }
      default:
        continue;
      }
      SMESH_Gen_i*           gen = SMESH_Gen_i::GetSMESHGen();
      SALOMEDS::SObject_wrap  so = gen->getStudyServant()->FindObjectID( entry.c_str() );
      if ( so->_is_nil() )
        return false;
      CORBA::Object_var      obj = so->GetObject();
      GEOM::GEOM_Object_var geom = GEOM::GEOM_Object::_narrow( obj );
      if ( gen->GeomObjectToShape( geom ).IsNull() )
        return false;

    } // loop on criteria

    return true;
  }
}

//=============================================================================
/*!
 * \brief Update data if geometry changes
 *
 * Issue 0022501
 */
//=============================================================================

void SMESH_Mesh_i::CheckGeomModif( bool theIsBreakLink )
{
  SMESH::SMESH_Mesh_var me = _this();
  GEOM::GEOM_Object_var mainGO = GetShapeToMesh();

  TPythonDump dumpNothing; // prevent any dump

  //bool removedFromClient = false;

  if ( mainGO->_is_nil() ) // GEOM_Client cleared or geometry removed? (IPAL52735, PAL23636)
  {
    //removedFromClient = _impl->HasShapeToMesh();

    // try to find geometry by study reference
    SALOMEDS::SObject_wrap meshSO = _gen_i->ObjectToSObject( me );
    SALOMEDS::SObject_wrap geomRefSO, geomSO;
    if ( !meshSO->_is_nil() &&
         meshSO->FindSubObject( SMESH::Tag_RefOnShape, geomRefSO.inout() ) &&
         geomRefSO->ReferencedObject( geomSO.inout() ))
    {
      CORBA::Object_var geomObj = _gen_i->SObjectToObject( geomSO );
      mainGO = GEOM::GEOM_Object::_narrow( geomObj );
    }

    if ( mainGO->_is_nil() &&    // geometry removed ==>
         !geomRefSO->_is_nil() ) // remove geom dependent data: sub-meshes etc.
    {
      // convert geom dependent groups into standalone ones
      CheckGeomGroupModif();

      _impl->ShapeToMesh( TopoDS_Shape() );

      // remove sub-meshes
      std::map<int, SMESH::SMESH_subMesh_ptr>::iterator i_sm = _mapSubMeshIor.begin();
      while ( i_sm != _mapSubMeshIor.end() )
      {
        SMESH::SMESH_subMesh_ptr sm = i_sm->second;
        ++i_sm;
        RemoveSubMesh( sm );
      }
      // remove all children except groups in the study
      SALOMEDS::StudyBuilder_var builder = _gen_i->getStudyServant()->NewBuilder();
      SALOMEDS::SObject_wrap so;
      for ( CORBA::Long tag = SMESH::Tag_RefOnShape; tag <= SMESH::Tag_LastSubMesh; ++tag )
        if ( meshSO->FindSubObject( tag, so.inout() ))
          builder->RemoveObjectWithChildren( so );

      _gen_i->SetPixMap( meshSO, "ICON_SMESH_TREE_MESH_IMPORTED" );

      return;
    }
  }

  if ( !_impl->HasShapeToMesh() ) return;


  // Update after group modification

  const bool geomChanged = ( mainGO->GetTick() != _mainShapeTick );
  if ( !theIsBreakLink )
    if ( mainGO->GetType() == GEOM_GROUP || !geomChanged )  // is group or not modified
    {
      smIdType nb = NbNodes() + NbElements();
      CheckGeomGroupModif();
      bool updated = ( nb != NbNodes() + NbElements() );
      if ( updated ) // something removed due to hypotheses change
        _gen_i->UpdateIcons( me );
      if ( updated == geomChanged || nb == 0 )
        return;
    }

  // 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 );

  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)

  if ( _preMeshInfo )
    _preMeshInfo->ForgetAllData();

  if ( geomChanged || !isShaper )
    _impl->Clear();
  if ( newShape.IsNull() )
    return;

  _mainShapeTick = mainGO->GetTick();

  // 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() );
  TopTools_DataMapOfShapeShape old2newShapeMap;
  std::set<SMESHDS_GroupBase*>::const_iterator g = groups.begin();
  for ( ; g != groups.end(); ++g )
  {
    if ( const SMESHDS_GroupOnGeom* group = dynamic_cast< SMESHDS_GroupOnGeom* >( *g ))
    {
      groupsData.push_back( TGroupOnGeomData( group ));

      // get a new shape
      SMESH::SMESH_GroupOnGeom_var gog;
      std::map<int, SMESH::SMESH_GroupBase_ptr>::iterator i_grp = _mapGroups.find( group->GetID() );
      if ( i_grp != _mapGroups.end() )
        gog = SMESH::SMESH_GroupOnGeom::_narrow( i_grp->second );

      GEOM::GEOM_Object_var geom;
      if ( !gog->_is_nil() )
      {
        if ( !theIsBreakLink )
          geom = gog->GetShape();

        if ( theIsBreakLink || geom->_is_nil() )
        {
          SALOMEDS::SObject_wrap grpSO = _gen_i->ObjectToSObject( gog );
          SALOMEDS::SObject_wrap geomRefSO, geomSO;
          if ( !grpSO->_is_nil() &&
               grpSO->FindSubObject( SMESH::Tag_RefOnShape, geomRefSO.inout() ) &&
               geomRefSO->ReferencedObject( geomSO.inout() ))
          {
            CORBA::Object_var geomObj = _gen_i->SObjectToObject( geomSO );
            geom = GEOM::GEOM_Object::_narrow( geomObj );
          }
        }
      }
      if ( old2newShapeMap.IsBound( group->GetShape() ))
      {
        groupsData.back()._shape = old2newShapeMap( group->GetShape() );
      }
      else if ( !geom->_is_nil() )
      {
        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
  std::vector< pair< int, THypList > > ids2Hyps;
  const ShapeToHypothesis & hyps = meshDS->GetHypotheses();
  for ( ShapeToHypothesis::Iterator s2hyps( hyps ); s2hyps.More(); s2hyps.Next() )
  {
    const TopoDS_Shape& s = s2hyps.Key();
    const THypList&  hyps = s2hyps.ChangeValue();
    ids2Hyps.push_back( make_pair( meshDS->ShapeToIndex( s ), hyps ));
  }

  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();
  for ( ; oldNbSubShapes > 0; --oldNbSubShapes )
  {
    const TopoDS_Shape& s = meshDS->IndexToShape( oldNbSubShapes );
    if ( s.IsNull() || s.ShapeType() != TopAbs_COMPOUND )
      break;
    // fill ii2iMap
    std::set<int> subIds;
    for ( TopoDS_Iterator it( s ); it.More(); it.Next() )
      subIds.insert( meshDS->ShapeToIndex( it.Value() ));
    ii2iMap.insert( std::make_pair( subIds, oldNbSubShapes ));
  }

  // check if shape topology changes - save shape type per shape ID
  std::vector< TopAbs_ShapeEnum > shapeTypes( Max( oldNbSubShapes + 1, 1 ));
  for ( int shapeID = oldNbSubShapes; shapeID > 0; --shapeID )
    shapeTypes[ shapeID ] = meshDS->IndexToShape( shapeID ).ShapeType();

  // change shape to mesh
  _impl->ShapeToMesh( TopoDS_Shape() );
  _impl->ShapeToMesh( newShape );

  // check if shape topology changes - check new shape types
  bool sameTopology = ( oldNbSubShapes == meshDS->MaxShapeIndex() );
  for ( int shapeID = oldNbSubShapes; shapeID > 0 &&  sameTopology; --shapeID )
  {
    const TopoDS_Shape& s = meshDS->IndexToShape( shapeID );
    sameTopology = ( !s.IsNull() && s.ShapeType() == shapeTypes[ shapeID ]);
  }

  // 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 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::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 );

    if ( !newShape.IsNull() )
    {
      if ( oldNbSub > 1 && meshDS->ShapeToIndex( newShape ) > 0 ) // group reduced to one sub-shape
      {
        TopoDS_Compound compound;
        BRep_Builder().MakeCompound( compound );
        BRep_Builder().Add( compound, newShape );
        newShape = compound;
      }
      int newID = _impl->GetSubMesh( newShape )->GetId();
      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 )
  {
    int sID = ids2Hyps[i].first;
    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 );
    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() );
  }

  {
    // restore groups on geometry
    for ( size_t i = 0; i < groupsData.size(); ++i )
    {
      const TGroupOnGeomData& data = groupsData[i];
      if ( data._shape.IsNull() )
        continue;

      std::map<int, SMESH::SMESH_GroupBase_ptr>::iterator i2g = _mapGroups.find( data._oldID );
      if ( i2g == _mapGroups.end() ) continue;

      SMESH_GroupBase_i* gr_i = SMESH::DownCast<SMESH_GroupBase_i*>( i2g->second );
      if ( !gr_i ) continue;

      SMESH_Group* g = _impl->AddGroup( data._type, data._name.c_str(), data._oldID, data._shape );
      if ( !g )
        _mapGroups.erase( i2g );
      else
        g->GetGroupDS()->SetColor( data._color );
    }

    if ( !sameTopology )
    {
      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
    std::map<int, ::SMESH_subMesh*>::iterator i_sm = _mapSubMesh.begin();
    for ( ; i_sm != _mapSubMesh.end(); ++i_sm )
      i_sm->second = _impl->GetSubMesh( meshDS->IndexToShape( i_sm->first ));
  }

  if ( !sameTopology )
  {
    // remove invalid study sub-objects
    CheckGeomGroupModif();
  }

  _gen_i->UpdateIcons( me );

  if ( !theIsBreakLink && isShaper )
  {
    SALOMEDS::SObject_wrap meshSO = _gen_i->ObjectToSObject( me );
    if ( !meshSO->_is_nil() )
      _gen_i->SetPixMap(meshSO, "ICON_SMESH_TREE_GEOM_MODIF");
  }
}

//=============================================================================
/*!
 * \brief Update objects depending on changed geom groups
 *
 * NPAL16168: geometrical group edition from a submesh don't modify mesh computation
 * issue 0020210: Update of a smesh group after modification of the associated geom group
 */
//=============================================================================

void SMESH_Mesh_i::CheckGeomGroupModif()
{
  // remove sub-meshes referring a removed sub-shapes (if main shape still exists)
  SALOMEDS::StudyBuilder_var builder = _gen_i->getStudyServant()->NewBuilder();
  GEOM::GEOM_Object_var  mainGO = GetShapeToMesh();
  SALOMEDS::SObject_wrap meshSO = _gen_i->ObjectToSObject( SMESH::SMESH_Mesh_var( _this() ));
  if ( !mainGO->_is_nil() && !meshSO->_is_nil() )
  {
    SALOMEDS::SObject_wrap rootSO, geomRefSO, geomSO;
    for ( CORBA::Long tag = SMESH::Tag_FirstSubMesh; tag <= SMESH::Tag_LastSubMesh; ++tag )
      if ( meshSO->FindSubObject( tag, rootSO.inout() ))
      {
        int nbValid = 0, nbRemoved = 0;
        SALOMEDS::ChildIterator_wrap chItr = _gen_i->getStudyServant()->NewChildIterator( rootSO );
        for ( ; chItr->More(); chItr->Next() )
        {
          SALOMEDS::SObject_wrap smSO = chItr->Value(); // sub-mesh SO
          if ( !smSO->_is_nil() &&
               smSO->FindSubObject( SMESH::Tag_RefOnShape, geomRefSO.inout() ) &&
               geomRefSO->ReferencedObject( geomSO.inout() )) // find geometry by reference
          {
            CORBA::Object_var  geomObj = _gen_i->SObjectToObject( geomSO );
            GEOM::GEOM_Object_var geom = GEOM::GEOM_Object::_narrow( geomObj );
            if ( !geom->_non_existent() )
            {
              ++nbValid;
              continue; // keep the sub-mesh
            }
          }
          CORBA::Object_var     smObj = _gen_i->SObjectToObject( smSO );
          SMESH::SMESH_subMesh_var sm = SMESH::SMESH_subMesh::_narrow( smObj );
          if ( !sm->_is_nil() && !sm->_non_existent() )
          {
            GEOM::GEOM_Object_var smGeom = sm->GetSubShape();
            if ( smGeom->_is_nil() )
            {
              RemoveSubMesh( sm );
              ++nbRemoved;
            }
          }
          else
          {
            if ( _preMeshInfo )
              _preMeshInfo->ForgetAllData(); // unknown hypothesis modified
            builder->RemoveObjectWithChildren( smSO ); // sub-shape removed before loading SMESH
            ++nbRemoved;
          }
        }
        if ( /*nbRemoved > 0 &&*/ nbValid == 0 )
          builder->RemoveObjectWithChildren( rootSO );
      }
  }

  // check for removed sub-shapes and convert geom dependent groups into standalone ones
  std::map<int, SMESH::SMESH_GroupBase_ptr>::iterator i_gr = _mapGroups.begin();
  while ( i_gr != _mapGroups.end())
  {
    SMESH::SMESH_GroupBase_ptr group = i_gr->second;
    ++i_gr;
    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() ); // 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() )
    {
      isValidGeom = isValidGeomFilter( onFilt->GetFilter() );
    }
    else // standalone
    {
      isValidGeom = ( !groupSO->_is_nil() &&
                      !groupSO->FindSubObject( SMESH::Tag_RefOnShape, refSO.inout() ));
    }
    if ( !isValidGeom )
    {
      if ( !IsLoaded() || group->IsEmpty() )
      {
        RemoveGroup( group );
      }
      else if ( !onGeom->_is_nil() || !onFilt->_is_nil() )
      {
        SMESH::SMESH_Group_var ( ConvertToStandalone( group ));
      }
      else // is it possible?
      {
        builder->RemoveObjectWithChildren( refSO );
      }
    }
  }


  if ( !_impl->HasShapeToMesh() ) return;

  SMESH::smIdType nbEntities = NbNodes() + NbElements();

  // Check if group contents changed

  typedef map< string, TopoDS_Shape > TEntry2Geom;
  TEntry2Geom newGroupContents;

  list<TGeomGroupData>::iterator
    data = _geomGroupData.begin(), dataEnd = _geomGroupData.end();
  for ( ; data != dataEnd; ++data )
  {
    pair< TEntry2Geom::iterator, bool > it_new =
      newGroupContents.insert( make_pair( data->_groupEntry, TopoDS_Shape() ));
    bool processedGroup    = !it_new.second;
    TopoDS_Shape& newShape = it_new.first->second;
    if ( !processedGroup )
      newShape = newGroupShape( *data, ONLY_IF_CHANGED );
    if ( newShape.IsNull() )
      continue; // no changes

    if ( _preMeshInfo )
      _preMeshInfo->ForgetOrLoad();

    if ( processedGroup ) { // update group indices
      list<TGeomGroupData>::iterator data2 = data;
      for ( --data2; data2->_groupEntry != data->_groupEntry; --data2) {}
      data->_indices = data2->_indices;
    }

    // Update SMESH objects according to new GEOM group contents

    SMESH::SMESH_subMesh_var submesh = SMESH::SMESH_subMesh::_narrow( data->_smeshObject );
    if ( !submesh->_is_nil() ) // -------------- Sub mesh ---------------------
    {
      int oldID = submesh->GetId();
      if ( !_mapSubMeshIor.count( oldID ))
        continue;
      TopoDS_Shape oldShape = _mapSubMesh[oldID]->GetSubShape();

      // update hypotheses
      list <const SMESHDS_Hypothesis * > hyps = _impl->GetHypothesisList(oldShape);
      list <const SMESHDS_Hypothesis * >::iterator hypIt;
      for ( hypIt = hyps.begin(); hypIt != hyps.end(); ++hypIt )
      {
        _impl->RemoveHypothesis( oldShape, (*hypIt)->GetID());
        _impl->AddHypothesis   ( newShape, (*hypIt)->GetID());
      }
      // care of submeshes
      SMESH_subMesh* newSubmesh = _impl->GetSubMesh( newShape );
      int newID = newSubmesh->GetId();
      if ( newID != oldID ) {
        _mapSubMesh   [ newID ] = newSubmesh;
        _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 );
      }
      continue;
    }

    SMESH::SMESH_GroupOnGeom_var smeshGroup =
      SMESH::SMESH_GroupOnGeom::_narrow( data->_smeshObject );
    if ( !smeshGroup->_is_nil() ) // ------------ GROUP -----------------------
    {
      SMESH_GroupOnGeom_i* group_i = SMESH::DownCast<SMESH_GroupOnGeom_i*>( smeshGroup );
      if ( group_i ) {
        ::SMESH_Group* group = _impl->GetGroup( group_i->GetLocalID() );
        SMESHDS_GroupOnGeom* ds = static_cast<SMESHDS_GroupOnGeom*>( group->GetGroupDS() );
        ds->SetShape( newShape );
      }
      continue;
    }

    SMESH::SMESH_Mesh_var mesh = SMESH::SMESH_Mesh::_narrow( data->_smeshObject );
    if ( !mesh->_is_nil() ) // -------------- MESH ----------------------------
    {
      // Remove groups and submeshes basing on removed sub-shapes

      TopTools_MapOfShape newShapeMap;
      TopoDS_Iterator shapeIt( newShape );
      for ( ; shapeIt.More(); shapeIt.Next() )
        newShapeMap.Add( shapeIt.Value() );

      SMESHDS_Mesh* meshDS = _impl->GetMeshDS();
      for ( shapeIt.Initialize( meshDS->ShapeToMesh() ); shapeIt.More(); shapeIt.Next() )
      {
        if ( newShapeMap.Contains( shapeIt.Value() ))
          continue;
        TopTools_IndexedMapOfShape oldShapeMap;
        TopExp::MapShapes( shapeIt.Value(), oldShapeMap );
        for ( int i = 1; i <= oldShapeMap.Extent(); ++i )
        {
          const TopoDS_Shape& oldShape = oldShapeMap(i);
          int oldInd = meshDS->ShapeToIndex( oldShape );
          // -- submeshes --
          map<int, SMESH::SMESH_subMesh_ptr>::iterator i_smIor = _mapSubMeshIor.find( oldInd );
          if ( i_smIor != _mapSubMeshIor.end() ) {
            RemoveSubMesh( i_smIor->second ); // one submesh per shape index
          }
          // --- groups ---
          map<int, SMESH::SMESH_GroupBase_ptr>::iterator i_grp = _mapGroups.begin();
          for ( ; i_grp != _mapGroups.end(); ++i_grp )
          {
            // check if a group bases on oldInd shape
            SMESHDS_GroupOnGeom* grpOnGeom = 0;
            if ( ::SMESH_Group* g = _impl->GetGroup( i_grp->first ))
              grpOnGeom = dynamic_cast<SMESHDS_GroupOnGeom*>( g->GetGroupDS() );
            if ( grpOnGeom && oldShape.IsSame( grpOnGeom->GetShape() ))
            { // remove
              RemoveGroup( i_grp->second ); // several groups can base on same shape
              i_grp = _mapGroups.begin(); // _mapGroups changed - restart iteration
            }
          }
        }
      }
      // Reassign hypotheses and update groups after setting the new shape to mesh

      // collect anassigned hypotheses
      typedef list< pair< TIndexedShape, list<const SMESHDS_Hypothesis*> > > TShapeHypList;
      list <const SMESHDS_Hypothesis * >::const_iterator hypIt;
      TShapeHypList assignedHyps;
      for ( int i = 1; i <= meshDS->MaxShapeIndex(); ++i )
      {
        const TopoDS_Shape& oldShape = meshDS->IndexToShape(i);
        list<const SMESHDS_Hypothesis*> hyps = meshDS->GetHypothesis( oldShape );// copy
        if ( !hyps.empty() ) {
          assignedHyps.push_back( make_pair( TIndexedShape(i,oldShape), hyps ));
          for ( hypIt = hyps.begin(); hypIt != hyps.end(); ++hypIt )
            _impl->RemoveHypothesis( oldShape, (*hypIt)->GetID());
        }
      }
      // collect shapes supporting groups
      typedef list < pair< TIndexedShape, SMDSAbs_ElementType > > TShapeTypeList;
      TShapeTypeList groupData;
      const set<SMESHDS_GroupBase*>& groups = meshDS->GetGroups();
      set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
      for ( ; grIt != groups.end(); ++grIt )
      {
        if ( SMESHDS_GroupOnGeom* gog = dynamic_cast<SMESHDS_GroupOnGeom*>( *grIt ))
          groupData.push_back
            ( make_pair( TIndexedShape( gog->GetID(),gog->GetShape()), gog->GetType()));
      }
      // set new shape to mesh -> DS of sub-meshes and geom groups is deleted
      _impl->Clear();
      _impl->ShapeToMesh( TopoDS_Shape() ); // IPAL52730
      _impl->ShapeToMesh( newShape );

      // reassign hypotheses
      TShapeHypList::iterator indS_hyps = assignedHyps.begin();
      for ( ; indS_hyps != assignedHyps.end(); ++indS_hyps )
      {
        TIndexedShape&                   geom = indS_hyps->first;
        list<const SMESHDS_Hypothesis*>& hyps = indS_hyps->second;
        int oldID = geom._index;
        int newID = meshDS->ShapeToIndex( geom._shape );
        if ( oldID == 1 ) { // main shape
          newID = 1;
          geom._shape = newShape;
        }
        if ( !newID )
          continue;
        for ( hypIt = hyps.begin(); hypIt != hyps.end(); ++hypIt )
          _impl->AddHypothesis( geom._shape, (*hypIt)->GetID());
        // care of sub-meshes
        SMESH_subMesh* newSubmesh = _impl->GetSubMesh( geom._shape );
        if ( newID != oldID ) {
          _mapSubMesh   [ newID ] = newSubmesh;
          _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 );
        }
      }
      // recreate groups
      TShapeTypeList::iterator geomType = groupData.begin();
      for ( ; geomType != groupData.end(); ++geomType )
      {
        const TIndexedShape& geom = geomType->first;
        int oldID = geom._index;
        if ( _mapGroups.find( oldID ) == _mapGroups.end() )
          continue;
        // get group name
        SALOMEDS::SObject_wrap groupSO = _gen_i->ObjectToSObject( _mapGroups[oldID] );
        CORBA::String_var      name    = groupSO->GetName();
        // update
        if ( SMESH_GroupBase_i* group_i = SMESH::DownCast<SMESH_GroupBase_i*>(_mapGroups[oldID]))
          if ( SMESH_Group* group = _impl->AddGroup( geomType->second, name.in(),
                                                     /*id=*/-1, geom._shape ))
            group_i->changeLocalId( group->GetID() );
      }

      break; // everything has been updated

    } // update mesh
  } // loop on group data

  // Update icons

  SMESH::smIdType newNbEntities = NbNodes() + NbElements();
  list< SALOMEDS::SObject_wrap > soToUpdateIcons;
  if ( newNbEntities != nbEntities )
  {
    // Add all SObjects with icons to soToUpdateIcons
    soToUpdateIcons.push_back( _gen_i->ObjectToSObject( _this() )); // mesh

    for (map<int, SMESH::SMESH_subMesh_ptr>::iterator i_sm = _mapSubMeshIor.begin();
         i_sm != _mapSubMeshIor.end(); ++i_sm ) // submeshes
      soToUpdateIcons.push_back( _gen_i->ObjectToSObject( i_sm->second ));

    for ( map<int, SMESH::SMESH_GroupBase_ptr>::iterator i_gr = _mapGroups.begin();
          i_gr != _mapGroups.end(); ++i_gr ) // groups
      soToUpdateIcons.push_back( _gen_i->ObjectToSObject( i_gr->second ));
  }

  list< SALOMEDS::SObject_wrap >::iterator so = soToUpdateIcons.begin();
  for ( ; so != soToUpdateIcons.end(); ++so )
    _gen_i->SetPixMap( *so, "ICON_SMESH_TREE_MESH_WARN" );
}

//=============================================================================
/*!
 * \brief Create standalone group from a group on geometry or filter
 */
//=============================================================================

SMESH::SMESH_Group_ptr SMESH_Mesh_i::ConvertToStandalone( SMESH::SMESH_GroupBase_ptr theGroup )
{
  SMESH::SMESH_Group_var aGroup;

  SMESH_TRY;

  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  if ( theGroup->_is_nil() )
    return aGroup._retn();

  SMESH_GroupBase_i* aGroupToRem = SMESH::DownCast<SMESH_GroupBase_i*>( theGroup );
  if ( !aGroupToRem )
    return aGroup._retn();

  const bool isOnFilter = ( SMESH::DownCast< SMESH_GroupOnFilter_i* > ( theGroup ));

  const int anId = aGroupToRem->GetLocalID();
  if ( !_impl->ConvertToStandalone( anId ) )
    return aGroup._retn();
  removeGeomGroupData( theGroup );

  SMESH_GroupBase_i* aGroupImpl = new SMESH_Group_i( SMESH_Gen_i::GetPOA(), this, anId );

  // remove old instance of group from own map
  { SMESH::SMESH_GroupBase_var var( _mapGroups[anId] ); } // decref CORBA object
  _mapGroups.erase( anId );

  SALOMEDS::StudyBuilder_var builder;
  SALOMEDS::SObject_wrap     aGroupSO;
  SALOMEDS::Study_var        aStudy = SMESH_Gen_i::GetSMESHGen()->getStudyServant();
  if ( !aStudy->_is_nil() ) {
    builder  = aStudy->NewBuilder();
    aGroupSO = _gen_i->ObjectToSObject( theGroup );
    if ( !aGroupSO->_is_nil() )
    {
      // remove reference to geometry
      SALOMEDS::ChildIterator_wrap chItr = aStudy->NewChildIterator(aGroupSO);
      for ( ; chItr->More(); chItr->Next() )
      {
        // Remove group's child SObject
        SALOMEDS::SObject_wrap so = chItr->Value();
        builder->RemoveObject( so );
      }
      // Update Python script
      TPythonDump() << aGroupSO << " = " << SMESH::SMESH_Mesh_var(_this())
                    << ".ConvertToStandalone( " << aGroupSO << " )";

      // change icon of Group on Filter
      if ( isOnFilter )
      {
        // SMESH::array_of_ElementType_var elemTypes = aGroupImpl->GetTypes();
        // const int isEmpty = ( elemTypes->length() == 0 );
        // if ( !isEmpty )
        {
          SALOMEDS::GenericAttribute_wrap anAttr =
            builder->FindOrCreateAttribute( aGroupSO, "AttributePixMap" );
          SALOMEDS::AttributePixMap_wrap pm = anAttr;
          pm->SetPixMap( "ICON_SMESH_TREE_GROUP" );
        }
      }
    }
  }

  // remember new group in own map
  aGroup = SMESH::SMESH_Group::_narrow( aGroupImpl->_this() );
  _mapGroups[anId] = SMESH::SMESH_GroupBase::_duplicate( aGroup );

  // register CORBA object for persistence
  _gen_i->RegisterObject( aGroup );

  CORBA::String_var ior = _gen_i->GetORB()->object_to_string( aGroup );
  builder->SetIOR( aGroupSO, ior.in() ); //  == aGroup->Register();
  //aGroup->Register();
  aGroupToRem->UnRegister();

  SMESH_CATCH( SMESH::throwCorbaException );

  return aGroup._retn();
}

//================================================================================
/*!
 * \brief Create a sub-mesh on a given sub-shape
 */
//================================================================================

SMESH::SMESH_subMesh_ptr SMESH_Mesh_i::createSubMesh( GEOM::GEOM_Object_ptr theSubShapeObject )
{
  if(MYDEBUG) MESSAGE( "createSubMesh" );
  TopoDS_Shape  myLocSubShape = _gen_i->GeomObjectToShape(theSubShapeObject);
  ::SMESH_subMesh * mySubMesh = _impl->GetSubMesh(myLocSubShape);
  int               subMeshId = 0;

  SMESH_subMesh_i * subMeshServant;
  if ( mySubMesh )
  {
    subMeshId = mySubMesh->GetId();
    subMeshServant = new SMESH_subMesh_i(myPOA, _gen_i, this, subMeshId);
  }
  else // "invalid sub-mesh"
  {
    // The invalid sub-mesh is created for the case where a valid sub-shape not found
    // by SMESH_Gen_i::CopyMeshWithGeom(). The invalid sub-mesh has GetId() < 0.
    if ( _mapSubMesh.empty() )
      subMeshId = -1;
    else
      subMeshId = _mapSubMesh.begin()->first - 1;
    subMeshServant = new SMESH_Invalid_subMesh_i(myPOA, _gen_i, this, subMeshId, theSubShapeObject);
  }

  SMESH::SMESH_subMesh_var subMesh = subMeshServant->_this();

  _mapSubMesh   [subMeshId] = mySubMesh;
  _mapSubMesh_i [subMeshId] = subMeshServant;
  _mapSubMeshIor[subMeshId] = SMESH::SMESH_subMesh::_duplicate( subMesh );

  subMeshServant->Register();

  // register CORBA object for persistence
  int nextId = _gen_i->RegisterObject( subMesh );
  if(MYDEBUG) { MESSAGE( "Add submesh to map with id = "<< nextId); }
  else        { (void)nextId; } // avoid "unused variable" warning

  // to track changes of GEOM groups
  if ( subMeshId > 0 )
    addGeomGroupData( theSubShapeObject, subMesh );

  return subMesh._retn();
}

//================================================================================
/*!
 * \brief Return an existing sub-mesh based on a sub-shape with the given ID
 */
//================================================================================

SMESH::SMESH_subMesh_ptr SMESH_Mesh_i::getSubMesh(int shapeID)
{
  map<int, SMESH::SMESH_subMesh_ptr>::iterator it = _mapSubMeshIor.find( shapeID );
  if ( it == _mapSubMeshIor.end() )
    return SMESH::SMESH_subMesh::_nil();

  return SMESH::SMESH_subMesh::_duplicate( (*it).second );
}

//================================================================================
/*!
 * \brief Remove a sub-mesh based on the given sub-shape
 */
//================================================================================

bool SMESH_Mesh_i::removeSubMesh (SMESH::SMESH_subMesh_ptr theSubMesh,
                                  GEOM::GEOM_Object_ptr    theSubShapeObject )
{
  bool isHypChanged = false;
  if ( theSubMesh->_is_nil() /*|| theSubShapeObject->_is_nil()*/ )
    return isHypChanged;

  const int subMeshId = theSubMesh->GetId();

  if ( theSubShapeObject->_is_nil() )  // not published shape (IPAL13617)
  {
    SMESH_subMesh* sm;
    if (( _mapSubMesh.count( subMeshId )) &&
        ( sm = _impl->GetSubMeshContaining( subMeshId )))
    {
      TopoDS_Shape S = sm->GetSubShape();
      if ( !S.IsNull() )
      {
        list<const SMESHDS_Hypothesis*> hyps = _impl->GetHypothesisList( S );
        isHypChanged = !hyps.empty();
        if ( isHypChanged && _preMeshInfo )
          _preMeshInfo->ForgetOrLoad();
        list<const SMESHDS_Hypothesis*>::const_iterator hyp = hyps.begin();
        for ( ; hyp != hyps.end(); ++hyp )
          _impl->RemoveHypothesis(S, (*hyp)->GetID());
      }
    }
  }
  else
  {
    try {
      SMESH::ListOfHypothesis_var aHypList = GetHypothesisList( theSubShapeObject );
      isHypChanged = ( aHypList->length() > 0 );
      for ( int i = 0, n = aHypList->length(); i < n; i++ ) {
        removeHypothesis( theSubShapeObject, aHypList[i] );
      }
    }
    catch( const SALOME::SALOME_Exception& ) {
      INFOS("SMESH_Mesh_i::removeSubMesh(): exception caught!");
    }
    removeGeomGroupData( theSubShapeObject );
  }

  // remove a servant
  std::map<int, SMESH_subMesh_i*>::iterator id_smi = _mapSubMesh_i.find( subMeshId );
  if ( id_smi != _mapSubMesh_i.end() )
    id_smi->second->UnRegister();

  // remove a CORBA object
  std::map<int, SMESH::SMESH_subMesh_ptr>::iterator id_smptr = _mapSubMeshIor.find( subMeshId );
  if ( id_smptr != _mapSubMeshIor.end() )
    SMESH::SMESH_subMesh_var( id_smptr->second );

  _mapSubMesh.erase(subMeshId);
  _mapSubMesh_i.erase(subMeshId);
  _mapSubMeshIor.erase(subMeshId);

  return isHypChanged;
}

//================================================================================
/*!
 * \brief Create a group. Group type depends on given arguments
 */
//================================================================================

SMESH::SMESH_GroupBase_ptr SMESH_Mesh_i::createGroup (SMESH::ElementType        theElemType,
                                                      const char*               theName,
                                                      const int                 theID,
                                                      const TopoDS_Shape&       theShape,
                                                      const SMESH_PredicatePtr& thePredicate )
{
  std::string newName;
  if ( !theName || !theName[0] )
  {
    std::set< std::string > presentNames;
    std::map<int, SMESH::SMESH_GroupBase_ptr>::const_iterator i_gr = _mapGroups.begin();
    for ( ; i_gr != _mapGroups.end(); ++i_gr )
    {
      CORBA::String_var name = i_gr->second->GetName();
      presentNames.insert( name.in() );
    }
    do {
      newName = "noname_Group_" + SMESH_Comment( presentNames.size() + 1 );
    } while ( !presentNames.insert( newName ).second );
    theName = newName.c_str();
  }
  SMESH::SMESH_GroupBase_var aGroup;
  if ( SMESH_Group* g = _impl->AddGroup( (SMDSAbs_ElementType)theElemType, theName,
                                         theID, theShape, thePredicate ))
  {
    int anId = g->GetID();
    SMESH_GroupBase_i* aGroupImpl;
    if ( !theShape.IsNull() )
      aGroupImpl = new SMESH_GroupOnGeom_i( SMESH_Gen_i::GetPOA(), this, anId );
    else if ( thePredicate )
      aGroupImpl = new SMESH_GroupOnFilter_i( SMESH_Gen_i::GetPOA(), this, anId );
    else
      aGroupImpl = new SMESH_Group_i( SMESH_Gen_i::GetPOA(), this, anId );

    aGroup = aGroupImpl->_this();
    _mapGroups[anId] = SMESH::SMESH_GroupBase::_duplicate( aGroup );
    aGroupImpl->Register();

    // register CORBA object for persistence
    int nextId = _gen_i->RegisterObject( aGroup );
    if(MYDEBUG) { MESSAGE( "Add group to map with id = "<< nextId); }
    else        { nextId = ( nextId > 0 ); } // avoid "unused variable" warning in release mode

    // to track changes of GEOM groups
    if ( !theShape.IsNull() ) {
      GEOM::GEOM_Object_var geom = _gen_i->ShapeToGeomObject( theShape );
      addGeomGroupData( geom, aGroup );
    }
  }
  return aGroup._retn();
}

//=============================================================================
/*!
 * SMESH_Mesh_i::removeGroup
 *
 * Should be called by ~SMESH_Group_i()
 */
//=============================================================================

void SMESH_Mesh_i::removeGroup( const int theId )
{
  if(MYDEBUG) MESSAGE("SMESH_Mesh_i::removeGroup()" );
  if ( _mapGroups.find( theId ) != _mapGroups.end() ) {
    SMESH::SMESH_GroupBase_var group = _mapGroups[theId];
    _mapGroups.erase( theId );
    removeGeomGroupData( group );
    if ( !_impl->RemoveGroup( theId ))
    {
      // it seems to be a call up from _impl caused by hyp modification (issue 0020918)
      RemoveGroup( group );
    }
    group->UnRegister();
  }
}

//================================================================================
/*!
 * \brief Return a log that can be used to move another mesh to the same state as this one
 */
//================================================================================

SMESH::log_array * SMESH_Mesh_i::GetLog(CORBA::Boolean clearAfterGet)
{
  SMESH::log_array_var aLog;

  SMESH_TRY;
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  list < SMESHDS_Command * >logDS = _impl->GetLog();
  aLog = new SMESH::log_array;
  int indexLog = 0;
  int lg = logDS.size();
  aLog->length(lg);
  list < SMESHDS_Command * >::iterator its = logDS.begin();
  while(its != logDS.end()){
    SMESHDS_Command *com = *its;
    int comType = com->GetType();
    smIdType lgcom = com->GetNumber();
    const list < smIdType >&intList = com->GetIndexes();
    int inum = intList.size();
    list < smIdType >::const_iterator ii = intList.begin();
    const list < double >&coordList = com->GetCoords();
    int rnum = coordList.size();
    list < double >::const_iterator ir = coordList.begin();
    aLog[indexLog].commandType = comType;
    aLog[indexLog].number = lgcom;
    aLog[indexLog].coords.length(rnum);
    aLog[indexLog].indexes.length(inum);
    for(int i = 0; i < rnum; i++){
      aLog[indexLog].coords[i] = *ir;
      ir++;
    }
    for(int i = 0; i < inum; i++){
      aLog[indexLog].indexes[i] = *ii;
      ii++;
    }
    indexLog++;
    its++;
  }
  if(clearAfterGet)
    _impl->ClearLog();

  SMESH_CATCH( SMESH::throwCorbaException );

  return aLog._retn();
}

//================================================================================
/*!
 * \brief Remove the log of commands
 */
//================================================================================

void SMESH_Mesh_i::ClearLog()
{
  SMESH_TRY;
  _impl->ClearLog();
  SMESH_CATCH( SMESH::throwCorbaException );
}

//================================================================================
/*!
 * \brief Return a mesh ID
 */
//================================================================================

CORBA::Long SMESH_Mesh_i::GetId()
{
  return _id;
}

//=============================================================================
namespace
{
  //!< implementation of struct used to call methods of SMESH_Mesh_i from SMESH_Mesh
  // issue 0020918: groups removal is caused by hyp modification
  // issue 0021208: to forget not loaded mesh data at hyp modification
  struct TCallUp_i : public SMESH_Mesh::TCallUp
  {
    SMESH_Mesh_i* _mesh;
    TCallUp_i(SMESH_Mesh_i* mesh):_mesh(mesh) {}
    void RemoveGroup (const int theGroupID) override { _mesh->removeGroup( theGroupID ); }
    void HypothesisModified( int hypID,
                             bool updIcons) override { _mesh->onHypothesisModified( hypID,
                                                                                    updIcons ); }
    void Load ()                            override { _mesh->Load(); }
    bool IsLoaded()                         override { return _mesh->IsLoaded(); }
    TopoDS_Shape GetShapeByEntry(const std::string& entry) override
    {
      GEOM::GEOM_Object_var go = SMESH_Gen_i::GetGeomObjectByEntry( entry );
      return SMESH_Gen_i::GeomObjectToShape( go );
    }
  };
}

//================================================================================
/*!
 * \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, bool theUpdateIcons)
{
  if ( _preMeshInfo )
    _preMeshInfo->ForgetOrLoad();

  if ( theUpdateIcons )
  {
    SMESH::SMESH_Mesh_var mesh = _this();
    _gen_i->UpdateIcons( mesh );
  }

  if ( _nbInvalidHypos != 0 )
  {
    // 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::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;
  }
}

//================================================================================
/*!
 * \brief Set mesh implementation
 */
//================================================================================

void SMESH_Mesh_i::SetImpl(::SMESH_Mesh * impl)
{
  if(MYDEBUG) MESSAGE("SMESH_Mesh_i::SetImpl");
  _impl = impl;
  if ( _impl )
    _impl->SetCallUp( new TCallUp_i(this));
}

//=============================================================================
/*!
 * Return a mesh implementation
 */
//=============================================================================

::SMESH_Mesh & SMESH_Mesh_i::GetImpl()
{
  if(MYDEBUG) MESSAGE("SMESH_Mesh_i::GetImpl()");
  return *_impl;
}

//=============================================================================
/*!
 * Return mesh editor
 */
//=============================================================================

SMESH::SMESH_MeshEditor_ptr SMESH_Mesh_i::GetMeshEditor()
{
  SMESH::SMESH_MeshEditor_var aMeshEdVar;

  SMESH_TRY;
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  // Create MeshEditor
  if ( !_editor )
    _editor = new SMESH_MeshEditor_i( this, false );
  aMeshEdVar = _editor->_this();

  // Update Python script
  TPythonDump() << _editor << " = "
                << SMESH::SMESH_Mesh_var(_this()) << ".GetMeshEditor()";

  SMESH_CATCH( SMESH::throwCorbaException );

  return aMeshEdVar._retn();
}

//=============================================================================
/*!
 * Return mesh edition previewer
 */
//=============================================================================

SMESH::SMESH_MeshEditor_ptr SMESH_Mesh_i::GetMeshEditPreviewer()
{
  SMESH::SMESH_MeshEditor_var aMeshEdVar;

  SMESH_TRY;
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  if ( !_previewEditor )
    _previewEditor = new SMESH_MeshEditor_i( this, true );
  aMeshEdVar = _previewEditor->_this();

  SMESH_CATCH( SMESH::throwCorbaException );

  return aMeshEdVar._retn();
}

//================================================================================
/*!
 * \brief Return true if the mesh has been edited since a last total re-compute
 *        and those modifications may prevent successful partial re-compute
 */
//================================================================================

CORBA::Boolean SMESH_Mesh_i::HasModificationsToDiscard()
{
  Unexpect aCatch(SALOME_SalomeException);
  return _impl->HasModificationsToDiscard();
}

//================================================================================
/*!
 * \brief Return a random unique color
 */
//================================================================================

static SALOMEDS::Color getUniqueColor( const std::list<SALOMEDS::Color>& theReservedColors )
{
  const int MAX_ATTEMPTS = 100;
  int cnt = 0;
  double tolerance = 0.5;
  SALOMEDS::Color col;

  bool ok = false;
  while ( !ok ) {
    // generate random color
    double red    = (double)rand() / RAND_MAX;
    double green  = (double)rand() / RAND_MAX;
    double blue   = (double)rand() / RAND_MAX;
    // check existence in the list of the existing colors
    bool matched = false;
    std::list<SALOMEDS::Color>::const_iterator it;
    for ( it = theReservedColors.begin(); it != theReservedColors.end() && !matched; ++it ) {
      SALOMEDS::Color color = *it;
      double tol = fabs( color.R - red ) + fabs( color.G - green ) + fabs( color.B  - blue  );
      matched = tol < tolerance;
    }
    if ( (cnt+1) % 20 == 0 ) tolerance = tolerance/2;
    ok = ( ++cnt == MAX_ATTEMPTS ) || !matched;
    col.R = red;
    col.G = green;
    col.B = blue;
  }
  return col;
}

//=============================================================================
/*!
 * Set auto-color mode. If it is on, groups get unique random colors
 */
//=============================================================================

void SMESH_Mesh_i::SetAutoColor(CORBA::Boolean theAutoColor)
{
  Unexpect aCatch(SALOME_SalomeException);
  _impl->SetAutoColor(theAutoColor);

  TPythonDump pyDump; // not to dump group->SetColor() from below code
  pyDump << SMESH::SMESH_Mesh_var(_this()) <<".SetAutoColor( "<<theAutoColor<<" )";

  std::list<SALOMEDS::Color> aReservedColors;
  map<int, SMESH::SMESH_GroupBase_ptr>::iterator it = _mapGroups.begin();
  for ( ; it != _mapGroups.end(); it++ ) {
    if ( CORBA::is_nil( it->second )) continue;
    SALOMEDS::Color aColor = getUniqueColor( aReservedColors );
    it->second->SetColor( aColor );
    aReservedColors.push_back( aColor );
  }
}

//=============================================================================
/*!
 * Return true if auto-color mode is on
 */
//=============================================================================

CORBA::Boolean SMESH_Mesh_i::GetAutoColor()
{
  Unexpect aCatch(SALOME_SalomeException);
  return _impl->GetAutoColor();
}

//=============================================================================
/*!
 *  Check if there are groups with equal names
 */
//=============================================================================

CORBA::Boolean SMESH_Mesh_i::HasDuplicatedGroupNamesMED()
{
  return _impl->HasDuplicatedGroupNamesMED();
}

//================================================================================
/*!
 * \brief Care of a file before exporting mesh into it
 */
//================================================================================

void SMESH_Mesh_i::PrepareForWriting (const char* file, bool overwrite)
{
  SMESH_File aFile( file, false );
  SMESH_Comment msg;
  if ( aFile.exists() ) {
    // existing filesystem node
    if ( !aFile.isDirectory() ) {
      if ( aFile.openForWriting() ) {
        if ( overwrite && ! aFile.remove()) {
          msg << "Can't replace " << aFile.getName();
        }
      } else {
        msg << "Can't write into " << aFile.getName();
      }
    } else {
      msg << "Location " << aFile.getName() << " is not a file";
    }
  }
  else {
    // nonexisting file; check if it can be created
    if ( !aFile.openForWriting() ) {
      msg << "You cannot create the file "
          << aFile.getName()
          << ". Check the directory existence and access rights";
    }
    aFile.remove();
  }

  if ( !msg.empty() )
  {
    msg << ".";
    THROW_SALOME_CORBA_EXCEPTION(msg.c_str(), SALOME::BAD_PARAM);
  }
}

/*!
  Return a MeshName
 */
std::string SMESH_Mesh_i::generateMeshName()
{
  string aMeshName = "Mesh";
  SALOMEDS::Study_var aStudy = SMESH_Gen_i::GetSMESHGen()->getStudyServant();
  if ( !aStudy->_is_nil() )
  {
    SALOMEDS::SObject_wrap aMeshSO = _gen_i->ObjectToSObject(  _this() );
    if ( !aMeshSO->_is_nil() )
    {
      CORBA::String_var name = aMeshSO->GetName();
      aMeshName = name;
    }
  }
  return aMeshName;
}

//================================================================================
/*!
 * \brief Prepare a file for export and pass names of mesh groups from study to mesh DS
 *  \param file - file name
 *  \param overwrite - to erase the file or not
 *  \retval string - mesh name
 */
//================================================================================

string SMESH_Mesh_i::prepareMeshNameAndGroups(const char*    file,
                                              CORBA::Boolean overwrite)
{
  // Perform Export
  PrepareForWriting(file, overwrite);
  string aMeshName(this->generateMeshName());
  SALOMEDS::Study_var aStudy = SMESH_Gen_i::GetSMESHGen()->getStudyServant();
  if ( !aStudy->_is_nil() ) {
    SALOMEDS::SObject_wrap aMeshSO = _gen_i->ObjectToSObject(  _this() );
    if ( !aMeshSO->_is_nil() ) {
      // asv : 27.10.04 : fix of 6903: check for StudyLocked before adding attributes
      if ( !aStudy->GetProperties()->IsLocked() )
      {
        SALOMEDS::GenericAttribute_wrap anAttr;
        SALOMEDS::StudyBuilder_var aStudyBuilder = aStudy->NewBuilder();
        anAttr=aStudyBuilder->FindOrCreateAttribute(aMeshSO, "AttributeExternalFileDef");
        SALOMEDS::AttributeExternalFileDef_wrap aFileName = anAttr;
        ASSERT(!aFileName->_is_nil());
        aFileName->SetValue(file);
        anAttr=aStudyBuilder->FindOrCreateAttribute(aMeshSO, "AttributeFileType");
        SALOMEDS::AttributeFileType_wrap aFileType = anAttr;
        ASSERT(!aFileType->_is_nil());
        aFileType->SetValue("FICHIERMED");
      }
    }
  }
  // Update Python script
  // set name of mesh before export
  TPythonDump() << _gen_i << ".SetName("
                << SMESH::SMESH_Mesh_var(_this()) << ", '" << aMeshName.c_str() << "')";

  // check names of groups
  checkGroupNames();

  return aMeshName;
}

//================================================================================
/*!
 * \brief Export to MED file
 */
//================================================================================

void SMESH_Mesh_i::ExportMED(const char*    file,
                             CORBA::Boolean auto_groups,
                             CORBA::Long    version,
                             CORBA::Boolean overwrite,
                             CORBA::Boolean autoDimension)
{
  //MESSAGE("MED minor version: "<< minor);
  SMESH_TRY;
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  string aMeshName = prepareMeshNameAndGroups(file, overwrite);
  _impl->ExportMED( file, aMeshName.c_str(), auto_groups, version, 0, autoDimension );

  TPythonDump() << SMESH::SMESH_Mesh_var(_this()) << ".ExportMED( r'"
                << file << "', "
                << "auto_groups=" <<auto_groups << ", "
                << "version=" << version <<  ", "
                << "overwrite=" << overwrite << ", "
                << "meshPart=None, "
                << "autoDimension=" << autoDimension << " )";

  SMESH_CATCH( SMESH::throwCorbaException );
}

CORBA::LongLong SMESH_Mesh_i::ExportMEDCoupling(CORBA::Boolean auto_groups, CORBA::Boolean autoDimension)
{
  MEDCoupling::MCAuto<MEDCoupling::DataArrayByte> data;
  SMESH_TRY;
  // TODO : Fix me ! 2 next lines are required
  if( !this->_gen_i->isSSLMode() )
    SMESH::throwCorbaException("SMESH_Mesh_i::ExportMEDCoupling : only for embedded mode !");
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  string aMeshName = this->generateMeshName();
  data = _impl->ExportMEDCoupling( aMeshName.c_str(), auto_groups, 0, autoDimension );
  SMESH_CATCH( SMESH::throwCorbaException );
  MEDCoupling::DataArrayByte *ret(data.retn());
  return reinterpret_cast<CORBA::LongLong>(ret);
}

//================================================================================
/*!
 * \brief Export a mesh to a DAT file
 */
//================================================================================

void SMESH_Mesh_i::ExportDAT (const char *file, CORBA::Boolean renumber )
{
  SMESH_TRY;
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  // check names of groups
  checkGroupNames();
  // Update Python script
  TPythonDump() << SMESH::SMESH_Mesh_var(_this())
                << ".ExportDAT( r'" << file<< ", " << renumber << "' )";

  // Perform Export
  PrepareForWriting( file );
  _impl->ExportDAT( file, /*part=*/nullptr, renumber );

  SMESH_CATCH( SMESH::throwCorbaException );
}

//================================================================================
/*!
 * \brief Export a mesh to an UNV file
 */
//================================================================================

void SMESH_Mesh_i::ExportUNV (const char *file, CORBA::Boolean renumber)
{
  SMESH_TRY;
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  // check names of groups
  checkGroupNames();
  // Update Python script
  TPythonDump() << SMESH::SMESH_Mesh_var(_this())
                << ".ExportUNV( r'" << file << "', " << renumber << " )";

  // Perform Export
  PrepareForWriting( file );
  _impl->ExportUNV( file, /*part=*/nullptr, renumber );

  SMESH_CATCH( SMESH::throwCorbaException );
}

//================================================================================
/*!
 * \brief Export a mesh to an STL file
 */
//================================================================================

void SMESH_Mesh_i::ExportSTL (const char *file, const bool isascii)
{
  SMESH_TRY;
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  // check names of groups
  checkGroupNames();
  // Update Python script
  TPythonDump() << SMESH::SMESH_Mesh_var(_this())
                << ".ExportSTL( r'" << file << "', " << isascii << " )";

  CORBA::String_var name;
  SALOMEDS::SObject_wrap so = _gen_i->ObjectToSObject( _this() );
  if ( !so->_is_nil() )
    name = so->GetName();

  // Perform Export
  PrepareForWriting( file );
  _impl->ExportSTL( file, isascii, name.in() );

  SMESH_CATCH( SMESH::throwCorbaException );
}

//================================================================================

class MEDFileSpeCls
{
public:
  MEDFileSpeCls(const char *   file,
                CORBA::Boolean overwrite,
                CORBA::Long    version)
    :_file(file), _overwrite(overwrite), _version(version)
  {}
  std::string prepareMeshNameAndGroups(SMESH_Mesh_i& self)
  {
    return self.prepareMeshNameAndGroups(_file.c_str(),_overwrite);
  }

  void exportTo(SMESH_Mesh *mesh, const std::string& aMeshName, CORBA::Boolean auto_groups,
                SMESH_MeshPartDS* partDS, CORBA::Boolean autoDimension, bool have0dField,
                CORBA::Double ZTolerance, CORBA::Boolean saveNumbers )
  {
    mesh->ExportMED( _file.c_str(), aMeshName.c_str(), auto_groups, _version,
                     partDS, autoDimension, have0dField, ZTolerance, saveNumbers );
  }

  void exportField(SMESH_Mesh_i& self, const std::string& aMeshName, bool have0dField,
                   SMESHDS_Mesh *meshDS, const GEOM::ListOfFields& fields,
                   const char*geomAssocFields)
  {
    DriverMED_W_Field fieldWriter;
    fieldWriter.SetFile( _file.c_str() );
    fieldWriter.SetMeshName( aMeshName );
    fieldWriter.AddODOnVertices( have0dField );
    self.exportMEDFields( fieldWriter, meshDS, fields, geomAssocFields );
  }

  void prepareForWriting(SMESH_Mesh_i& self) { self.PrepareForWriting(_file.c_str(), _overwrite); }

private:
  std::string    _file;
  CORBA::Boolean _overwrite;
  CORBA::Long    _version;
};

//================================================================================
/*!
 * \brief Export a part of mesh to a med file
 */
//================================================================================

template<class SPECLS>
void SMESH_Mesh_i::ExportPartToMEDCommon(SPECLS&                   speCls,
                                         SMESH::SMESH_IDSource_ptr meshPart,
                                         CORBA::Boolean            auto_groups,
                                         CORBA::Boolean            autoDimension,
                                         const GEOM::ListOfFields& fields,
                                         const char*               geomAssocFields,
                                         CORBA::Double             ZTolerance,
                                         CORBA::Boolean            saveNumbers)
{
  SMESH_TRY;
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  // check fields
  bool have0dField = false;
  if ( fields.length() > 0 )
  {
    GEOM::GEOM_Object_var shapeToMesh = GetShapeToMesh();
    if ( shapeToMesh->_is_nil() )
      THROW_SALOME_CORBA_EXCEPTION( "No shape to mesh", SALOME::INTERNAL_ERROR );

    for ( size_t i = 0; i < fields.length(); ++i )
    {
      if ( fields[i]->GetDataType() == GEOM::FDT_String )
        THROW_SALOME_CORBA_EXCEPTION
          ( "Export of string fields is not supported", SALOME::BAD_PARAM);
      GEOM::GEOM_Object_var fieldShape = fields[i]->GetShape();
      if ( fieldShape->_is_nil() )
        THROW_SALOME_CORBA_EXCEPTION( "Null shape under a field", SALOME::INTERNAL_ERROR );
      if ( !fieldShape->IsSame( shapeToMesh ) )
        THROW_SALOME_CORBA_EXCEPTION( "Field defined not on shape", SALOME::BAD_PARAM);
      if ( fields[i]->GetDimension() == 0 )
        have0dField = true;
    }
    if ( geomAssocFields )
      for ( int i = 0; geomAssocFields[i]; ++i )
        switch ( geomAssocFields[i] ) {
        case 'v':case 'e':case 'f':case 's': break;
        case 'V':case 'E':case 'F':case 'S': break;
        default: THROW_SALOME_CORBA_EXCEPTION
            ( "geomAssocFields can include only [vefs] characters", SALOME::BAD_PARAM);
        }
  }

  SMESHDS_Mesh* meshDS = _impl->GetMeshDS();

  // write mesh

  string aMeshName = "Mesh";
  SMESHUtils::Deleter< SMESH_MeshPartDS > tmpDSDeleter(0);
  if ( CORBA::is_nil( meshPart ) ||
       SMESH::DownCast< SMESH_Mesh_i* >( meshPart ))
  {
    aMeshName = speCls.prepareMeshNameAndGroups(*this);
    speCls.exportTo(_impl, aMeshName, auto_groups, nullptr, autoDimension,
                    have0dField, ZTolerance, saveNumbers );
    meshDS = _impl->GetMeshDS();
  }
  else
  {
    if ( _preMeshInfo )
      _preMeshInfo->FullLoadFromFile();

    speCls.prepareForWriting(*this);

    SALOMEDS::SObject_wrap SO = _gen_i->ObjectToSObject( meshPart );
    if ( !SO->_is_nil() ) {
      CORBA::String_var name = SO->GetName();
      aMeshName = name;
    }

    SMESH_MeshPartDS* partDS = new SMESH_MeshPartDS( meshPart );
    speCls.exportTo(_impl, aMeshName, auto_groups, partDS, autoDimension,
                    have0dField, ZTolerance, saveNumbers);
    meshDS = tmpDSDeleter._obj = partDS;
  }

  // write fields

  if ( _impl->HasShapeToMesh() )
  {
    speCls.exportField( *this, aMeshName, have0dField, meshDS, fields, geomAssocFields);
  }
  SMESH_CATCH( SMESH::throwCorbaException );
}

//================================================================================
/*!
 * \brief Export a part of mesh to a med file
 */
//================================================================================

void SMESH_Mesh_i::ExportPartToMED(SMESH::SMESH_IDSource_ptr meshPart,
                                   const char*               file,
                                   CORBA::Boolean            auto_groups,
                                   CORBA::Long               version,
                                   CORBA::Boolean            overwrite,
                                   CORBA::Boolean            autoDimension,
                                   const GEOM::ListOfFields& fields,
                                   const char*               geomAssocFields,
                                   CORBA::Double             ZTolerance,
                                   CORBA::Boolean            saveNumbers)
{
  MESSAGE("MED version: "<< version);

  MEDFileSpeCls spe( file, overwrite, version );
  this->ExportPartToMEDCommon( spe, meshPart, auto_groups, autoDimension, fields,
                               geomAssocFields, ZTolerance, saveNumbers );
  // dump
  SMESH_TRY;
  GEOM::ListOfGBO_var goList = new GEOM::ListOfGBO;
  goList->length( fields.length() );
  for ( size_t i = 0; i < fields.length(); ++i )
  {
    GEOM::GEOM_BaseObject_var gbo = GEOM::GEOM_BaseObject::_narrow( fields[i] );
    goList[i] = gbo;
  }
  TPythonDump() << _this() << ".ExportPartToMED( "
                << meshPart << ", r'"
                << file << "', "
                << auto_groups << ", "
                << version << ", "
                << overwrite << ", "
                << autoDimension << ", "
                << goList << ", '"
                << ( geomAssocFields ? geomAssocFields : "" ) << "',"
                << TVar( ZTolerance ) << ", "
                << saveNumbers
                << " )";
  SMESH_CATCH( SMESH::throwCorbaException );
}

//================================================================================

class MEDFileMemSpeCls
{
public:
  std::string prepareMeshNameAndGroups(SMESH_Mesh_i& self) { return self.generateMeshName(); }

  void exportTo(SMESH_Mesh *mesh, const std::string& aMeshName, CORBA::Boolean auto_groups,
                SMESH_MeshPartDS* partDS, CORBA::Boolean autoDimension, bool have0dField,
                CORBA::Double ZTolerance, CORBA::Boolean saveNumbers )
  {
    _res = mesh->ExportMEDCoupling(aMeshName.c_str(), auto_groups, partDS,
                                   autoDimension, have0dField, ZTolerance, saveNumbers );
  }
  void prepareForWriting(SMESH_Mesh_i& /*self*/) { /* nothing here */ }

  void exportField(SMESH_Mesh_i& self, const std::string& aMeshName, bool have0dField,
                   SMESHDS_Mesh *meshDS, const GEOM::ListOfFields& fields,
                   const char*geomAssocFields)
  {
    DriverMED_W_Field_Mem fieldWriter(_res);
    fieldWriter.SetMeshName( aMeshName );
    fieldWriter.AddODOnVertices( have0dField );
    self.exportMEDFields( fieldWriter, meshDS, fields, geomAssocFields );
    _res = fieldWriter.getData();
  }
public:
  MEDCoupling::MCAuto<MEDCoupling::DataArrayByte> getData() { return _res; }

private:
  MEDCoupling::MCAuto<MEDCoupling::DataArrayByte> _res;
};

//================================================================================
/*!
 * \brief Export a part of mesh to a MEDCoupling DS
 */
//================================================================================

CORBA::LongLong SMESH_Mesh_i::ExportPartToMEDCoupling(SMESH::SMESH_IDSource_ptr meshPart,
                                                      CORBA::Boolean            auto_groups,
                                                      CORBA::Boolean            autoDimension,
                                                      const GEOM::ListOfFields& fields,
                                                      const char*               geomAssocFields,
                                                      CORBA::Double             ZTolerance,
                                                      CORBA::Boolean            saveNumbers)
{
  MEDCoupling::MCAuto<MEDCoupling::DataArrayByte> data;

  SMESH_TRY;
  if( !this->_gen_i->isSSLMode() )
    SMESH::throwCorbaException("SMESH_Mesh_i::ExportPartToMEDCoupling : only for embedded mode !");

  MEDFileMemSpeCls spe;
  this->ExportPartToMEDCommon( spe, meshPart, auto_groups, autoDimension, fields, geomAssocFields,
                               ZTolerance, saveNumbers );
  data = spe.getData();

  SMESH_CATCH( SMESH::throwCorbaException );

  MEDCoupling::DataArrayByte *ret(data.retn());
  return reinterpret_cast<CORBA::LongLong>(ret);
}

//================================================================================
/*!
 * Write GEOM fields to MED file
 */
//================================================================================

void SMESH_Mesh_i::exportMEDFields( DriverMED_W_Field&        fieldWriter,
                                    SMESHDS_Mesh*             meshDS,
                                    const GEOM::ListOfFields& fields,
                                    const char*               geomAssocFields)
{
#define METH "SMESH_Mesh_i::exportMEDFields() "

  if (( fields.length() < 1 ) &&
      ( !geomAssocFields || !geomAssocFields[0] ))
    return;

  std::vector< std::vector< double > > dblVals;
  std::vector< std::vector< int > >    intVals;
  std::vector< int >                   subIdsByDim[ 4 ];
  const double noneDblValue = 0.;
  const double noneIntValue = 0;

  for ( size_t iF = 0; iF < fields.length(); ++iF )
  {
    // set field data

    int dim = fields[ iF ]->GetDimension();
    SMDSAbs_ElementType elemType;
    TopAbs_ShapeEnum    shapeType;
    switch ( dim ) {
    case 0: elemType = SMDSAbs_0DElement; shapeType = TopAbs_VERTEX; break;
    case 1: elemType = SMDSAbs_Edge;      shapeType = TopAbs_EDGE;   break;
    case 2: elemType = SMDSAbs_Face;      shapeType = TopAbs_FACE;   break;
    case 3: elemType = SMDSAbs_Volume;    shapeType = TopAbs_SOLID;  break;
    default:
      continue; // skip fields on whole shape
    }
    GEOM::field_data_type dataType = fields[ iF ]->GetDataType();
    if ( dataType == GEOM::FDT_String )
      continue;
    GEOM::ListOfLong_var stepIDs = fields[ iF ]->GetSteps();
    if ( stepIDs->length() < 1 )
      continue;
    GEOM::string_array_var comps = fields[ iF ]->GetComponents();
    if ( comps->length() < 1 )
      continue;
    CORBA::String_var       name = fields[ iF ]->GetName();

    if ( !fieldWriter.Set( meshDS,
                           name.in(),
                           elemType,
                           comps->length(),
                           /*isIntData=*/false ))//( dataType == GEOM::FDT_Int )))
      continue;

    for ( size_t iC = 0; iC < comps->length(); ++iC )
      fieldWriter.SetCompName( iC, comps[ iC ].in() );

    dblVals.resize( comps->length() );
    intVals.resize( comps->length() );

    // find sub-shape IDs

    std::vector< int >& subIds = subIdsByDim[ dim ];
    if ( subIds.empty() )
      for ( int id = 1; id <= meshDS->MaxShapeIndex(); ++id )
        if ( meshDS->IndexToShape( id ).ShapeType() == shapeType )
          subIds.push_back( id );

    // write steps

    SMDS_ElemIteratorPtr elemIt = fieldWriter.GetOrderedElems();
    if ( !elemIt )
      continue;

    for ( size_t iS = 0; iS < stepIDs->length(); ++iS )
    {
      GEOM::GEOM_FieldStep_var step = fields[ iF ]->GetStep( stepIDs[ iS ]);
      if ( step->_is_nil() )
        continue;

      CORBA::Long stamp = step->GetStamp();
      CORBA::Long id    = step->GetID();
      fieldWriter.SetDtIt( int( stamp ), int( id ));

      // fill dblVals or intVals
      for ( size_t iC = 0; iC < comps->length(); ++iC )
        if ( dataType == GEOM::FDT_Double )
        {
          dblVals[ iC ].clear();
          dblVals[ iC ].resize( meshDS->MaxShapeIndex()+1, 0 );
        }
        else
        {
          intVals[ iC ].clear();
          intVals[ iC ].resize( meshDS->MaxShapeIndex()+1, 0 );
        }
      switch ( dataType )
      {
      case GEOM::FDT_Double:
      {
        GEOM::GEOM_DoubleFieldStep_var dblStep = GEOM::GEOM_DoubleFieldStep::_narrow( step );
        if ( dblStep->_is_nil() ) continue;
        GEOM::ListOfDouble_var vv = dblStep->GetValues();
        if ( vv->length() != subIds.size() * comps->length() )
          THROW_SALOME_CORBA_EXCEPTION( METH "BUG: wrong nb subIds", SALOME::INTERNAL_ERROR );
        for ( size_t iS = 0, iV = 0; iS < subIds.size(); ++iS )
          for ( size_t iC = 0; iC < comps->length(); ++iC )
            dblVals[ iC ][ subIds[ iS ]] = vv[ iV++ ];
        break;
      }
      case GEOM::FDT_Int:
      {
        GEOM::GEOM_IntFieldStep_var intStep = GEOM::GEOM_IntFieldStep::_narrow( step );
        if ( intStep->_is_nil() ) continue;
        GEOM::ListOfLong_var vv = intStep->GetValues();
        if ( vv->length() != subIds.size() * comps->length() )
          THROW_SALOME_CORBA_EXCEPTION( METH "BUG: wrong nb subIds", SALOME::INTERNAL_ERROR );
        for ( size_t iS = 0, iV = 0; iS < subIds.size(); ++iS )
          for ( size_t iC = 0; iC < comps->length(); ++iC )
            intVals[ iC ][ subIds[ iS ]] = (int) vv[ iV++ ];
        break;
      }
      case GEOM::FDT_Bool:
      {
        GEOM::GEOM_BoolFieldStep_var boolStep = GEOM::GEOM_BoolFieldStep::_narrow( step );
        if ( boolStep->_is_nil() ) continue;
        GEOM::short_array_var vv = boolStep->GetValues();
        if ( vv->length() != subIds.size() * comps->length() )
          THROW_SALOME_CORBA_EXCEPTION( METH "BUG: wrong nb subIds", SALOME::INTERNAL_ERROR );
        for ( size_t iS = 0, iV = 0; iS < subIds.size(); ++iS )
          for ( size_t iC = 0; iC < comps->length(); ++iC )
            intVals[ iC ][ subIds[ iS ]] = (int) vv[ iV++ ];
        break;
      }
      default: continue;
      }

      // pass values to fieldWriter
      elemIt = fieldWriter.GetOrderedElems();
      if ( dataType == GEOM::FDT_Double )
        while ( elemIt->more() )
        {
          const SMDS_MeshElement* e = elemIt->next();
          const int shapeID = e->getshapeId();
          if ( shapeID < 1 || shapeID >= (int) dblVals[0].size() )
            for ( size_t iC = 0; iC < comps->length(); ++iC )
              fieldWriter.AddValue( noneDblValue );
          else
            for ( size_t iC = 0; iC < comps->length(); ++iC )
              fieldWriter.AddValue( dblVals[ iC ][ shapeID ]);
        }
      else
        while ( elemIt->more() )
        {
          const SMDS_MeshElement* e = elemIt->next();
          const int shapeID = e->getshapeId();
          if ( shapeID < 1 || shapeID >= (int) intVals[0].size() )
            for ( size_t iC = 0; iC < comps->length(); ++iC )
              fieldWriter.AddValue( (double) noneIntValue );
          else
            for ( size_t iC = 0; iC < comps->length(); ++iC )
              fieldWriter.AddValue( (double) intVals[ iC ][ shapeID ]);
        }

      // write a step
      fieldWriter.Perform();
      SMESH_ComputeErrorPtr res = fieldWriter.GetError();
      if ( res && res->IsKO() )
      {
        if ( res->myComment.empty() )
        { THROW_SALOME_CORBA_EXCEPTION( METH "Fatal error", SALOME::INTERNAL_ERROR ); }
        else
        { THROW_SALOME_CORBA_EXCEPTION( res->myComment.c_str(), SALOME::INTERNAL_ERROR ); }
      }

    } // loop on steps
  } // loop on fields

  if ( !geomAssocFields || !geomAssocFields[0] )
    return;

  // write geomAssocFields

  std::vector< int > shapeDim( TopAbs_SHAPE + 1 );
  shapeDim[ TopAbs_COMPOUND  ] = 3;
  shapeDim[ TopAbs_COMPSOLID ] = 3;
  shapeDim[ TopAbs_SOLID     ] = 3;
  shapeDim[ TopAbs_SHELL     ] = 2;
  shapeDim[ TopAbs_FACE      ] = 2;
  shapeDim[ TopAbs_WIRE      ] = 1;
  shapeDim[ TopAbs_EDGE      ] = 1;
  shapeDim[ TopAbs_VERTEX    ] = 0;
  shapeDim[ TopAbs_SHAPE     ] = 3;

  for ( int iF = 0; geomAssocFields[ iF ]; ++iF )
  {
    std::vector< std::string > compNames;
    switch ( geomAssocFields[ iF ]) {
    case 'v': case 'V':
      fieldWriter.Set( meshDS, "_vertices_", SMDSAbs_Node, /*nbComps=*/2, /*isInt=*/false );
      compNames.push_back( "dim" );
      break;
    case 'e': case 'E':
      fieldWriter.Set( meshDS, "_edges_", SMDSAbs_Edge, /*nbComps=*/1, /*isInt=*/false );
      break;
    case 'f': case 'F':
      fieldWriter.Set( meshDS, "_faces_", SMDSAbs_Face, /*nbComps=*/1, /*isInt=*/false );
      break;
    case 's': case 'S':
      fieldWriter.Set( meshDS, "_solids_", SMDSAbs_Volume, /*nbComps=*/1, /*isInt=*/false );
      break;
    default: continue;
    }
    compNames.push_back( "id" );
    for ( size_t iC = 0; iC < compNames.size(); ++iC )
      fieldWriter.SetCompName( iC, compNames[ iC ].c_str() );

    fieldWriter.SetDtIt( -1, -1 );

    SMDS_ElemIteratorPtr elemIt = fieldWriter.GetOrderedElems();
    if ( !elemIt )
      continue;

    if ( compNames.size() == 2 ) // _vertices_
      while ( elemIt->more() )
      {
        const SMDS_MeshElement* e = elemIt->next();
        const int shapeID = e->getshapeId();
        if ( shapeID < 1 )
        {
          fieldWriter.AddValue( (double) -1 );
          fieldWriter.AddValue( (double) -1 );
        }
        else
        {
          const TopoDS_Shape& S = meshDS->IndexToShape( shapeID );
          fieldWriter.AddValue( (double) ( S.IsNull() ? -1 : shapeDim[ S.ShapeType() ]));
          fieldWriter.AddValue( (double) shapeID );
        }
      }
    else
      while ( elemIt->more() )
      {
        const SMDS_MeshElement* e = elemIt->next();
        const int shapeID = e->getshapeId();
        if ( shapeID < 1 )
          fieldWriter.AddValue( (double) -1 );
        else
          fieldWriter.AddValue( (double) shapeID );
      }

    // write a step
    fieldWriter.Perform();
    SMESH_ComputeErrorPtr res = fieldWriter.GetError();
    if ( res && res->IsKO() )
    {
      if ( res->myComment.empty() )
      { THROW_SALOME_CORBA_EXCEPTION( METH "Fatal error", SALOME::INTERNAL_ERROR ); }
      else
      { THROW_SALOME_CORBA_EXCEPTION( res->myComment.c_str(), SALOME::INTERNAL_ERROR ); }
    }

  } // loop on geomAssocFields

#undef METH
}

//================================================================================
/*!
 * \brief Export a part of mesh to a DAT file
 */
//================================================================================

void SMESH_Mesh_i::ExportPartToDAT(SMESH::SMESH_IDSource_ptr meshPart,
                                   const char*               file,
                                   CORBA::Boolean            renumber )
{
  SMESH_TRY;

  SMESH_MeshPartDS partDS( meshPart );
  _impl->ExportDAT( file, &partDS, renumber );

  TPythonDump() << SMESH::SMESH_Mesh_var(_this())
                << ".ExportPartToDAT( " << meshPart << ", r'" << file << ", " << renumber << "' )";

  SMESH_CATCH( SMESH::throwCorbaException );
}
//================================================================================
/*!
 * \brief Export a part of mesh to an UNV file
 */
//================================================================================

void SMESH_Mesh_i::ExportPartToUNV(SMESH::SMESH_IDSource_ptr meshPart,
                                   const char*               file,
                                   CORBA::Boolean            renumber)
{
  SMESH_TRY;
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  PrepareForWriting(file);

  SMESH_MeshPartDS partDS( meshPart );
  _impl->ExportUNV(file, &partDS, renumber );

  TPythonDump() << SMESH::SMESH_Mesh_var(_this())
                << ".ExportPartToUNV( " << meshPart<< ", r'" << file << ", " << renumber << "' )";

  SMESH_CATCH( SMESH::throwCorbaException );
}
//================================================================================
/*!
 * \brief Export a part of mesh to an STL file
 */
//================================================================================

void SMESH_Mesh_i::ExportPartToSTL(::SMESH::SMESH_IDSource_ptr meshPart,
                                   const char*                 file,
                                   ::CORBA::Boolean            isascii)
{
  SMESH_TRY;
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  PrepareForWriting(file);

  CORBA::String_var name;
  SALOMEDS::SObject_wrap so = _gen_i->ObjectToSObject( meshPart );
  if ( !so->_is_nil() )
    name = so->GetName();

  SMESH_MeshPartDS partDS( meshPart );
  _impl->ExportSTL( file, isascii, name.in(), &partDS );

  TPythonDump() << SMESH::SMESH_Mesh_var(_this()) << ".ExportPartToSTL( "
                << meshPart<< ", r'" << file << "', " << isascii << ")";

  SMESH_CATCH( SMESH::throwCorbaException );
}

//================================================================================
/*!
 * \brief Export a part of mesh to an STL file
 */
//================================================================================

void SMESH_Mesh_i::ExportCGNS(::SMESH::SMESH_IDSource_ptr meshPart,
                              const char*                 file,
                              CORBA::Boolean              overwrite,
                              CORBA::Boolean              groupElemsByType)
{
#ifdef WITH_CGNS
  SMESH_TRY;
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  PrepareForWriting(file,overwrite);

  std::string meshName("");
  SALOMEDS::SObject_wrap so = _gen_i->ObjectToSObject( meshPart );
  if ( !so->_is_nil() )
  {
    CORBA::String_var name = so->GetName();
    meshName = name.in();
  }
  SMESH_TRY;

  SMESH_MeshPartDS partDS( meshPart );
  _impl->ExportCGNS(file, &partDS, meshName.c_str(), groupElemsByType );

  SMESH_CATCH( SMESH::throwCorbaException );

  TPythonDump() << SMESH::SMESH_Mesh_var(_this()) << ".ExportCGNS( "
                << meshPart<< ", r'" << file << "', " << overwrite << ")";

  SMESH_CATCH( SMESH::throwCorbaException );

#else
  THROW_SALOME_CORBA_EXCEPTION("CGNS library is unavailable", SALOME::INTERNAL_ERROR);
#endif
}

//================================================================================
/*!
 * \brief Export a part of mesh to a GMF file
 */
//================================================================================

void SMESH_Mesh_i::ExportGMF(::SMESH::SMESH_IDSource_ptr meshPart,
                             const char*                 file,
                             bool                        withRequiredGroups)
{
  SMESH_TRY;
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  PrepareForWriting(file,/*overwrite=*/true);

  SMESH_MeshPartDS partDS( meshPart );
  _impl->ExportGMF(file, &partDS, withRequiredGroups);

  TPythonDump() << SMESH::SMESH_Mesh_var(_this()) << ".ExportGMF( "
                << meshPart<< ", r'"
                << file << "', "
                << withRequiredGroups << ")";

  SMESH_CATCH( SMESH::throwCorbaException );
}

//=============================================================================
/*!
 * Return computation progress [0.,1]
 */
//=============================================================================

CORBA::Double SMESH_Mesh_i::GetComputeProgress()
{
  SMESH_TRY;

  return _impl->GetComputeProgress();

  SMESH_CATCH( SMESH::doNothing );
  return 0.;
}

//================================================================================
/*!
 * \brief Return nb of nodes
 */
//================================================================================

SMESH::smIdType SMESH_Mesh_i::NbNodes()
{
  Unexpect aCatch(SALOME_SalomeException);
  if ( _preMeshInfo )
    return _preMeshInfo->NbNodes();

  return _impl->NbNodes();
}

//================================================================================
/*!
 * \brief Return nb of elements
 */
//================================================================================

SMESH::smIdType SMESH_Mesh_i::NbElements()
{
  Unexpect aCatch(SALOME_SalomeException);
  if ( _preMeshInfo )
    return _preMeshInfo->NbElements();

  return Nb0DElements() + NbEdges() + NbFaces() + NbVolumes() + NbBalls();
}

//================================================================================
/*!
 * \brief Return nb of 0D elements
 */
//================================================================================

SMESH::smIdType SMESH_Mesh_i::Nb0DElements()
{
  Unexpect aCatch(SALOME_SalomeException);
  if ( _preMeshInfo )
    return _preMeshInfo->Nb0DElements();

  return _impl->Nb0DElements();
}

//================================================================================
/*!
 * \brief Return nb of BALL elements
 */
//================================================================================

SMESH::smIdType SMESH_Mesh_i::NbBalls()
{
  Unexpect aCatch(SALOME_SalomeException);
  if ( _preMeshInfo )
    return _preMeshInfo->NbBalls();

  return _impl->NbBalls();
}

//================================================================================
/*!
 * \brief Return nb of 1D elements
 */
//================================================================================

SMESH::smIdType SMESH_Mesh_i::NbEdges()
{
  Unexpect aCatch(SALOME_SalomeException);
  if ( _preMeshInfo )
    return _preMeshInfo->NbEdges();

  return _impl->NbEdges();
}

//================================================================================
/*!
 * \brief Return nb of edges
 */
//================================================================================

SMESH::smIdType SMESH_Mesh_i::NbEdgesOfOrder(SMESH::ElementOrder order)
{
  Unexpect aCatch(SALOME_SalomeException);
  if ( _preMeshInfo )
    return _preMeshInfo->NbEdges( (SMDSAbs_ElementOrder) order );

  return _impl->NbEdges( (SMDSAbs_ElementOrder) order);
}

//================================================================================
/*!
 * \brief Return nb of faces
 */
//================================================================================

SMESH::smIdType SMESH_Mesh_i::NbFaces()
{
  Unexpect aCatch(SALOME_SalomeException);
  if ( _preMeshInfo )
    return _preMeshInfo->NbFaces();

  return _impl->NbFaces();
}

//================================================================================
/*!
 * \brief Return nb of tringles
 */
//================================================================================

SMESH::smIdType SMESH_Mesh_i::NbTriangles()
{
  Unexpect aCatch(SALOME_SalomeException);
  if ( _preMeshInfo )
    return _preMeshInfo->NbTriangles();

  return _impl->NbTriangles();
}

//================================================================================
/*!
 * \brief Return nb of bi-quadratic triangles
 */
//================================================================================

SMESH::smIdType SMESH_Mesh_i::NbBiQuadTriangles()
{
  Unexpect aCatch(SALOME_SalomeException);
  if ( _preMeshInfo )
    return _preMeshInfo->NbBiQuadTriangles();

  return _impl->NbBiQuadTriangles();
}

SMESH::smIdType SMESH_Mesh_i::NbQuadrangles()
{
  Unexpect aCatch(SALOME_SalomeException);
  if ( _preMeshInfo )
    return _preMeshInfo->NbQuadrangles();

  return _impl->NbQuadrangles();
}

SMESH::smIdType SMESH_Mesh_i::NbBiQuadQuadrangles()
{
  Unexpect aCatch(SALOME_SalomeException);
  if ( _preMeshInfo )
    return _preMeshInfo->NbBiQuadQuadrangles();

  return _impl->NbBiQuadQuadrangles();
}

SMESH::smIdType SMESH_Mesh_i::NbPolygons()
{
  Unexpect aCatch(SALOME_SalomeException);
  if ( _preMeshInfo )
    return _preMeshInfo->NbPolygons();

  return _impl->NbPolygons();
}

SMESH::smIdType SMESH_Mesh_i::NbPolygonsOfOrder(SMESH::ElementOrder order)
{
  Unexpect aCatch(SALOME_SalomeException);
  if ( _preMeshInfo )
    return _preMeshInfo->NbPolygons((SMDSAbs_ElementOrder) order);

  return _impl->NbPolygons((SMDSAbs_ElementOrder)order);
}

SMESH::smIdType SMESH_Mesh_i::NbFacesOfOrder(SMESH::ElementOrder order)
{
  Unexpect aCatch(SALOME_SalomeException);
  if ( _preMeshInfo )
    return _preMeshInfo->NbFaces( (SMDSAbs_ElementOrder) order );

  return _impl->NbFaces( (SMDSAbs_ElementOrder) order);
}

SMESH::smIdType SMESH_Mesh_i::NbTrianglesOfOrder(SMESH::ElementOrder order)
{
  Unexpect aCatch(SALOME_SalomeException);
  if ( _preMeshInfo )
    return _preMeshInfo->NbTriangles( (SMDSAbs_ElementOrder) order );

  return _impl->NbTriangles( (SMDSAbs_ElementOrder) order);
}

SMESH::smIdType SMESH_Mesh_i::NbQuadranglesOfOrder(SMESH::ElementOrder order)
{
  Unexpect aCatch(SALOME_SalomeException);
  if ( _preMeshInfo )
    return _preMeshInfo->NbQuadrangles( (SMDSAbs_ElementOrder) order );

  return _impl->NbQuadrangles( (SMDSAbs_ElementOrder) order);
}

//=============================================================================

SMESH::smIdType SMESH_Mesh_i::NbVolumes()
{
  Unexpect aCatch(SALOME_SalomeException);
  if ( _preMeshInfo )
    return _preMeshInfo->NbVolumes();

  return _impl->NbVolumes();
}

SMESH::smIdType SMESH_Mesh_i::NbTetras()
{
  Unexpect aCatch(SALOME_SalomeException);
  if ( _preMeshInfo )
    return _preMeshInfo->NbTetras();

  return _impl->NbTetras();
}

SMESH::smIdType SMESH_Mesh_i::NbHexas()
{
  Unexpect aCatch(SALOME_SalomeException);
  if ( _preMeshInfo )
    return _preMeshInfo->NbHexas();

  return _impl->NbHexas();
}

SMESH::smIdType SMESH_Mesh_i::NbTriQuadraticHexas()
{
  Unexpect aCatch(SALOME_SalomeException);
  if ( _preMeshInfo )
    return _preMeshInfo->NbTriQuadHexas();

  return _impl->NbTriQuadraticHexas();
}

SMESH::smIdType SMESH_Mesh_i::NbPyramids()
{
  Unexpect aCatch(SALOME_SalomeException);
  if ( _preMeshInfo )
    return _preMeshInfo->NbPyramids();

  return _impl->NbPyramids();
}

SMESH::smIdType SMESH_Mesh_i::NbPrisms()
{
  Unexpect aCatch(SALOME_SalomeException);
  if ( _preMeshInfo )
    return _preMeshInfo->NbPrisms();

  return _impl->NbPrisms();
}

SMESH::smIdType SMESH_Mesh_i::NbHexagonalPrisms()
{
  Unexpect aCatch(SALOME_SalomeException);
  if ( _preMeshInfo )
    return _preMeshInfo->NbHexPrisms();

  return _impl->NbHexagonalPrisms();
}

SMESH::smIdType SMESH_Mesh_i::NbPolyhedrons()
{
  Unexpect aCatch(SALOME_SalomeException);
  if ( _preMeshInfo )
    return _preMeshInfo->NbPolyhedrons();

  return _impl->NbPolyhedrons();
}

SMESH::smIdType SMESH_Mesh_i::NbVolumesOfOrder(SMESH::ElementOrder order)
{
  Unexpect aCatch(SALOME_SalomeException);
  if ( _preMeshInfo )
    return _preMeshInfo->NbVolumes( (SMDSAbs_ElementOrder) order );

  return _impl->NbVolumes( (SMDSAbs_ElementOrder) order);
}

SMESH::smIdType SMESH_Mesh_i::NbTetrasOfOrder(SMESH::ElementOrder order)
{
  Unexpect aCatch(SALOME_SalomeException);
  if ( _preMeshInfo )
    return _preMeshInfo->NbTetras( (SMDSAbs_ElementOrder) order);

  return _impl->NbTetras( (SMDSAbs_ElementOrder) order);
}

SMESH::smIdType SMESH_Mesh_i::NbHexasOfOrder(SMESH::ElementOrder order)
{
  Unexpect aCatch(SALOME_SalomeException);
  if ( _preMeshInfo )
    return _preMeshInfo->NbHexas( (SMDSAbs_ElementOrder) order);

  return _impl->NbHexas( (SMDSAbs_ElementOrder) order);
}

SMESH::smIdType SMESH_Mesh_i::NbPyramidsOfOrder(SMESH::ElementOrder order)
{
  Unexpect aCatch(SALOME_SalomeException);
  if ( _preMeshInfo )
    return _preMeshInfo->NbPyramids( (SMDSAbs_ElementOrder) order);

  return _impl->NbPyramids( (SMDSAbs_ElementOrder) order);
}

SMESH::smIdType SMESH_Mesh_i::NbPrismsOfOrder(SMESH::ElementOrder order)
{
  Unexpect aCatch(SALOME_SalomeException);
  if ( _preMeshInfo )
    return _preMeshInfo->NbPrisms( (SMDSAbs_ElementOrder) order);

  return _impl->NbPrisms( (SMDSAbs_ElementOrder) order);
}

//=============================================================================
/*!
 * Return nb of published sub-meshes
 */
//=============================================================================

SMESH::smIdType SMESH_Mesh_i::NbSubMesh()
{
  Unexpect aCatch(SALOME_SalomeException);
  return _mapSubMesh_i.size();
}

//=============================================================================
/*!
 * Dumps mesh into a string
 */
//=============================================================================

char* SMESH_Mesh_i::Dump()
{
  ostringstream os;
  _impl->Dump( os );
  return CORBA::string_dup( os.str().c_str() );
}

//=============================================================================
/*!
 * Method of SMESH_IDSource interface
 */
//=============================================================================

SMESH::smIdType_array* SMESH_Mesh_i::GetIDs()
{
  return GetElementsId();
}

//=============================================================================
/*!
 * Return ids of all elements
 */
//=============================================================================

SMESH::smIdType_array* SMESH_Mesh_i::GetElementsId()
{
  Unexpect aCatch(SALOME_SalomeException);
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  SMESH::smIdType_array_var aResult = new SMESH::smIdType_array();
  SMESHDS_Mesh* aSMESHDS_Mesh = _impl->GetMeshDS();

  if ( aSMESHDS_Mesh == NULL )
    return aResult._retn();

  smIdType nbElements = NbElements();
  aResult->length( nbElements );
  SMDS_ElemIteratorPtr anIt = aSMESHDS_Mesh->elementsIterator();
  for ( smIdType i = 0, n = nbElements; i < n && anIt->more(); i++ )
    aResult[i] = anIt->next()->GetID();

  return aResult._retn();
}


//=============================================================================
/*!
 * Return ids of all elements of given type
 */
//=============================================================================

SMESH::smIdType_array* SMESH_Mesh_i::GetElementsByType( SMESH::ElementType theElemType )
{
  Unexpect aCatch(SALOME_SalomeException);
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  SMESH::smIdType_array_var aResult = new SMESH::smIdType_array();
  SMESHDS_Mesh* aSMESHDS_Mesh = _impl->GetMeshDS();

  if ( aSMESHDS_Mesh == NULL )
    return aResult._retn();

  smIdType nbElements = NbElements();

  // No sense in returning ids of elements along with ids of nodes:
  // when theElemType == SMESH::ALL, return node ids only if
  // there are no elements
  if ( theElemType == SMESH::NODE || (theElemType == SMESH::ALL && nbElements == 0) )
    return GetNodesId();

  aResult->length( nbElements );

  smIdType i = 0;

  SMDS_ElemIteratorPtr anIt = aSMESHDS_Mesh->elementsIterator( (SMDSAbs_ElementType)theElemType );
  while ( i < nbElements && anIt->more() )
    aResult[i++] = anIt->next()->GetID();

  aResult->length( i );

  return aResult._retn();
}

//=============================================================================
/*!
 * Return ids of all nodes
 */
//=============================================================================

SMESH::smIdType_array* SMESH_Mesh_i::GetNodesId()
{
  Unexpect aCatch(SALOME_SalomeException);
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  SMESH::smIdType_array_var aResult = new SMESH::smIdType_array();
  SMESHDS_Mesh* aMeshDS = _impl->GetMeshDS();

  if ( aMeshDS == NULL )
    return aResult._retn();

  smIdType nbNodes = NbNodes();
  aResult->length( nbNodes );
  SMDS_NodeIteratorPtr anIt = aMeshDS->nodesIterator();
  for ( smIdType i = 0, n = nbNodes; i < n && anIt->more(); i++ )
    aResult[i] = anIt->next()->GetID();

  return aResult._retn();
}

//=============================================================================
/*!
 * Return type of the given element
 */
//=============================================================================

SMESH::ElementType SMESH_Mesh_i::GetElementType( const SMESH::smIdType id, const bool iselem )
{
  SMESH::ElementType type = SMESH::ALL;
  SMESH_TRY;

  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  type = ( SMESH::ElementType ) _impl->GetElementType( id, iselem );

  SMESH_CATCH( SMESH::throwCorbaException );

  return type;
}

//=============================================================================
/*!
 * Return geometric type of the given element
 */
//=============================================================================

SMESH::EntityType SMESH_Mesh_i::GetElementGeomType( const SMESH::smIdType id )
{
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  const SMDS_MeshElement* e = _impl->GetMeshDS()->FindElement(id);
  if ( !e )
    THROW_SALOME_CORBA_EXCEPTION( "invalid element id", SALOME::BAD_PARAM );

  return ( SMESH::EntityType ) e->GetEntityType();
}

//=============================================================================
/*!
 * Return type of the given element
 */
//=============================================================================

SMESH::GeometryType SMESH_Mesh_i::GetElementShape( const SMESH::smIdType id )
{
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  const SMDS_MeshElement* e = _impl->GetMeshDS()->FindElement(id);
  if ( !e )
    THROW_SALOME_CORBA_EXCEPTION( "invalid element id", SALOME::BAD_PARAM );

  return ( SMESH::GeometryType ) e->GetGeomType();
}

//=============================================================================
/*!
 * Return ID of elements for given submesh
 */
//=============================================================================

SMESH::smIdType_array* SMESH_Mesh_i::GetSubMeshElementsId(const CORBA::Long ShapeID)
{
  SMESH::smIdType_array_var aResult = new SMESH::smIdType_array();

  SMESH_TRY;
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  SMESH_subMesh* SM = _impl->GetSubMeshContaining(ShapeID);
  if(!SM) return aResult._retn();

  SMESHDS_SubMesh* SDSM = SM->GetSubMeshDS();
  if(!SDSM) return aResult._retn();

  aResult->length(SDSM->NbElements());

  SMDS_ElemIteratorPtr eIt = SDSM->GetElements();
  smIdType i = 0;
  while ( eIt->more() ) {
    aResult[i++] = eIt->next()->GetID();
  }

  SMESH_CATCH( SMESH::throwCorbaException );

  return aResult._retn();
}

//=============================================================================
/*!
 * Return ID of nodes for given sub-mesh
 * If param all==true - return all nodes, else -
 * Return only nodes on shapes.
 */
//=============================================================================

SMESH::smIdType_array* SMESH_Mesh_i::GetSubMeshNodesId(const CORBA::Long ShapeID,
                                                   CORBA::Boolean    all)
{
  SMESH::smIdType_array_var aResult = new SMESH::smIdType_array();

  SMESH_TRY;
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  SMESH_subMesh* SM = _impl->GetSubMeshContaining(ShapeID);
  if(!SM) return aResult._retn();

  SMESHDS_SubMesh* SDSM = SM->GetSubMeshDS();
  if(!SDSM) return aResult._retn();

  set<smIdType> theElems;
  if( !all || (SDSM->NbElements()==0) ) { // internal nodes or vertex submesh
    SMDS_NodeIteratorPtr nIt = SDSM->GetNodes();
    while ( nIt->more() ) {
      const SMDS_MeshNode* elem = nIt->next();
      theElems.insert( elem->GetID() );
    }
  }
  else { // all nodes of submesh elements
    SMDS_ElemIteratorPtr eIt = SDSM->GetElements();
    while ( eIt->more() ) {
      const SMDS_MeshElement* anElem = eIt->next();
      SMDS_ElemIteratorPtr nIt = anElem->nodesIterator();
      while ( nIt->more() ) {
        const SMDS_MeshElement* elem = nIt->next();
        theElems.insert( elem->GetID() );
      }
    }
  }

  aResult->length(theElems.size());
  set<smIdType>::iterator itElem;
  smIdType i = 0;
  for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
    aResult[i++] = *itElem;

  SMESH_CATCH( SMESH::throwCorbaException );

  return aResult._retn();
}

//=============================================================================
/*!
 * Return type of elements for given sub-mesh
 */
//=============================================================================

SMESH::ElementType SMESH_Mesh_i::GetSubMeshElementType(const CORBA::Long ShapeID)
{
  SMESH::ElementType type = SMESH::ALL;

  SMESH_TRY;
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  SMESH_subMesh* SM = _impl->GetSubMeshContaining(ShapeID);
  if(!SM) return SMESH::ALL;

  SMESHDS_SubMesh* SDSM = SM->GetSubMeshDS();
  if(!SDSM) return SMESH::ALL;

  if(SDSM->NbElements()==0)
    return (SM->GetSubShape().ShapeType() == TopAbs_VERTEX) ? SMESH::NODE : SMESH::ALL;

  SMDS_ElemIteratorPtr eIt = SDSM->GetElements();
  const SMDS_MeshElement* anElem = eIt->next();

  type = ( SMESH::ElementType ) anElem->GetType();

  SMESH_CATCH( SMESH::throwCorbaException );

  return type;
}


//=============================================================================
/*!
 * Return pointer to _impl as an integer value. Is called from constructor of SMESH_Client
 */
//=============================================================================

CORBA::LongLong SMESH_Mesh_i::GetMeshPtr()
{
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  CORBA::LongLong pointeur = CORBA::LongLong(_impl);
  if ( MYDEBUG ) MESSAGE("CORBA::LongLong SMESH_Mesh_i::GetMeshPtr() "<<pointeur);
  return pointeur;
}


//=============================================================================
/*!
 * Get XYZ coordinates of node as list of double
 * If there is not node for given ID - return empty list
 */
//=============================================================================

SMESH::double_array* SMESH_Mesh_i::GetNodeXYZ(const SMESH::smIdType id)
{
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  SMESH::double_array_var aResult = new SMESH::double_array();
  SMESHDS_Mesh* aMeshDS = _impl->GetMeshDS();
  if ( aMeshDS == NULL )
    return aResult._retn();

  // find node
  const SMDS_MeshNode* aNode = aMeshDS->FindNode(id);
  if(!aNode)
    return aResult._retn();

  // add coordinates
  aResult->length(3);
  aResult[0] = aNode->X();
  aResult[1] = aNode->Y();
  aResult[2] = aNode->Z();
  return aResult._retn();
}


//=============================================================================
/*!
 * For given node return list of IDs of inverse elements
 * If there is not node for given ID - return empty list
 */
//=============================================================================

SMESH::smIdType_array* SMESH_Mesh_i::GetNodeInverseElements(const SMESH::smIdType  id,
                                                            SMESH::ElementType elemType)
{
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  SMESH::smIdType_array_var aResult = new SMESH::smIdType_array();
  SMESHDS_Mesh* aMeshDS = _impl->GetMeshDS();
  if ( aMeshDS == NULL )
    return aResult._retn();

  // find node
  const SMDS_MeshNode* aNode = aMeshDS->FindNode( id );
  if ( !aNode )
    return aResult._retn();

  // find inverse elements
  SMDSAbs_ElementType type = SMDSAbs_ElementType( elemType );
  SMDS_ElemIteratorPtr eIt = aNode->GetInverseElementIterator( type );
  aResult->length( aNode->NbInverseElements( type ));
  for( smIdType i = 0; eIt->more(); ++i )
  {
    const SMDS_MeshElement* elem = eIt->next();
    aResult[ i ] = elem->GetID();
  }
  return aResult._retn();
}

//=============================================================================
/*!
 * \brief Return position of a node on shape
 */
//=============================================================================

SMESH::NodePosition* SMESH_Mesh_i::GetNodePosition(SMESH::smIdType NodeID)
{
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  SMESH::NodePosition* aNodePosition = new SMESH::NodePosition();
  aNodePosition->shapeID = 0;
  aNodePosition->shapeType = GEOM::SHAPE;

  SMESHDS_Mesh* mesh = _impl->GetMeshDS();
  if ( !mesh ) return aNodePosition;

  if ( const SMDS_MeshNode* aNode = mesh->FindNode(NodeID) )
  {
    if ( SMDS_PositionPtr pos = aNode->GetPosition() )
    {
      aNodePosition->shapeID = aNode->getshapeId();
      switch ( pos->GetTypeOfPosition() ) {
      case SMDS_TOP_EDGE:
        aNodePosition->shapeType = GEOM::EDGE;
        aNodePosition->params.length(1);
        aNodePosition->params[0] = SMDS_EdgePositionPtr( pos )->GetUParameter();
        break;
      case SMDS_TOP_FACE: {
        SMDS_FacePositionPtr fPos = pos;
        aNodePosition->shapeType = GEOM::FACE;
        aNodePosition->params.length(2);
        aNodePosition->params[0] = fPos->GetUParameter();
        aNodePosition->params[1] = fPos->GetVParameter();
        break;
      }
      case SMDS_TOP_VERTEX:
        aNodePosition->shapeType = GEOM::VERTEX;
        break;
      case SMDS_TOP_3DSPACE:
        if ( TopExp_Explorer(_impl->GetShapeToMesh(), TopAbs_SOLID).More() )
          aNodePosition->shapeType = GEOM::SOLID;
        else if ( TopExp_Explorer(_impl->GetShapeToMesh(), TopAbs_SHELL).More() )
          aNodePosition->shapeType = GEOM::SHELL;
        break;
      default:;
      }
    }
  }
  return aNodePosition;
}

//=============================================================================
/*!
 * \brief Return position of an element on shape
 */
//=============================================================================

SMESH::ElementPosition SMESH_Mesh_i::GetElementPosition(SMESH::smIdType ElemID)
{
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  SMESH::ElementPosition anElementPosition;
  anElementPosition.shapeID = 0;
  anElementPosition.shapeType = GEOM::SHAPE;

  SMESHDS_Mesh* mesh = _impl->GetMeshDS();
  if ( !mesh ) return anElementPosition;

  if ( const SMDS_MeshElement* anElem = mesh->FindElement( ElemID ) )
  {
    anElementPosition.shapeID = anElem->getshapeId();
    const TopoDS_Shape& aSp = mesh->IndexToShape( anElem->getshapeId() );
    if ( !aSp.IsNull() ) {
      switch ( aSp.ShapeType() ) {
      case TopAbs_EDGE:
        anElementPosition.shapeType = GEOM::EDGE;
        break;
      case TopAbs_FACE:
        anElementPosition.shapeType = GEOM::FACE;
        break;
      case TopAbs_VERTEX:
        anElementPosition.shapeType = GEOM::VERTEX;
        break;
      case TopAbs_SOLID:
        anElementPosition.shapeType = GEOM::SOLID;
        break;
      case TopAbs_SHELL:
        anElementPosition.shapeType = GEOM::SHELL;
        break;
      default:;
      }
    }
  }
  return anElementPosition;
}

//=============================================================================
/*!
 * If given element is node return IDs of shape from position
 * If there is not node for given ID - return -1
 */
//=============================================================================

CORBA::Long SMESH_Mesh_i::GetShapeID(const SMESH::smIdType id)
{
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  SMESHDS_Mesh* aMeshDS = _impl->GetMeshDS();
  if ( aMeshDS == NULL )
    return -1;

  // try to find node
  const SMDS_MeshNode* aNode = aMeshDS->FindNode(id);
  if(aNode) {
    return aNode->getshapeId();
  }

  return -1;
}


//=============================================================================
/*!
 * For given element return ID of result shape after
 * ::FindShape() from SMESH_MeshEditor
 * If there is not element for given ID - return -1
 */
//=============================================================================

CORBA::Long SMESH_Mesh_i::GetShapeIDForElem(const SMESH::smIdType id)
{
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  SMESHDS_Mesh* aMeshDS = _impl->GetMeshDS();
  if ( aMeshDS == NULL )
    return -1;

  // try to find element
  const SMDS_MeshElement* elem = aMeshDS->FindElement(id);
  if(!elem)
    return -1;

  ::SMESH_MeshEditor aMeshEditor(_impl);
  int index = aMeshEditor.FindShape( elem );
  if(index>0)
    return index;

  return -1;
}


//=============================================================================
/*!
 * Return number of nodes for given element
 * If there is not element for given ID - return -1
 */
//=============================================================================

CORBA::Short SMESH_Mesh_i::GetElemNbNodes(const SMESH::smIdType id)
{
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  SMESHDS_Mesh* aMeshDS = _impl->GetMeshDS();
  if ( aMeshDS == NULL ) return -1;
  // try to find element
  const SMDS_MeshElement* elem = aMeshDS->FindElement(id);
  if(!elem) return -1;
  return elem->NbNodes();
}


//=============================================================================
/*!
 * Return ID of node by given index for given element
 * If there is not element for given ID - return -1
 * If there is not node for given index - return -2
 */
//=============================================================================

SMESH::smIdType SMESH_Mesh_i::GetElemNode(const SMESH::smIdType id, const CORBA::Short index)
{
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  SMESHDS_Mesh* aMeshDS = _impl->GetMeshDS();
  if ( aMeshDS == NULL ) return -1;
  const SMDS_MeshElement* elem = aMeshDS->FindElement(id);
  if(!elem) return -1;
  if( index>=elem->NbNodes() || index<0 ) return -1;
  return elem->GetNode(index)->GetID();
}

//=============================================================================
/*!
 * Return IDs of nodes of given element
 */
//=============================================================================

SMESH::smIdType_array* SMESH_Mesh_i::GetElemNodes(const SMESH::smIdType id)
{
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  SMESH::smIdType_array_var aResult = new SMESH::smIdType_array();
  if ( SMESHDS_Mesh* aMeshDS = _impl->GetMeshDS() )
  {
    if ( const SMDS_MeshElement* elem = aMeshDS->FindElement(id) )
    {
      aResult->length( elem->NbNodes() );
      for ( CORBA::ULong i = 0; i < aResult->length(); ++i )
        if ( const SMDS_MeshNode* n = elem->GetNode( i ))
          aResult[ i ] = n->GetID();
    }
  }
  return aResult._retn();
}

//=============================================================================
/*!
 * Return true if given node is medium node
 * in given quadratic element
 */
//=============================================================================

CORBA::Boolean SMESH_Mesh_i::IsMediumNode(const SMESH::smIdType ide, const SMESH::smIdType idn)
{
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  SMESHDS_Mesh* aMeshDS = _impl->GetMeshDS();
  if ( aMeshDS == NULL ) return false;
  // try to find node
  const SMDS_MeshNode* aNode = aMeshDS->FindNode(idn);
  if(!aNode) return false;
  // try to find element
  const SMDS_MeshElement* elem = aMeshDS->FindElement(ide);
  if(!elem) return false;

  return elem->IsMediumNode(aNode);
}


//=============================================================================
/*!
 * Return true if given node is medium node
 * in one of quadratic elements
 */
//=============================================================================

CORBA::Boolean SMESH_Mesh_i::IsMediumNodeOfAnyElem(const SMESH::smIdType idn,
                                                   SMESH::ElementType theElemType)
{
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  SMESHDS_Mesh* aMeshDS = _impl->GetMeshDS();
  if ( aMeshDS == NULL ) return false;

  // try to find node
  const SMDS_MeshNode* aNode = aMeshDS->FindNode(idn);
  if(!aNode) return false;

  SMESH_MesherHelper aHelper( *(_impl) );

  SMDSAbs_ElementType aType;
  if(theElemType==SMESH::EDGE) aType = SMDSAbs_Edge;
  else if(theElemType==SMESH::FACE) aType = SMDSAbs_Face;
  else if(theElemType==SMESH::VOLUME) aType = SMDSAbs_Volume;
  else aType = SMDSAbs_All;

  return aHelper.IsMedium(aNode,aType);
}


//=============================================================================
/*!
 * Return number of edges for given element
 */
//=============================================================================

CORBA::Long SMESH_Mesh_i::ElemNbEdges(const SMESH::smIdType id)
{
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  SMESHDS_Mesh* aMeshDS = _impl->GetMeshDS();
  if ( aMeshDS == NULL ) return -1;
  const SMDS_MeshElement* elem = aMeshDS->FindElement(id);
  if(!elem) return -1;
  return elem->NbEdges();
}


//=============================================================================
/*!
 * Return number of faces for given element
 */
//=============================================================================

CORBA::Long SMESH_Mesh_i::ElemNbFaces(const SMESH::smIdType id)
{
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  SMESHDS_Mesh* aMeshDS = _impl->GetMeshDS();
  if ( aMeshDS == NULL ) return -1;
  const SMDS_MeshElement* elem = aMeshDS->FindElement(id);
  if(!elem) return -1;
  return elem->NbFaces();
}

//================================================================================
/*!
 * \brief Return nodes of given face (counted from zero) for given element.
 */
//================================================================================

SMESH::smIdType_array* SMESH_Mesh_i::GetElemFaceNodes(SMESH::smIdType  elemId,
                                                      CORBA::Short     faceIndex)
{
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  SMESH::smIdType_array_var aResult = new SMESH::smIdType_array();
  if ( SMESHDS_Mesh* aMeshDS = _impl->GetMeshDS() )
  {
    if ( const SMDS_MeshElement* elem = aMeshDS->FindElement(elemId) )
    {
      SMDS_VolumeTool vtool( elem, /*skipCentralNodes = */false );
      if ( faceIndex < vtool.NbFaces() )
      {
        aResult->length( vtool.NbFaceNodes( faceIndex ));
        const SMDS_MeshNode** nn = vtool.GetFaceNodes( faceIndex );
        for ( CORBA::ULong i = 0; i < aResult->length(); ++i )
          aResult[ i ] = nn[ i ]->GetID();
      }
    }
  }
  return aResult._retn();
}

//================================================================================
/*!
 * \brief Return three components of normal of given mesh face.
 */
//================================================================================

SMESH::double_array* SMESH_Mesh_i::GetFaceNormal(CORBA::Long    elemId,
                                                 CORBA::Boolean normalized)
{
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  SMESH::double_array_var aResult = new SMESH::double_array();

  if ( SMESHDS_Mesh* mesh = _impl->GetMeshDS() )
  {
    gp_XYZ normal;
    if ( SMESH_MeshAlgos::FaceNormal( mesh->FindElement(elemId), normal, normalized ))
    {
      aResult->length( 3 );
      aResult[ 0 ] = normal.X();
      aResult[ 1 ] = normal.Y();
      aResult[ 2 ] = normal.Z();
    }
  }
  return aResult._retn();
}

//================================================================================
/*!
 * \brief Return an element based on all given nodes.
 */
//================================================================================

SMESH::smIdType SMESH_Mesh_i::FindElementByNodes(const SMESH::smIdType_array& nodes)
{
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  CORBA::Long elemID(0);
  if ( SMESHDS_Mesh* mesh = _impl->GetMeshDS() )
  {
    vector< const SMDS_MeshNode * > nn( nodes.length() );
    for ( CORBA::ULong i = 0; i < nodes.length(); ++i )
      if ( !( nn[i] = mesh->FindNode( nodes[i] )))
        return elemID;

    const SMDS_MeshElement* elem = mesh->FindElement( nn, SMDSAbs_All, /*noMedium=*/false );
    if ( !elem && ( _impl->NbEdges  ( ORDER_QUADRATIC ) ||
                    _impl->NbFaces  ( ORDER_QUADRATIC ) ||
                    _impl->NbVolumes( ORDER_QUADRATIC )))
      elem = mesh->FindElement( nn, SMDSAbs_All, /*noMedium=*/true );

    if ( elem ) elemID = CORBA::Long( elem->GetID() );
  }
  return elemID;
}

//================================================================================
/*!
 * \brief Return elements including all given nodes.
 */
//================================================================================

SMESH::smIdType_array* SMESH_Mesh_i::GetElementsByNodes(const SMESH::smIdType_array& nodes,
                                                        SMESH::ElementType       elemType)
{
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  SMESH::smIdType_array_var result = new SMESH::smIdType_array();

  if ( SMESHDS_Mesh* mesh = _impl->GetMeshDS() )
  {
    vector< const SMDS_MeshNode * > nn( nodes.length() );
    for ( CORBA::ULong i = 0; i < nodes.length(); ++i )
      nn[i] = mesh->FindNode( nodes[i] );

    std::vector<const SMDS_MeshElement *> elems;
    mesh->GetElementsByNodes( nn, elems, (SMDSAbs_ElementType) elemType );
    result->length( elems.size() );
    for ( size_t i = 0; i < elems.size(); ++i )
      result[i] = elems[i]->GetID();
  }
  return result._retn();
}

//=============================================================================
/*!
 * Return true if given element is polygon
 */
//=============================================================================

CORBA::Boolean SMESH_Mesh_i::IsPoly(const SMESH::smIdType id)
{
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  SMESHDS_Mesh* aMeshDS = _impl->GetMeshDS();
  if ( aMeshDS == NULL ) return false;
  const SMDS_MeshElement* elem = aMeshDS->FindElement(id);
  if(!elem) return false;
  return elem->IsPoly();
}


//=============================================================================
/*!
 * Return true if given element is quadratic
 */
//=============================================================================

CORBA::Boolean SMESH_Mesh_i::IsQuadratic(const SMESH::smIdType id)
{
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  SMESHDS_Mesh* aMeshDS = _impl->GetMeshDS();
  if ( aMeshDS == NULL ) return false;
  const SMDS_MeshElement* elem = aMeshDS->FindElement(id);
  if(!elem) return false;
  return elem->IsQuadratic();
}

//=============================================================================
/*!
 * Return diameter of ball discrete element or zero in case of an invalid \a id
 */
//=============================================================================

CORBA::Double SMESH_Mesh_i::GetBallDiameter(SMESH::smIdType id)
{
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  if ( const SMDS_BallElement* ball =
       SMDS_Mesh::DownCast<SMDS_BallElement>( _impl->GetMeshDS()->FindElement( id )))
    return ball->GetDiameter();

  return 0;
}

//=============================================================================
/*!
 * Return bary center for given element
 */
//=============================================================================

SMESH::double_array* SMESH_Mesh_i::BaryCenter(const SMESH::smIdType id)
{
  if ( _preMeshInfo )
    _preMeshInfo->FullLoadFromFile();

  SMESH::double_array_var aResult = new SMESH::double_array();
  SMESHDS_Mesh* aMeshDS = _impl->GetMeshDS();
  if ( aMeshDS == NULL )
    return aResult._retn();

  const SMDS_MeshElement* elem = aMeshDS->FindElement(id);
  if(!elem)
    return aResult._retn();

  if(elem->GetType()==SMDSAbs_Volume) {
    SMDS_VolumeTool aTool;
    if(aTool.Set(elem)) {
      aResult->length(3);
      if (!aTool.GetBaryCenter( aResult[0], aResult[1], aResult[2]) )
        aResult->length(0);
    }
  }
  else {
    SMDS_ElemIteratorPtr anIt = elem->nodesIterator();
    int nbn = 0;
    double x=0., y=0., z=0.;
    for(; anIt->more(); ) {
      nbn++;
      const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>(anIt->next());
      x += aNode->X();
      y += aNode->Y();
      z += aNode->Z();
    }
    if(nbn>0) {
      // add coordinates
      aResult->length(3);
      aResult[0] = x/nbn;
      aResult[1] = y/nbn;
      aResult[2] = z/nbn;
    }
  }

  return aResult._retn();
}

//================================================================================
/*!
 * \brief Create a group of elements preventing computation of a sub-shape
 */
//================================================================================

SMESH::ListOfGroups*
SMESH_Mesh_i::MakeGroupsOfBadInputElements( int         theSubShapeID,
                                            const char* theGroupName )
{
  Unexpect aCatch(SALOME_SalomeException);

  if ( !theGroupName || strlen( theGroupName) == 0 )
    THROW_SALOME_CORBA_EXCEPTION( "empty group name",SALOME::BAD_PARAM );

  SMESH::ListOfGroups_var groups = new SMESH::ListOfGroups;
  ::SMESH_MeshEditor::ElemFeatures elemType;

  // submesh by subshape id
  if ( !_impl->HasShapeToMesh() ) theSubShapeID = 1;
  if ( SMESH_subMesh * sm = _impl->GetSubMeshContaining( theSubShapeID ))
  {
    // compute error
    SMESH_ComputeErrorPtr error = sm->GetComputeError();
    if ( error && error->HasBadElems() )
    {
      // sort bad elements by type
      vector< const SMDS_MeshElement* > elemsByType[ SMDSAbs_NbElementTypes ];
      const list<const SMDS_MeshElement*>& badElems =
        static_cast<SMESH_BadInputElements*>( error.get() )->myBadElements;
      list<const SMDS_MeshElement*>::const_iterator elemIt  = badElems.begin();
      list<const SMDS_MeshElement*>::const_iterator elemEnd = badElems.end();
      for ( ; elemIt != elemEnd; ++elemIt )
      {
        const SMDS_MeshElement* elem = *elemIt;
        if ( !elem ) continue;

        if ( elem->GetID() < 1 )
        {
          // elem is a temporary element, make a real element
          vector< const SMDS_MeshNode* > nodes;
          SMDS_NodeIteratorPtr nIt = elem->nodeIterator();
          while ( nIt->more() && elem )
          {
            nodes.push_back( nIt->next() );
            if ( nodes.back()->GetID() < 1 )
              elem = 0;  // a temporary element on temporary nodes
          }
          if ( elem )
          {
            ::SMESH_MeshEditor editor( _impl );
            elem = editor.AddElement( nodes, elemType.Init( elem ));
          }
        }
        if ( elem )
          elemsByType[ elem->GetType() ].push_back( elem );
      }

      // how many groups to create?
      int nbTypes = 0;
      for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
        nbTypes += int( !elemsByType[ i ].empty() );
      groups->length( nbTypes );

      // create groups
      for ( int i = 0, iG = -1; i < SMDSAbs_NbElementTypes; ++i )
      {
        vector< const SMDS_MeshElement* >& elems = elemsByType[ i ];
        if ( elems.empty() ) continue;

        groups[ ++iG ] = createGroup( SMESH::ElementType(i), theGroupName );
        if ( _gen_i->CanPublishInStudy( groups[ iG ] ))
        {
          SMESH::SMESH_Mesh_var mesh = _this();
          SALOMEDS::SObject_wrap aSO =
            _gen_i->PublishGroup( mesh, groups[ iG ],
                                 GEOM::GEOM_Object::_nil(), theGroupName);
        }
        SMESH_GroupBase_i* grp_i = SMESH::DownCast< SMESH_GroupBase_i* >( groups[ iG ]);
        if ( !grp_i ) continue;

        if ( SMESHDS_Group*  grpDS = dynamic_cast< SMESHDS_Group* >( grp_i->GetGroupDS() ))
          for ( size_t iE = 0; iE < elems.size(); ++iE )
            grpDS->SMDSGroup().Add( elems[ iE ]);
      }
    }
  }

  return groups._retn();
}

//=============================================================================
/*!
 * Create and publish group servants if any groups were imported or created anyhow
 */
//=============================================================================

void SMESH_Mesh_i::CreateGroupServants()
{
  SMESH::SMESH_Mesh_var aMesh = _this();

  set<int> addedIDs;
  ::SMESH_Mesh::GroupIteratorPtr groupIt = _impl->GetGroups();
  while ( groupIt->more() )
  {
    ::SMESH_Group* group = groupIt->next();
    int             anId = group->GetID();

    map<int, SMESH::SMESH_GroupBase_ptr>::iterator it = _mapGroups.find(anId);
    if ( it != _mapGroups.end() && !CORBA::is_nil( it->second ))
      continue;
    addedIDs.insert( anId );

    SMESH_GroupBase_i* aGroupImpl;
    TopoDS_Shape       shape;
    if ( SMESHDS_GroupOnGeom* groupOnGeom =
         dynamic_cast<SMESHDS_GroupOnGeom*>( group->GetGroupDS() ))
    {
      aGroupImpl = new SMESH_GroupOnGeom_i( SMESH_Gen_i::GetPOA(), this, anId );
      shape      = groupOnGeom->GetShape();
    }
    else {
      aGroupImpl = new SMESH_Group_i( SMESH_Gen_i::GetPOA(), this, anId );
    }

    SMESH::SMESH_GroupBase_var groupVar = aGroupImpl->_this();
    _mapGroups[anId] = SMESH::SMESH_GroupBase::_duplicate( groupVar );
    aGroupImpl->Register();

    // register CORBA object for persistence
    int nextId = _gen_i->RegisterObject( groupVar );
    if(MYDEBUG) { MESSAGE( "Add group to map with id = "<< nextId); }
    else        { (void)nextId; } // avoid "unused variable" warning in release mode

    // publishing the groups in the study
    GEOM::GEOM_Object_var shapeVar = _gen_i->ShapeToGeomObject( shape );
    _gen_i->PublishGroup( aMesh, groupVar, shapeVar, group->GetName());
  }

  if ( !addedIDs.empty() )
  {
    // python dump
    map<int, SMESH::SMESH_GroupBase_ptr>::iterator i_grp = _mapGroups.begin();
    for ( int index = 0; i_grp != _mapGroups.end(); ++index, ++i_grp )
    {
      set<int>::iterator it = addedIDs.find( i_grp->first );
      if ( it != addedIDs.end() )
      {
        TPythonDump() << i_grp->second << " = " << aMesh << ".GetGroups()[ "<< index << " ]";
        addedIDs.erase( it );
        if ( addedIDs.empty() )
          break;
      }
    }
  }
}

//=============================================================================
/*!
 * \brief Return true if all sub-meshes are computed OK - to update an icon
 */
//=============================================================================

bool SMESH_Mesh_i::IsComputedOK()
{
  return _impl->IsComputedOK();
}

//=============================================================================
/*!
 * \brief Return groups cantained in _mapGroups by their IDs
 */
//=============================================================================

SMESH::ListOfGroups* SMESH_Mesh_i::GetGroups(const list<int>& groupIDs) const
{
  int nbGroups = groupIDs.size();
  SMESH::ListOfGroups_var aList = new SMESH::ListOfGroups();
  aList->length( nbGroups );

  list<int>::const_iterator ids = groupIDs.begin();
  for ( nbGroups = 0; ids != groupIDs.end(); ++ids )
  {
    map<int, SMESH::SMESH_GroupBase_ptr>::const_iterator it = _mapGroups.find( *ids );
    if ( it != _mapGroups.end() && !CORBA::is_nil( it->second ))
      aList[nbGroups++] = SMESH::SMESH_GroupBase::_duplicate( it->second );
  }
  aList->length( nbGroups );
  return aList._retn();
}

//=============================================================================
/*!
 * \brief Return information about imported file
 */
//=============================================================================

SMESH::MedFileInfo* SMESH_Mesh_i::GetMEDFileInfo()
{
  SMESH::MedFileInfo_var res( _medFileInfo );
  if ( !res.operator->() ) {
    res = new SMESH::MedFileInfo;
    res->fileName = "";
    res->fileSize = res->major = res->minor = res->release = -1;
  }
  return res._retn();
}

//=======================================================================
//function : FileInfoToString
//purpose  : Persistence of file info
//=======================================================================

std::string SMESH_Mesh_i::FileInfoToString()
{
  std::string s;
  if ( &_medFileInfo.in() && _medFileInfo->fileName[0] )
  {
    s = SMESH_Comment( _medFileInfo->fileSize )
      << " " << _medFileInfo->major
      << " " << _medFileInfo->minor
      << " " << _medFileInfo->release
      << " " << _medFileInfo->fileName;
  }
  return s;
}

//=======================================================================
//function : FileInfoFromString
//purpose  : Persistence of file info
//=======================================================================

void SMESH_Mesh_i::FileInfoFromString(const std::string& info)
{
  std::string size, major, minor, release, fileName;
  std::istringstream is(info);
  is >> size >> major >> minor >> release;
  fileName = info.data() + ( size.size()   + 1 +
                             major.size()  + 1 +
                             minor.size()  + 1 +
                             release.size()+ 1 );

  _medFileInfo           = new SMESH::MedFileInfo();
  _medFileInfo->fileName = fileName.c_str();
  _medFileInfo->fileSize = atoi( size.c_str() );
  _medFileInfo->major    = atoi( major.c_str() );
  _medFileInfo->minor    = atoi( minor.c_str() );
  _medFileInfo->release  = atoi( release.c_str() );
}

//=============================================================================
/*!
 * \brief Pass names of mesh groups from study to mesh DS
 */
//=============================================================================

void SMESH_Mesh_i::checkGroupNames()
{
  int nbGrp = NbGroups();
  if ( !nbGrp )
    return;

  SMESH::ListOfGroups* grpList = 0;
  // avoid dump of "GetGroups"
  {
    // store python dump into a local variable inside local scope
    SMESH::TPythonDump pDump; // do not delete this line of code
    grpList = GetGroups();
  }

  for ( int gIndx = 0; gIndx < nbGrp; gIndx++ ) {
    SMESH::SMESH_GroupBase_ptr aGrp = (*grpList)[ gIndx ];
    if ( !aGrp )
      continue;
    SALOMEDS::SObject_wrap aGrpSO = _gen_i->ObjectToSObject( aGrp );
    if ( aGrpSO->_is_nil() )
      continue;
    // correct name of the mesh group if necessary
    const char* guiName = aGrpSO->GetName();
    if ( strcmp(guiName, aGrp->GetName()) )
      aGrp->SetName( guiName );
  }
}

//=============================================================================
/*!
 * \brief Set list of notebook variables used for Mesh operations separated by ":" symbol
 */
//=============================================================================
void SMESH_Mesh_i::SetParameters(const char* theParameters)
{
  SMESH_Gen_i::GetSMESHGen()->UpdateParameters( CORBA::Object_var( _this() ).in(),
                                                theParameters );
}

//=============================================================================
/*!
 * \brief Return list of notebook variables used for Mesh operations separated by ":" symbol
 */
//=============================================================================

char* SMESH_Mesh_i::GetParameters()
{
  return SMESH_Gen_i::GetSMESHGen()->GetParameters( SMESH::SMESH_Mesh_var( _this()) );
}

//=============================================================================
/*!
 * \brief Return list of notebook variables used for last Mesh operation
 */
//=============================================================================
SMESH::string_array* SMESH_Mesh_i::GetLastParameters()
{
  SMESH::string_array_var aResult = new SMESH::string_array();
  SMESH_Gen_i *gen = SMESH_Gen_i::GetSMESHGen();
  if(gen) {
    CORBA::String_var aParameters = GetParameters();
    SALOMEDS::ListOfListOfStrings_var aSections = SMESH_Gen_i::GetSMESHGen()->getStudyServant()->ParseVariables(aParameters);
    if ( aSections->length() > 0 ) {
      SALOMEDS::ListOfStrings aVars = aSections[ aSections->length() - 1 ];
      aResult->length( aVars.length() );
      for ( CORBA::ULong i = 0;i < aVars.length(); i++ )
        aResult[i] = CORBA::string_dup( aVars[i] );
    }
  }
  return aResult._retn();
}

//================================================================================
/*!
 * \brief Return types of elements it contains
 */
//================================================================================

SMESH::array_of_ElementType* SMESH_Mesh_i::GetTypes()
{
  if ( _preMeshInfo )
    return _preMeshInfo->GetTypes();

  SMESH::array_of_ElementType_var types = new SMESH::array_of_ElementType;

  types->length( 5 );
  int nbTypes = 0;
  if (_impl->NbEdges())      types[nbTypes++] = SMESH::EDGE;
  if (_impl->NbFaces())      types[nbTypes++] = SMESH::FACE;
  if (_impl->NbVolumes())    types[nbTypes++] = SMESH::VOLUME;
  if (_impl->Nb0DElements()) types[nbTypes++] = SMESH::ELEM0D;
  if (_impl->NbBalls())      types[nbTypes++] = SMESH::BALL;
  if (_impl->NbNodes() &&
      nbTypes == 0 )         types[nbTypes++] = SMESH::NODE;
  types->length( nbTypes );

  return types._retn();
}

//================================================================================
/*!
 * \brief Return self
 */
//================================================================================

SMESH::SMESH_Mesh_ptr SMESH_Mesh_i::GetMesh()
{
  return SMESH::SMESH_Mesh::_duplicate( _this() );
}

//================================================================================
/*!
 * \brief Return false if GetMeshInfo() return incorrect information that may
 *        happen if mesh data is not yet fully loaded from the file of study.
 *
 *
 */
//================================================================================

bool SMESH_Mesh_i::IsMeshInfoCorrect()
{
  return _preMeshInfo ? _preMeshInfo->IsMeshInfoCorrect() : true;
}

//=============================================================================
/*!
 * \brief Return number of mesh elements per each \a EntityType
 */
//=============================================================================

SMESH::smIdType_array* SMESH_Mesh_i::GetMeshInfo()
{
  if ( _preMeshInfo )
    return _preMeshInfo->GetMeshInfo();

  SMESH::smIdType_array_var aRes = new SMESH::smIdType_array();
  aRes->length(SMESH::Entity_Last);
  for (int i = SMESH::Entity_Node; i < SMESH::Entity_Last; i++)
    aRes[i] = 0;
  SMESHDS_Mesh* aMeshDS = _impl->GetMeshDS();
  if (!aMeshDS)
    return aRes._retn();
  const SMDS_MeshInfo& aMeshInfo = aMeshDS->GetMeshInfo();
  for (int i = SMESH::Entity_Node; i < SMESH::Entity_Last; i++)
    aRes[i] = aMeshInfo.NbEntities((SMDSAbs_EntityType)i);
  return aRes._retn();
}

//=============================================================================
/*!
 * \brief Return number of mesh elements per each \a ElementType
 */
//=============================================================================

SMESH::smIdType_array* SMESH_Mesh_i::GetNbElementsByType()
{
  SMESH::smIdType_array_var aRes = new SMESH::smIdType_array();
  aRes->length(SMESH::NB_ELEMENT_TYPES);
  for (smIdType i = 0; i < SMESH::NB_ELEMENT_TYPES; i++)
    aRes[ i ] = 0;

  const SMDS_MeshInfo* meshInfo = 0;
  if ( _preMeshInfo )
    meshInfo = _preMeshInfo;
  else if ( SMESHDS_Mesh* meshDS = _impl->GetMeshDS() )
    meshInfo = & meshDS->GetMeshInfo();

  if (meshInfo)
    for (smIdType i = 0; i < SMESH::NB_ELEMENT_TYPES; i++)
      aRes[i] = meshInfo->NbElements((SMDSAbs_ElementType)i);

  return aRes._retn();
}

//=============================================================================
/*
 * Collect statistic of mesh elements given by iterator
 */
//=============================================================================

void SMESH_Mesh_i::CollectMeshInfo(const SMDS_ElemIteratorPtr theItr,
                                   SMESH::smIdType_array&     theInfo)
{
  if (!theItr) return;
  while (theItr->more())
    theInfo[ theItr->next()->GetEntityType() ]++;
}
//=============================================================================
/*
 * Return mesh unstructed grid information.
 */
//=============================================================================

SALOMEDS::TMPFile* SMESH_Mesh_i::GetVtkUgStream()
{
  SALOMEDS::TMPFile_var SeqFile;
  if ( SMESHDS_Mesh* aMeshDS = _impl->GetMeshDS() ) {
    SMDS_UnstructuredGrid* aGrid = aMeshDS->GetGrid();
    if(aGrid) {
      vtkUnstructuredGridWriter* aWriter = vtkUnstructuredGridWriter::New();
      aWriter->WriteToOutputStringOn();
      aWriter->SetInputData(aGrid);
      aWriter->SetFileTypeToBinary();
      aWriter->Write();
      char* str = aWriter->GetOutputString();
      int size = aWriter->GetOutputStringLength();

      //Allocate octet buffer of required size
      CORBA::Octet* OctetBuf = SALOMEDS::TMPFile::allocbuf(size);
      //Copy ostrstream content to the octet buffer
      memcpy(OctetBuf, str, size);
      //Create and return TMPFile
      SeqFile = new SALOMEDS::TMPFile(size, size, OctetBuf, 1);
      aWriter->Delete();
    }
  }
  return SeqFile._retn();
}

//=============================================================================
namespace /* Iterators used in SMESH_Mesh_i::GetElements(SMESH::SMESH_IDSource_var obj,
           *                                             SMESH::ElementType        type) */
{
  using namespace SMESH::Controls;
  //-----------------------------------------------------------------------------
  struct PredicateIterator : public SMDS_ElemIterator
  {
    SMDS_ElemIteratorPtr    _elemIter;
    PredicatePtr            _predicate;
    const SMDS_MeshElement* _elem;
    SMDSAbs_ElementType     _type;

    PredicateIterator( SMDS_ElemIteratorPtr iterator,
                       PredicatePtr         predicate,
                       SMDSAbs_ElementType  type):
      _elemIter(iterator), _predicate(predicate), _type(type)
    {
      next();
    }
    virtual bool more()
    {
      return _elem;
    }
    virtual const SMDS_MeshElement* next()
    {
      const SMDS_MeshElement* res = _elem;
      _elem = 0;
      while ( _elemIter->more() && !_elem )
      {
        if ((_elem = _elemIter->next()) &&
            (( _type != SMDSAbs_All && _type != _elem->GetType() ) ||
             ( !_predicate->IsSatisfy( _elem->GetID() ))))
          _elem = 0;
      }
      return res;
    }
  };

  //-----------------------------------------------------------------------------
  struct IDSourceIterator : public SMDS_ElemIterator
  {
    const SMESH::smIdType*        _idPtr;
    const SMESH::smIdType*        _idEndPtr;
    SMESH::smIdType_array_var     _idArray;
    const SMDS_Mesh*              _mesh;
    const SMDSAbs_ElementType     _type;
    const SMDS_MeshElement*       _elem;

    IDSourceIterator( const SMDS_Mesh*       mesh,
                      const SMESH::smIdType* ids,
                      const smIdType         nbIds,
                      SMDSAbs_ElementType    type):
      _idPtr( ids ), _idEndPtr( ids + nbIds ), _mesh( mesh ), _type( type ), _elem( 0 )
    {
      if ( _idPtr && nbIds && _mesh )
        next();
    }
    IDSourceIterator( const SMDS_Mesh*       mesh,
                      SMESH::smIdType_array* idArray,
                      SMDSAbs_ElementType    type):
      _idPtr( 0 ), _idEndPtr( 0 ), _idArray( idArray), _mesh( mesh ), _type( type ), _elem( 0 )
    {
      if ( idArray && _mesh )
      {
        _idPtr    = &_idArray[0];
        _idEndPtr = _idPtr + _idArray->length();
        next();
      }
    }
    virtual bool more()
    {
      return _elem;
    }
    virtual const SMDS_MeshElement* next()
    {
      const SMDS_MeshElement* res = _elem;
      _elem = 0;
      while ( _idPtr < _idEndPtr && !_elem )
      {
        if ( _type == SMDSAbs_Node )
        {
          _elem = _mesh->FindNode( *_idPtr++ );
        }
        else if ((_elem = _mesh->FindElement( *_idPtr++ )) &&
                 (_elem->GetType() != _type && _type != SMDSAbs_All ))
        {
          _elem = 0;
        }
      }
      return res;
    }
  };
  //-----------------------------------------------------------------------------

  struct NodeOfElemIterator : public SMDS_ElemIterator
  {
    TColStd_MapOfInteger    _checkedNodeIDs;
    SMDS_ElemIteratorPtr    _elemIter;
    SMDS_ElemIteratorPtr    _nodeIter;
    const SMDS_MeshElement* _node;

    NodeOfElemIterator( SMDS_ElemIteratorPtr iter ): _elemIter( iter ), _node( 0 )
    {
      if ( _elemIter && _elemIter->more() )
      {
        _nodeIter = _elemIter->next()->nodesIterator();
        next();
      }
    }
    virtual bool more()
    {
      return _node;
    }
    virtual const SMDS_MeshElement* next()
    {
      const SMDS_MeshElement* res = _node;
      _node = 0;
      while ( !_node && ( _elemIter->more() || _nodeIter->more() ))
      {
        if ( _nodeIter->more() )
        {
          _node = _nodeIter->next();
          if ( !_checkedNodeIDs.Add( _node->GetID() ))
            _node = 0;
        }
        else
        {
          _nodeIter = _elemIter->next()->nodesIterator();
        }
      }
      return res;
    }
  };
}

//=============================================================================
/*
 * Return iterator on elements of given type in given object
 */
//=============================================================================

SMDS_ElemIteratorPtr SMESH_Mesh_i::GetElements(SMESH::SMESH_IDSource_ptr theObject,
                                               SMESH::ElementType        theType)
{
  SMDS_ElemIteratorPtr  elemIt;
  bool                  typeOK = ( theType == SMESH::ALL );
  SMDSAbs_ElementType elemType = SMDSAbs_ElementType( theType );

  SMESH::SMESH_Mesh_var meshVar = theObject->GetMesh();
  SMESH_Mesh_i*          mesh_i = SMESH::DownCast<SMESH_Mesh_i*>( meshVar );
  if ( !mesh_i ) return elemIt;
  SMESHDS_Mesh*          meshDS = mesh_i->GetImpl().GetMeshDS();

  if ( SMESH::DownCast<SMESH_Mesh_i*>( theObject ))
  {
    elemIt = meshDS->elementsIterator( elemType );
    typeOK = true;
  }
  else if ( SMESH_subMesh_i* submesh_i = SMESH::DownCast<SMESH_subMesh_i*>( theObject ))
  {
    SMESHDS_SubMesh* sm = ((SMESHDS_Mesh*) meshDS)->MeshElements( submesh_i->GetId() );
    if ( sm )
    {
      elemIt = sm->GetElements();
      if ( elemType != SMDSAbs_Node && elemType != SMDSAbs_All )
      {
        typeOK = ( elemIt && elemIt->more() && elemIt->next()->GetType() == elemType );
        elemIt = typeOK ? sm->GetElements() : SMDS_ElemIteratorPtr();
      }
    }
  }
  else if ( SMESH_GroupBase_i* group_i = SMESH::DownCast<SMESH_GroupBase_i*>( theObject ))
  {
    SMESHDS_GroupBase* groupDS = group_i->GetGroupDS();
    if ( groupDS && ( elemType == groupDS->GetType()  ||
                      elemType == SMDSAbs_Node ||
                      elemType == SMDSAbs_All ))
    {
      elemIt = groupDS->GetElements();
      typeOK = ( groupDS->GetType() == elemType || elemType == SMDSAbs_All );
    }
  }
  else if ( SMESH::Filter_i* filter_i = SMESH::DownCast<SMESH::Filter_i*>( theObject ))
  {
    if ( filter_i->GetElementType() == theType ||
         filter_i->GetElementType() == SMESH::ALL ||
         elemType == SMDSAbs_Node ||
         elemType == SMDSAbs_All)
    {
      SMESH::Predicate_i* pred_i = filter_i->GetPredicate_i();
      if ( pred_i && pred_i->GetPredicate() )
      {
        SMDSAbs_ElementType filterType = SMDSAbs_ElementType( filter_i->GetElementType() );
        SMDS_ElemIteratorPtr allElemIt = meshDS->elementsIterator( filterType );
        SMDSAbs_ElementType   iterType = elemType == SMDSAbs_Node ? filterType : elemType;
        elemIt = SMDS_ElemIteratorPtr
          ( new PredicateIterator( allElemIt, pred_i->GetPredicate(), iterType ));
        typeOK = ( elemType == SMDSAbs_Node ? filterType == SMDSAbs_Node : elemIt->more() );
      }
    }
  }
  else
  {
    SMESH::array_of_ElementType_var types = theObject->GetTypes();
    const bool                    isNodes = ( types->length() == 1 && types[0] == SMESH::NODE );
    if ( isNodes && elemType != SMDSAbs_Node && elemType != SMDSAbs_All )
      return elemIt;
    SMDSAbs_ElementType iterType = isNodes ? SMDSAbs_Node : elemType;
    if ( SMESH_MeshEditor_i::IsTemporaryIDSource( theObject ))
    {
      SMESH::smIdType nbIds;
      if ( SMESH::smIdType* ids = SMESH_MeshEditor_i::GetTemporaryIDs( theObject, nbIds ))
        elemIt = SMDS_ElemIteratorPtr( new IDSourceIterator( meshDS, ids, nbIds, iterType ));
    }
    else
    {
      SMESH::smIdType_array_var ids = theObject->GetIDs();
      elemIt = SMDS_ElemIteratorPtr( new IDSourceIterator( meshDS, ids._retn(), iterType ));
    }
    typeOK = ( isNodes == ( elemType == SMDSAbs_Node )) || ( elemType == SMDSAbs_All );
  }

  if ( elemIt && elemIt->more() && !typeOK )
  {
    if ( elemType == SMDSAbs_Node )
    {
      elemIt = SMDS_ElemIteratorPtr( new NodeOfElemIterator( elemIt ));
    }
    else
    {
      elemIt = SMDS_ElemIteratorPtr();
    }
  }
  return elemIt;
}

//=============================================================================
namespace // Finding concurrent hypotheses
//=============================================================================
{

/*!
 * \brief mapping of mesh dimension into shape type
 */
TopAbs_ShapeEnum shapeTypeByDim(const int theDim)
{
  TopAbs_ShapeEnum aType = TopAbs_SOLID;
  switch ( theDim ) {
  case 0: aType = TopAbs_VERTEX; break;
  case 1: aType = TopAbs_EDGE; break;
  case 2: aType = TopAbs_FACE; break;
  case 3:
  default:aType = TopAbs_SOLID; break;
  }
  return aType;
}

//-----------------------------------------------------------------------------
/*!
 * \brief Internal structure used to find concurrent submeshes
 *
 * It represents a pair < submesh, concurrent dimension >, where
 * 'concurrent dimension' is dimension of shape where the submesh can concurrent
 *  with another submesh. In other words, it is dimension of a hypothesis assigned
 *  to submesh.
 */
class SMESH_DimHyp
{
 public:
  //! fields
  int _dim;    //!< a dimension the algo can build (concurrent dimension)
  int _ownDim; //!< dimension of shape of _subMesh (>=_dim)
  TopTools_MapOfShape  _shapeMap; //!< [sub-]shapes of dimension == _dim
  const SMESH_subMesh* _subMesh;
  list<const SMESHDS_Hypothesis*> _hypotheses; //!< algo is first, then its parameters

  //-----------------------------------------------------------------------------
  // Return the algorithm
  const SMESH_Algo* GetAlgo() const
  { return _hypotheses.empty() ? 0 : dynamic_cast<const SMESH_Algo*>( _hypotheses.front() ); }

  //-----------------------------------------------------------------------------
  //! Constructors
  SMESH_DimHyp(const SMESH_subMesh* theSubMesh,
               const int            theDim,
               const TopoDS_Shape&  theShape)
  {
    _subMesh = theSubMesh;
    SetShape( theDim, theShape );
  }

  //-----------------------------------------------------------------------------
  //! set shape
  void SetShape(const int           theDim,
                const TopoDS_Shape& theShape)
  {
    _dim = theDim;
    _ownDim = SMESH_Gen::GetShapeDim(theShape);
    if (_dim >= _ownDim)
      _shapeMap.Add( theShape );
    else {
      TopExp_Explorer anExp( theShape, shapeTypeByDim(theDim) );
      for( ; anExp.More(); anExp.Next() )
        _shapeMap.Add( anExp.Current() );
    }
  }

  //-----------------------------------------------------------------------------
  //! Check sharing of sub-shapes
  static bool isShareSubShapes(const TopTools_MapOfShape& theToCheck,
                               const TopTools_MapOfShape& theToFind,
                               const TopAbs_ShapeEnum     theType)
  {
    bool isShared = false;
    TopTools_MapIteratorOfMapOfShape anItr( theToCheck );
    for (; !isShared && anItr.More(); anItr.Next() )
    {
      const TopoDS_Shape& aSubSh = anItr.Key();
      // check for case when concurrent dimensions are same
      isShared = theToFind.Contains( aSubSh );
      // check for sub-shape with concurrent dimension
      TopExp_Explorer anExp( aSubSh, theType );
      for ( ; !isShared && anExp.More(); anExp.Next() )
        isShared = theToFind.Contains( anExp.Current() );
    }
    return isShared;
  }

  //-----------------------------------------------------------------------------
  //! check algorithms
  static bool checkAlgo(const SMESHDS_Hypothesis* theA1,
                        const SMESHDS_Hypothesis* theA2)
  {
    if ( !theA1 || !theA2 ||
         theA1->GetType() == SMESHDS_Hypothesis::PARAM_ALGO ||
         theA2->GetType() == SMESHDS_Hypothesis::PARAM_ALGO )
      return false; // one of the hypothesis is not algorithm
    // check algorithm names (should be equal)
    return strcmp( theA1->GetName(), theA2->GetName() ) == 0;
  }


  //-----------------------------------------------------------------------------
  //! Check if sub-shape hypotheses are concurrent
  bool IsConcurrent(const SMESH_DimHyp* theOther) const
  {
    if ( _subMesh == theOther->_subMesh )
      return false; // same sub-shape - should not be

    // if ( <own dim of either of submeshes> == <concurrent dim> &&
    //      any of the two submeshes is not on COMPOUND shape )
    //  -> no concurrency
    bool meIsCompound    = (_subMesh->GetSubMeshDS() &&
                            _subMesh->GetSubMeshDS()->IsComplexSubmesh());
    bool otherIsCompound = (theOther->_subMesh->GetSubMeshDS() &&
                            theOther->_subMesh->GetSubMeshDS()->IsComplexSubmesh());
    if ( (_ownDim == _dim  || theOther->_ownDim == _dim ) && (!meIsCompound || !otherIsCompound))
      return false;

    bool checkSubShape = isShareSubShapes( _shapeMap, theOther->_shapeMap, shapeTypeByDim(_dim));
    if ( !checkSubShape )
      return false;

    // check algorithms to be same
    const SMESH_Algo* a1 = this->GetAlgo();
    const SMESH_Algo* a2 = theOther->GetAlgo();
    bool isSame = checkAlgo( a1, a2 );
    if ( !isSame )
    {
      return true;
      // commented off for IPAL54678
      // if ( !a1 || !a2 )
      //   return false; // pb?
      // return a1->GetDim() == a2->GetDim(); // different algorithms of same dim -> concurrency !
    }

    // check hypothesises for concurrence (skip first as algorithm)
    size_t nbSame = 0;
    // pointers should be same, because it is referened from mesh hypothesis partition
    list <const SMESHDS_Hypothesis*>::const_iterator hypIt = _hypotheses.begin();
    list <const SMESHDS_Hypothesis*>::const_iterator otheEndIt = theOther->_hypotheses.end();
    for ( hypIt++ /*skip first as algo*/; hypIt != _hypotheses.end(); hypIt++ )
      if ( find( theOther->_hypotheses.begin(), otheEndIt, *hypIt ) != otheEndIt )
        nbSame++;
    // the submeshes are concurrent if their algorithms has different parameters
    return nbSame != theOther->_hypotheses.size() - 1;
  }

  // Return true if algorithm of this SMESH_DimHyp is used if no
  // sub-mesh order is imposed by the user
  bool IsHigherPriorityThan( const SMESH_DimHyp* theOther ) const
  {
    // NeedDiscreteBoundary() algo has a higher priority
    if ( this    ->GetAlgo()->NeedDiscreteBoundary() !=
         theOther->GetAlgo()->NeedDiscreteBoundary() )
      return !this->GetAlgo()->NeedDiscreteBoundary();

    return ( this->_subMesh->GetId() < theOther->_subMesh->GetId() );
  }

}; // end of SMESH_DimHyp
//-----------------------------------------------------------------------------

typedef list<const SMESH_DimHyp*> TDimHypList;

//-----------------------------------------------------------------------------

void addDimHypInstance(const int                               theDim,
                       const TopoDS_Shape&                     theShape,
                       const SMESH_Algo*                       theAlgo,
                       const SMESH_subMesh*                    theSubMesh,
                       const list <const SMESHDS_Hypothesis*>& theHypList,
                       TDimHypList*                            theDimHypListArr )
{
  if ( !theAlgo->NeedDiscreteBoundary() &&
       theAlgo->NeedLowerHyps( theDim )) // IPAL54678
    return;
  TDimHypList& listOfdimHyp = theDimHypListArr[theDim];
  if ( listOfdimHyp.empty() || listOfdimHyp.back()->_subMesh != theSubMesh )
  {
    SMESH_DimHyp* dimHyp = new SMESH_DimHyp( theSubMesh, theDim, theShape );
    dimHyp->_hypotheses.push_front(theAlgo);
    listOfdimHyp.push_back( dimHyp );
  }

  SMESH_DimHyp* dimHyp = const_cast<SMESH_DimHyp*>( listOfdimHyp.back() );
  dimHyp->_hypotheses.insert( dimHyp->_hypotheses.end(),
                              theHypList.begin(), theHypList.end() );
}

//-----------------------------------------------------------------------------
void addInOrderOfPriority( const SMESH_DimHyp* theDimHyp,
                           TDimHypList&        theListOfConcurr)
{
  if ( theListOfConcurr.empty() )
  {
    theListOfConcurr.push_back( theDimHyp );
  }
  else
  {
    TDimHypList::iterator hypIt = theListOfConcurr.begin();
    while ( hypIt != theListOfConcurr.end() &&
            !theDimHyp->IsHigherPriorityThan( *hypIt ))
      ++hypIt;
    theListOfConcurr.insert( hypIt, theDimHyp );
  }
}

//-----------------------------------------------------------------------------
void findConcurrents(const SMESH_DimHyp* theDimHyp,
                     const TDimHypList&  theListOfDimHyp,
                     TDimHypList&        theListOfConcurrHyp,
                     set<int>&           theSetOfConcurrId )
{
  TDimHypList::const_reverse_iterator rIt = theListOfDimHyp.rbegin();
  for ( ; rIt != theListOfDimHyp.rend(); rIt++ )
  {
    const SMESH_DimHyp* curDimHyp = *rIt;
    if ( curDimHyp == theDimHyp )
      break; // meet own dimHyp pointer in same dimension

    if ( theDimHyp->IsConcurrent( curDimHyp ) &&
         theSetOfConcurrId.insert( curDimHyp->_subMesh->GetId() ).second )
    {
      addInOrderOfPriority( curDimHyp, theListOfConcurrHyp );
    }
  }
}

//-----------------------------------------------------------------------------
bool unionLists(TListOfInt&       theListOfId,
                TListOfListOfInt& theListOfListOfId,
                const int         theIndx )
{
  bool changed = false;
  if ( theListOfId.empty() )
    return changed;
  TListOfListOfInt::iterator it = theListOfListOfId.begin();
  for ( int i = 0; it != theListOfListOfId.end(); it++, i++ )
  {
    if ( i < theIndx )
      continue; //skip already treated lists
    // check if other list has any same submesh object
    TListOfInt& otherListOfId = *it;
    if ( find_first_of( theListOfId.begin(), theListOfId.end(),
                        otherListOfId.begin(), otherListOfId.end() ) == theListOfId.end() )
      continue;

    // union two lists (from source into target)
    TListOfInt::iterator it2 = otherListOfId.begin();
    for ( ; it2 != otherListOfId.end(); it2++ ) {
      if ( find( theListOfId.begin(), theListOfId.end(), (*it2) ) == theListOfId.end() )
      {
        theListOfId.push_back(*it2);
        changed = true;
      }
    }
    // clear source list
    otherListOfId.clear();
  }
  return changed;
}
//-----------------------------------------------------------------------------

//! free memory allocated for dimension-hypothesis objects
void removeDimHyps( TDimHypList* theArrOfList )
{
  for (int i = 0; i < 4; i++ ) {
    TDimHypList& listOfdimHyp = theArrOfList[i];
    TDimHypList::const_iterator it = listOfdimHyp.begin();
    for ( ; it != listOfdimHyp.end(); it++ )
      delete (*it);
  }
}

//-----------------------------------------------------------------------------
/*!
 * \brief find common submeshes with given submesh
 * \param theSubMeshList list of already collected submesh to check
 * \param theSubMesh given submesh to intersect with other
 * \param theCommonSubMeshes collected common submeshes
 */
void findCommonSubMesh (list<const SMESH_subMesh*>& theSubMeshList,
                        const SMESH_subMesh*        theSubMesh,
                        set<const SMESH_subMesh*>&  theCommon )
{
  if ( !theSubMesh )
    return;
  list<const SMESH_subMesh*>::const_iterator it = theSubMeshList.begin();
  for ( ; it != theSubMeshList.end(); it++ )
    theSubMesh->FindIntersection( *it, theCommon );
  theSubMeshList.push_back( theSubMesh );
  //theCommon.insert( theSubMesh );
}

//-----------------------------------------------------------------------------
bool isSubMeshInList ( int smID, const TListOfListOfInt& smLists )
{
  TListOfListOfInt::const_iterator listsIt = smLists.begin();
  for ( ; listsIt != smLists.end(); ++listsIt )
  {
    const TListOfInt& smIDs = *listsIt;
    if ( std::find( smIDs.begin(), smIDs.end(), smID ) != smIDs.end() )
      return true;
  }
  return false;
}

} // namespace

//=============================================================================
/*!
 * \brief Return \c true if a meshing order not yet set for a concurrent sub-mesh
 */
//=============================================================================

CORBA::Boolean SMESH_Mesh_i::IsUnorderedSubMesh(CORBA::Long submeshID)
{
  TListOfListOfInt anOrder = GetImpl().GetMeshOrder(); // already defined order
  if ( isSubMeshInList( submeshID, anOrder ))
    return false;

  TListOfListOfInt allConurrent = findConcurrentSubMeshes();
  return isSubMeshInList( submeshID, allConurrent );
}

//=============================================================================
/*!
 * \brief Return sub-mesh objects list in meshing order
 */
//=============================================================================

SMESH::submesh_array_array* SMESH_Mesh_i::GetMeshOrder()
{
  SMESH::submesh_array_array_var aResult = new SMESH::submesh_array_array();

  SMESHDS_Mesh* aMeshDS = _impl->GetMeshDS();
  if ( !aMeshDS )
    return aResult._retn();

  TListOfListOfInt      anOrder = GetImpl().GetMeshOrder(); // already defined order
  TListOfListOfInt allConurrent = findConcurrentSubMeshes();
  anOrder.splice( anOrder.end(), allConurrent );

  bool changed;
  do {
    changed = false;
    TListOfListOfInt::iterator listIt = anOrder.begin();
    for ( int listIndx = 1; listIt != anOrder.end(); listIt++, listIndx++ )
      if ( unionLists( *listIt,  anOrder, listIndx ))
        changed = true;
  }
  while ( changed );

  // convert submesh ids into interface instances
  //  and dump command into python
  convertMeshOrder( anOrder, aResult, false );

  return aResult._retn();
}

//=============================================================================
/*!
 * \brief Finds concurrent sub-meshes
 */
//=============================================================================

TListOfListOfInt SMESH_Mesh_i::findConcurrentSubMeshes()
{
  TListOfListOfInt anOrder;
  ::SMESH_Mesh& mesh = GetImpl();

  // collect submeshes and detect concurrent algorithms and hypothesises
  TDimHypList dimHypListArr[4]; // dimHyp list for each shape dimension

  map<int, ::SMESH_subMesh*>::iterator i_sm = _mapSubMesh.begin();
  for ( ; i_sm != _mapSubMesh.end(); i_sm++ )
  {
    ::SMESH_subMesh* sm = (*i_sm).second;
    // shape of submesh
    const TopoDS_Shape& aSubMeshShape = sm->GetSubShape();

    // list of assigned hypothesises
    const list <const SMESHDS_Hypothesis*>& hypList = mesh.GetHypothesisList(aSubMeshShape);
    // Find out dimensions where the submesh can be concurrent.
    // We define the dimensions by algo of each of hypotheses in hypList
    list <const SMESHDS_Hypothesis*>::const_iterator hypIt = hypList.begin();
    for( ; hypIt != hypList.end(); hypIt++ )
    {
      SMESH_Algo* anAlgo = 0;
      const SMESH_Hypothesis* hyp = dynamic_cast<const SMESH_Hypothesis*>(*hypIt);
      if ( hyp->GetType() != SMESHDS_Hypothesis::PARAM_ALGO )
        // hyp it-self is algo
        anAlgo = (SMESH_Algo*)dynamic_cast<const SMESH_Algo*>(hyp);
      else {
        // try to find algorithm with help of sub-shapes
        TopExp_Explorer anExp( aSubMeshShape, shapeTypeByDim(hyp->GetDim()) );
        for ( ; !anAlgo && anExp.More(); anExp.Next() )
          anAlgo = mesh.GetGen()->GetAlgo( mesh, anExp.Current() );
      }
      if (!anAlgo)
        continue; // no algorithm assigned to a current submesh

      int dim = anAlgo->GetDim(); // top concurrent dimension (see comment to SMESH_DimHyp)
      // the submesh can concurrent at <dim> (or lower dims if !anAlgo->NeedDiscreteBoundary()
      // and !anAlgo->NeedLowerHyps( dim ))

      // create instance of dimension-hypothesis for found concurrent dimension(s) and algorithm
      for ( int j = anAlgo->NeedDiscreteBoundary() ? dim : 1, jn = dim; j <= jn; j++ )
        addDimHypInstance( j, aSubMeshShape, anAlgo, sm, hypList, dimHypListArr );
    }
  } // end iterations on submesh

    // iterate on created dimension-hypotheses and check for concurrents
  for ( int i = 0; i < 4; i++ )
  {
    const TDimHypList& listOfDimHyp = dimHypListArr[i];
    // check for concurrents in own and other dimensions (step-by-step)
    TDimHypList::const_iterator dhIt = listOfDimHyp.begin();
    for ( ; dhIt != listOfDimHyp.end(); dhIt++ )
    {
      const SMESH_DimHyp* dimHyp = *dhIt;
      TDimHypList listOfConcurr;
      set<int>    setOfConcurrIds;
      // looking for concurrents and collect into own list
      for ( int j = i; j < 4; j++ )
        findConcurrents( dimHyp, dimHypListArr[j], listOfConcurr, setOfConcurrIds );
      // check if any concurrents found
      if ( listOfConcurr.size() > 0 )
      {
        // add own submesh to list of concurrent
        addInOrderOfPriority( dimHyp, listOfConcurr );
        list<int> listOfConcurrIds;
        TDimHypList::iterator hypIt = listOfConcurr.begin();
        for ( ; hypIt != listOfConcurr.end(); ++hypIt )
          listOfConcurrIds.push_back( (*hypIt)->_subMesh->GetId() );
        anOrder.push_back( listOfConcurrIds );
      }
    }
  }

  removeDimHyps(dimHypListArr);

  // now, minimize the number of concurrent groups
  // Here we assume that lists of submeshes can have same submesh
  // in case of multi-dimension algorithms, as result
  //  list with common submesh has to be united into one list
  int listIndx = 0;
  TListOfListOfInt::iterator listIt = anOrder.begin();
  for(; listIt != anOrder.end(); listIt++, listIndx++ )
    unionLists( *listIt,  anOrder, listIndx + 1 );

  return anOrder;
}

//=============================================================================
/*!
 * \brief Set submesh object order
 * \param theSubMeshArray submesh array order
 */
//=============================================================================

::CORBA::Boolean SMESH_Mesh_i::SetMeshOrder(const SMESH::submesh_array_array& theSubMeshArray)
{
  if ( _preMeshInfo )
    _preMeshInfo->ForgetOrLoad();

  bool res = false;
  ::SMESH_Mesh& mesh = GetImpl();

  TPythonDump aPythonDump; // prevent dump of called methods
  aPythonDump << "isDone = " << SMESH::SMESH_Mesh_var(_this()) << ".SetMeshOrder( [ ";

  TListOfListOfInt subMeshOrder;
  for ( int i = 0, n = theSubMeshArray.length(); i < n; i++ )
  {
    const SMESH::submesh_array& aSMArray = theSubMeshArray[i];
    TListOfInt subMeshIds;
    if ( i > 0 )
      aPythonDump << ", ";
    aPythonDump << "[ ";
    // Collect subMeshes which should be clear
    //  do it list-by-list, because modification of submesh order
    //  take effect between concurrent submeshes only
    set<const SMESH_subMesh*> subMeshToClear;
    list<const SMESH_subMesh*> subMeshList;
    for ( int j = 0, jn = aSMArray.length(); j < jn; j++ )
    {
      const SMESH::SMESH_subMesh_var subMesh = SMESH::SMESH_subMesh::_duplicate(aSMArray[j]);
      if ( j > 0 )
        aPythonDump << ", ";
      aPythonDump << subMesh;
      subMeshIds.push_back( subMesh->GetId() );
      // detect common parts of submeshes
      if ( _mapSubMesh.find(subMesh->GetId()) != _mapSubMesh.end() )
        findCommonSubMesh( subMeshList, _mapSubMesh[ subMesh->GetId() ], subMeshToClear );
    }
    aPythonDump << " ]";
    subMeshOrder.push_back( subMeshIds );

    // clear collected sub-meshes
    set<const SMESH_subMesh*>::iterator clrIt = subMeshToClear.begin();
    for ( ; clrIt != subMeshToClear.end(); clrIt++ )
      if ( SMESH_subMesh* sm = (SMESH_subMesh*)*clrIt )
      {
        sm->ComputeStateEngine( SMESH_subMesh::CLEAN );
        if ( SMESH_Algo* algo = sm->GetAlgo() ) // #16748
          sm->AlgoStateEngine( SMESH_subMesh::MODIF_HYP, algo ); // to clear a cached algo
      }
  }
  aPythonDump << " ])";

  mesh.SetMeshOrder( subMeshOrder );
  res = true;

  SMESH::SMESH_Mesh_var me = _this();
  _gen_i->UpdateIcons( me );

  return res;
}

//=============================================================================
/*!
 * \brief Set the number of threads for a parallel computation
 */
//=============================================================================
void SMESH_Mesh_i::SetNbThreads(int nbThreads){
  _impl->SetNbThreads(nbThreads);
}

//=============================================================================
/*!
 * \brief Set the number of threads for the mesher for a parallel computation
 */
//=============================================================================
void SMESH_Mesh_i::SetMesherNbThreads(int nbThreads){
  _impl->SetMesherNbThreads(nbThreads);
}


//=============================================================================
/*!
 * \brief Convert submesh ids into submesh interfaces
 */
//=============================================================================

void SMESH_Mesh_i::convertMeshOrder (const TListOfListOfInt&     theIdsOrder,
                                     SMESH::submesh_array_array& theResOrder,
                                     const bool                  theIsDump)
{
  int nbSet = theIdsOrder.size();
  TPythonDump aPythonDump; // prevent dump of called methods
  if ( theIsDump )
    aPythonDump << "[ ";
  theResOrder.length(nbSet);
  TListOfListOfInt::const_iterator it = theIdsOrder.begin();
  int listIndx = 0;
  for( ; it != theIdsOrder.end(); it++ )
  {
    // translate submesh identificators into submesh objects
    //  takeing into account real number of concurrent lists
    const TListOfInt& aSubOrder = (*it);
    if (!aSubOrder.size())
      continue;
    if ( theIsDump )
      aPythonDump << "[ ";
    // convert shape indices into interfaces
    SMESH::submesh_array_var aResSubSet = new SMESH::submesh_array();
    aResSubSet->length(aSubOrder.size());
    TListOfInt::const_iterator subIt = aSubOrder.begin();
    int j;
    for( j = 0; subIt != aSubOrder.end(); subIt++ )
    {
      if ( _mapSubMeshIor.find(*subIt) == _mapSubMeshIor.end() )
        continue;
      SMESH::SMESH_subMesh_var subMesh =
        SMESH::SMESH_subMesh::_duplicate( _mapSubMeshIor[*subIt] );
      if ( theIsDump ) {
        if ( j > 0 )
          aPythonDump << ", ";
        aPythonDump << subMesh;
      }
      aResSubSet[ j++ ] = subMesh;
    }
    if ( theIsDump )
      aPythonDump << " ]";
    if ( j > 1 )
      theResOrder[ listIndx++ ] = aResSubSet;
  }
  // correct number of lists
  theResOrder.length( listIndx );

  if ( theIsDump ) {
    // finilise python dump
    aPythonDump << " ]";
    aPythonDump << " = " << SMESH::SMESH_Mesh_var(_this()) << ".GetMeshOrder()";
  }
}

namespace // utils used by SMESH_MeshPartDS
{
  /*!
   * \brief Class used to access to protected data of SMDS_MeshInfo
   */
  struct TMeshInfo : public SMDS_MeshInfo
  {
    void Add(const SMDS_MeshElement* e) { SMDS_MeshInfo::addWithPoly( e ); }
  };
  /*!
   * \brief Element holing its ID only
   */
  struct TElemID : public SMDS_LinearEdge
  {
    TElemID(int ID) : SMDS_LinearEdge(0,0) { setID( ID ); }
  };
}

//================================================================================
//
// Implementation of SMESH_MeshPartDS
//
SMESH_MeshPartDS::SMESH_MeshPartDS(SMESH::SMESH_IDSource_ptr meshPart):
  SMESHDS_Mesh( /*meshID=*/-1, /*isEmbeddedMode=*/true)
{
  SMESH::SMESH_Mesh_var mesh = meshPart->GetMesh();
  SMESH_Mesh_i*       mesh_i = SMESH::DownCast<SMESH_Mesh_i*>( mesh );

  mesh_i->Load();
  _meshDS = mesh_i->GetImpl().GetMeshDS();

  SetPersistentId( _meshDS->GetPersistentId() );

  if ( mesh_i == SMESH::DownCast<SMESH_Mesh_i*>( meshPart ))
  {
    // <meshPart> is the whole mesh
    myInfo = _meshDS->GetMeshInfo(); // copy mesh info;
    // copy groups
    set<SMESHDS_GroupBase*>& myGroupSet = const_cast<set<SMESHDS_GroupBase*>&>( GetGroups() );
    myGroupSet = _meshDS->GetGroups();
  }
  else
  {
    TMeshInfo tmpInfo;
    SMESH::smIdType_array_var       anIDs = meshPart->GetIDs();
    SMESH::array_of_ElementType_var types = meshPart->GetTypes();
    if ( types->length() == 1 && types[0] == SMESH::NODE ) // group of nodes
    {
      for ( CORBA::ULong i=0; i < anIDs->length(); i++ )
        if ( const SMDS_MeshNode * n = _meshDS->FindNode( anIDs[i] ))
          if ( _elements[ SMDSAbs_Node ].insert( n ).second )
            tmpInfo.Add( n );
    }
    else
    {
      for ( CORBA::ULong i=0; i < anIDs->length(); i++ )
        if ( const SMDS_MeshElement * e = _meshDS->FindElement(anIDs[i]))
          if ( _elements[ e->GetType() ].insert( e ).second )
          {
            tmpInfo.Add( e );
            SMDS_ElemIteratorPtr nIt = e->nodesIterator();
            while ( nIt->more() )
            {
              const SMDS_MeshNode * n = (const SMDS_MeshNode*) nIt->next();
              if ( _elements[ SMDSAbs_Node ].insert( n ).second )
                tmpInfo.Add( n );
            }
          }
    }
    myInfo = tmpInfo;

    ShapeToMesh( _meshDS->ShapeToMesh() );

    _meshDS = 0; // to enforce iteration on _elements and _nodes
  }
}
// -------------------------------------------------------------------------------------
SMESH_MeshPartDS::SMESH_MeshPartDS(const std::list< const SMDS_MeshElement* > & meshPart):
  SMESHDS_Mesh( /*meshID=*/-1, /*isEmbeddedMode=*/true), _meshDS(0)
{
  TMeshInfo tmpInfo;
  list< const SMDS_MeshElement* >::const_iterator partIt = meshPart.begin();
  for ( ; partIt != meshPart.end(); ++partIt )
    if ( const SMDS_MeshElement * e = *partIt )
      if ( _elements[ e->GetType() ].insert( e ).second )
      {
        tmpInfo.Add( e );
        SMDS_ElemIteratorPtr nIt = e->nodesIterator();
        while ( nIt->more() )
        {
          const SMDS_MeshNode * n = (const SMDS_MeshNode*) nIt->next();
          if ( _elements[ SMDSAbs_Node ].insert( n ).second )
            tmpInfo.Add( n );
        }
      }
  myInfo = tmpInfo;
}
// -------------------------------------------------------------------------------------
const SMDS_MeshElement * SMESH_MeshPartDS::FindElement(int IDelem) const
{
  if ( _meshDS ) return _meshDS->FindElement( IDelem );

  TElemID elem( IDelem );
  for ( int iType = SMDSAbs_Edge; iType < SMDSAbs_NbElementTypes; ++iType )
    if ( !_elements[ iType ].empty() )
    {
      TIDSortedElemSet::const_iterator it = _elements[ iType ].find( &elem );
      if ( it != _elements[ iType ].end() )
        return *it;
    }
  return 0;
}
// -------------------------------------------------------------------------------------
bool SMESH_MeshPartDS::HasNumerationHoles()
{
  if ( _meshDS ) return _meshDS->HasNumerationHoles();

  return ( MinNodeID() != 1 ||
           MaxNodeID() != NbNodes() ||
           MinElementID() != 1 ||
           MaxElementID() != NbElements() );
}
// -------------------------------------------------------------------------------------
smIdType SMESH_MeshPartDS::MaxNodeID() const
{
  if ( _meshDS ) return _meshDS->MaxNodeID();
  return NbNodes() == 0 ? 0 : (*_elements[ SMDSAbs_Node ].rbegin())->GetID();
}
// -------------------------------------------------------------------------------------
smIdType SMESH_MeshPartDS::MinNodeID() const
{
  if ( _meshDS ) return _meshDS->MinNodeID();
  return NbNodes() == 0 ? 0 : (*_elements[ SMDSAbs_Node ].begin())->GetID();
}
// -------------------------------------------------------------------------------------
smIdType SMESH_MeshPartDS::MaxElementID() const
{
  if ( _meshDS ) return _meshDS->MaxElementID();
  smIdType maxID = 0;
  for ( int iType = SMDSAbs_Edge; iType < SMDSAbs_NbElementTypes; ++iType )
    if ( !_elements[ iType ].empty() )
      maxID = std::max( maxID, (*_elements[ iType ].rbegin())->GetID() );
  return maxID;
}
// -------------------------------------------------------------------------------------
smIdType SMESH_MeshPartDS::MinElementID() const
{
  if ( _meshDS ) return _meshDS->MinElementID();
  smIdType minID = 0;
  for ( int iType = SMDSAbs_Edge; iType < SMDSAbs_NbElementTypes; ++iType )
    if ( !_elements[ iType ].empty() )
      minID = std::min( minID, (*_elements[ iType ].begin())->GetID() );
  return minID;
}
// -------------------------------------------------------------------------------------
SMDS_ElemIteratorPtr SMESH_MeshPartDS::elementGeomIterator(SMDSAbs_GeometryType geomType) const
{
  if ( _meshDS ) return _meshDS->elementGeomIterator( geomType );

  typedef SMDS_SetIterator
    <const SMDS_MeshElement*,
    TIDSortedElemSet::const_iterator,
    SMDS::SimpleAccessor<const SMDS_MeshElement*, TIDSortedElemSet::const_iterator>,
    SMDS_MeshElement::GeomFilter
    > TIter;

  SMDSAbs_ElementType type = SMDS_MeshCell::ElemType( geomType );

  return SMDS_ElemIteratorPtr( new TIter( _elements[type].begin(),
                                          _elements[type].end(),
                                          SMDS_MeshElement::GeomFilter( geomType )));
}
// -------------------------------------------------------------------------------------
SMDS_ElemIteratorPtr SMESH_MeshPartDS::elementEntityIterator(SMDSAbs_EntityType entity) const
{
  if ( _meshDS ) return _meshDS->elementEntityIterator( entity );

  typedef SMDS_SetIterator
    <const SMDS_MeshElement*,
    TIDSortedElemSet::const_iterator,
    SMDS::SimpleAccessor<const SMDS_MeshElement*, TIDSortedElemSet::const_iterator>,
    SMDS_MeshElement::EntityFilter
    > TIter;

  SMDSAbs_ElementType type = SMDS_MeshCell::ElemType( entity );

  return SMDS_ElemIteratorPtr( new TIter( _elements[type].begin(),
                                          _elements[type].end(),
                                          SMDS_MeshElement::EntityFilter( entity )));
}
// -------------------------------------------------------------------------------------
SMDS_ElemIteratorPtr SMESH_MeshPartDS::elementsIterator(SMDSAbs_ElementType type) const
{
  typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::const_iterator > TIter;
  if ( type == SMDSAbs_All && !_meshDS )
  {
    typedef vector< SMDS_ElemIteratorPtr > TIterVec;
    TIterVec iterVec;
    for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
      if ( !_elements[i].empty() && i != SMDSAbs_Node )
        iterVec.push_back
          ( SMDS_ElemIteratorPtr( new TIter( _elements[i].begin(), _elements[i].end() )));

    typedef SMDS_IteratorOnIterators<const SMDS_MeshElement*, TIterVec > TIterOnIters;
    return SMDS_ElemIteratorPtr( new TIterOnIters( iterVec ));
  }
  return _meshDS ? _meshDS->elementsIterator(type) : SMDS_ElemIteratorPtr
      ( new TIter( _elements[type].begin(), _elements[type].end() ));
}
// -------------------------------------------------------------------------------------
#define _GET_ITER_DEFINE( iterType, methName, elem, elemType)                       \
  iterType SMESH_MeshPartDS::methName() const                 \
  {                                                                                 \
    typedef SMDS_SetIterator<const elem*, TIDSortedElemSet::const_iterator > TIter; \
    return _meshDS ? _meshDS->methName() : iterType                 \
      ( new TIter( _elements[elemType].begin(), _elements[elemType].end() ));       \
  }
// -------------------------------------------------------------------------------------
_GET_ITER_DEFINE( SMDS_NodeIteratorPtr, nodesIterator, SMDS_MeshNode, SMDSAbs_Node )
_GET_ITER_DEFINE( SMDS_EdgeIteratorPtr, edgesIterator, SMDS_MeshEdge, SMDSAbs_Edge )
_GET_ITER_DEFINE( SMDS_FaceIteratorPtr, facesIterator, SMDS_MeshFace, SMDSAbs_Face )
_GET_ITER_DEFINE( SMDS_VolumeIteratorPtr, volumesIterator, SMDS_MeshVolume, SMDSAbs_Volume)
#undef _GET_ITER_DEFINE
//
// END Implementation of SMESH_MeshPartDS
//
//================================================================================
