Salome HOME
bos #24400 [CEA] Option in SALOME for not storing in med files the indices (number...
[modules/smesh.git] / src / DriverMED / DriverMED_W_SMESHDS_Mesh.cxx
1 // Copyright (C) 2007-2021  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 //  SMESH DriverMED : driver to read and write 'med' files
24 //  File   : DriverMED_W_SMESHDS_Mesh.cxx
25 //  Module : SMESH
26 //
27
28 #include "DriverMED_W_SMESHDS_Mesh.h"
29
30 #include "DriverMED_Family.h"
31 #include "MED_Factory.hxx"
32 #include "MED_Utilities.hxx"
33 #include "SMDS_IteratorOnIterators.hxx"
34 #include "SMDS_MeshNode.hxx"
35 #include "SMDS_SetIterator.hxx"
36 #include "SMESHDS_Mesh.hxx"
37 #include "MED_Common.hxx"
38 #include "MED_TFile.hxx"
39
40 #include <med.h>
41
42 #include <BRep_Tool.hxx>
43 #include <TopExp_Explorer.hxx>
44 #include <TopoDS.hxx>
45
46 #include <utilities.h>
47
48
49 #define _EDF_NODE_IDS_
50 //#define _ELEMENTS_BY_DIM_
51
52 using namespace std;
53 using namespace MED;
54
55
56 //================================================================================
57 /*!
58  * \brief Constructor
59  */
60 //================================================================================
61
62 DriverMED_W_SMESHDS_Mesh::DriverMED_W_SMESHDS_Mesh():
63   myAllSubMeshes (false),
64   myDoGroupOfNodes (false),
65   myDoGroupOfEdges (false),
66   myDoGroupOfFaces (false),
67   myDoGroupOfVolumes (false),
68   myDoGroupOf0DElems(false),
69   myDoGroupOfBalls(false),
70   myAutoDimension(false),
71   myAddODOnVertices(false),
72   myDoAllInGroups(false),
73   myVersion(-1),
74   myZTolerance(-1.),
75   mySaveNumbers(true)
76 {}
77
78 //================================================================================
79 /*!
80  * \brief Set a file name and a version
81  *  \param [in] theFileName - output file name
82  *  \param [in] theVersion - desired MED file version == major * 10 + minor
83  */
84 //================================================================================
85
86 void DriverMED_W_SMESHDS_Mesh::SetFile(const std::string& theFileName, int theVersion)
87 {
88   myVersion = theVersion;
89   Driver_SMESHDS_Mesh::SetFile(theFileName);
90 }
91
92 //================================================================================
93 /*!
94  * MED version is either the latest available, or with an inferior minor,
95  * to ensure backward compatibility on writing med files.
96  */
97 //================================================================================
98
99 string DriverMED_W_SMESHDS_Mesh::GetVersionString(int theMinor, int theNbDigits)
100 {
101   TInt majeur, mineur, release;
102   majeur=MED_MAJOR_NUM;
103   mineur=MED_MINOR_NUM;
104   release=MED_RELEASE_NUM;
105   TInt imposedMineur = mineur;
106
107   if (theMinor < 0)
108     imposedMineur = mineur;
109   else if (theMinor > MED_MINOR_NUM)
110     imposedMineur = mineur;
111   else
112     imposedMineur = theMinor;
113
114   ostringstream name;
115   if ( theNbDigits > 0 )
116     name << majeur;
117   if ( theNbDigits > 1 )
118     name << "." << imposedMineur;
119   if ( theNbDigits > 2 )
120     name << "." << release;
121   return name.str();
122 }
123
124 void DriverMED_W_SMESHDS_Mesh::AddGroup(SMESHDS_GroupBase* theGroup)
125 {
126   myGroups.push_back(theGroup);
127 }
128
129 void DriverMED_W_SMESHDS_Mesh::AddAllSubMeshes()
130 {
131   myAllSubMeshes = true;
132 }
133
134 void DriverMED_W_SMESHDS_Mesh::AddSubMesh(SMESHDS_SubMesh* theSubMesh, int /*theID*/)
135 {
136   mySubMeshes.push_back( theSubMesh );
137 }
138
139 void DriverMED_W_SMESHDS_Mesh::AddGroupOfNodes()
140 {
141   myDoGroupOfNodes = true;
142 }
143
144 void DriverMED_W_SMESHDS_Mesh::AddGroupOfEdges()
145 {
146   myDoGroupOfEdges = true;
147 }
148
149 void DriverMED_W_SMESHDS_Mesh::AddGroupOfFaces()
150 {
151   myDoGroupOfFaces = true;
152 }
153
154 void DriverMED_W_SMESHDS_Mesh::AddGroupOfVolumes()
155 {
156   myDoGroupOfVolumes = true;
157 }
158
159 void DriverMED_W_SMESHDS_Mesh::AddGroupOf0DElems()
160 {
161   myDoGroupOf0DElems = true;
162 }
163
164 void DriverMED_W_SMESHDS_Mesh::AddGroupOfBalls()
165 {
166   myDoGroupOfBalls = true;
167 }
168
169 //================================================================================
170 /*!
171  * \brief Set up a flag to add all elements not belonging to any group to
172  *        some auxiliary group. This is needed for SMESH -> SAUVE -> SMESH conversion,
173  *        which since PAL0023285 reads only SAUVE elements belonging to any group,
174  *        and hence can lose some elements. That auxiliary group is ignored while
175  *        reading a MED file.
176  */
177 //================================================================================
178
179 void DriverMED_W_SMESHDS_Mesh::AddAllToGroup()
180 {
181   myDoAllInGroups = true;
182 }
183
184
185 namespace
186 {
187   //---------------------------------------------
188   /*!
189    * \brief Retrieving node coordinates utilities
190    */
191   //---------------------------------------------
192
193   typedef double (SMDS_MeshNode::* TGetCoord)() const;
194   typedef const char* TName;
195   typedef const char* TUnit;
196
197   // name length in a mesh must be equal to 16 :
198   //         1234567890123456
199   TName M = "m               ";
200   TName X = "x               ";
201   TName Y = "y               ";
202   TName Z = "z               ";
203
204   TUnit aUnit[3] = {M,M,M};
205
206   // 3 dim
207   TGetCoord aXYZGetCoord[3] = {
208     &SMDS_MeshNode::X, 
209     &SMDS_MeshNode::Y, 
210     &SMDS_MeshNode::Z
211   };
212   TName aXYZName[3] = {X,Y,Z};
213   
214   // 2 dim
215   TGetCoord aXYGetCoord[2] = {
216     &SMDS_MeshNode::X, 
217     &SMDS_MeshNode::Y
218   };
219   TName aXYName[2] = {X,Y};
220
221   TGetCoord aYZGetCoord[2] = {
222     &SMDS_MeshNode::Y, 
223     &SMDS_MeshNode::Z
224   };
225   TName aYZName[2] = {Y,Z};
226
227   TGetCoord aXZGetCoord[2] = {
228     &SMDS_MeshNode::X, 
229     &SMDS_MeshNode::Z
230   };
231   TName aXZName[2] = {X,Z};
232
233   // 1 dim
234   TGetCoord aXGetCoord[1] = {
235     &SMDS_MeshNode::X
236   };
237   TName aXName[1] = {X};
238
239   TGetCoord aYGetCoord[1] = {
240     &SMDS_MeshNode::Y
241   };
242   TName aYName[1] = {Y};
243
244   TGetCoord aZGetCoord[1] = {
245     &SMDS_MeshNode::Z
246   };
247   TName aZName[1] = {Z};
248
249
250   class TCoordHelper{
251     SMDS_NodeIteratorPtr myNodeIter;
252     const SMDS_MeshNode* myCurrentNode;
253     TGetCoord* myGetCoord;
254     TName* myName;
255     TUnit* myUnit;
256   public:
257     TCoordHelper(const SMDS_NodeIteratorPtr& theNodeIter,
258                  TGetCoord* theGetCoord,
259                  TName* theName,
260                  TUnit* theUnit = aUnit):
261       myNodeIter(theNodeIter),
262       myGetCoord(theGetCoord),
263       myName(theName),
264       myUnit(theUnit)
265     {}
266     virtual ~TCoordHelper(){}
267     bool Next(){ 
268       return myNodeIter->more() && 
269         (myCurrentNode = myNodeIter->next());
270     }
271     const SMDS_MeshNode* GetNode(){
272       return myCurrentNode;
273     }
274     MED::TIDVector::value_type GetID(){
275       return myCurrentNode->GetID();
276     }
277     MED::TFloatVector::value_type GetCoord(TInt theCoodId){
278       return (myCurrentNode->*myGetCoord[theCoodId])();
279     }
280     MED::TStringVector::value_type GetName(TInt theDimId){
281       return myName[theDimId];
282     }
283     MED::TStringVector::value_type GetUnit(TInt theDimId){
284       return myUnit[theDimId];
285     }
286   };
287   typedef boost::shared_ptr<TCoordHelper> TCoordHelperPtr;
288
289   //-------------------------------------------------------
290   /*!
291    * \brief Structure describing element type
292    */
293   //-------------------------------------------------------
294   struct TElemTypeData
295   {
296     EEntiteMaillage     _entity;
297     EGeometrieElement   _geomType;
298     TInt                _nbElems;
299     SMDSAbs_ElementType _smdsType;
300
301     TElemTypeData (EEntiteMaillage entity, EGeometrieElement geom, TInt nb, SMDSAbs_ElementType type)
302       : _entity(entity), _geomType(geom), _nbElems( nb ), _smdsType( type ) {}
303   };
304
305
306   typedef NCollection_DataMap< Standard_Address, int > TElemFamilyMap;
307
308   //================================================================================
309   /*!
310    * \brief Fills element to famaly ID map for element type.
311    * Removes all families of anElemType
312    */
313   //================================================================================
314
315   void fillElemFamilyMap( TElemFamilyMap &            anElemFamMap,
316                           list<DriverMED_FamilyPtr> & aFamilies,
317                           const SMDSAbs_ElementType   anElemType)
318   {
319     anElemFamMap.Clear();
320     list<DriverMED_FamilyPtr>::iterator aFamsIter = aFamilies.begin();
321     while ( aFamsIter != aFamilies.end() )
322     {
323       if ((*aFamsIter)->GetType() != anElemType) {
324         aFamsIter++;
325       }
326       else {
327         int aFamId = (*aFamsIter)->GetId();
328         const ElementsSet&              anElems = (*aFamsIter)->GetElements();
329         ElementsSet::const_iterator anElemsIter = anElems.begin();
330         for (; anElemsIter != anElems.end(); anElemsIter++)
331         {
332           anElemFamMap.Bind( (Standard_Address)*anElemsIter, aFamId );
333         }
334         // remove a family from the list
335         aFamilies.erase( aFamsIter++ );
336       }
337     }
338   }
339
340   //================================================================================
341   /*!
342    * \brief For an element, return family ID found in the map or a default one
343    */
344   //================================================================================
345
346   int getFamilyId( const TElemFamilyMap &  anElemFamMap,
347                    const SMDS_MeshElement* anElement,
348                    const int               aDefaultFamilyId)
349   {
350     if ( anElemFamMap.IsBound( (Standard_Address) anElement ))
351       return anElemFamMap( (Standard_Address) anElement );
352
353     return aDefaultFamilyId;
354   }
355
356   //================================================================================
357   /*!
358    * \brief Returns iterator on sub-meshes
359    */
360   //================================================================================
361
362   SMESHDS_SubMeshIteratorPtr getIterator( std::vector<SMESHDS_SubMesh*>& mySubMeshes )
363   {
364     return SMESHDS_SubMeshIteratorPtr
365       ( new SMDS_SetIterator
366         < const SMESHDS_SubMesh*, std::vector< SMESHDS_SubMesh* >::iterator >( mySubMeshes.begin(),
367                                                                                mySubMeshes.end() ));
368   }
369 }
370
371 //================================================================================
372 /*!
373  * \brief Write my mesh to a file
374  */
375 //================================================================================
376
377 Driver_Mesh::Status DriverMED_W_SMESHDS_Mesh::Perform()
378 {
379   MED::PWrapper myMed = CrWrapperW(myFile, myVersion);
380   return this->PerformInternal<MED::PWrapper>(myMed);
381 }
382
383 //================================================================================
384 /*!
385  * \brief Write my mesh to a MEDCoupling DS
386  */
387 //================================================================================
388
389 Driver_Mesh::Status DriverMED_W_SMESHDS_Mesh_Mem::Perform()
390 {
391   void *ptr(nullptr);
392   std::size_t sz(0);
393   Driver_Mesh::Status status = Driver_Mesh::DRS_OK;
394   bool isClosed(false);
395   {// let braces to flush (call of MED::PWrapper myMed destructor)
396     TMemFile *tfileInst = new TMemFile(&ptr,&sz,&isClosed);// this new will be destroyed by destructor of myMed
397     MED::PWrapper myMed = CrWrapperW(myFile, -1, tfileInst);
398     status = this->PerformInternal<MED::PWrapper>(myMed);
399   }
400   if(!isClosed)
401     EXCEPTION(std::runtime_error, "TFTMemFile destructor : on destruction file has not been closed properly -> chunk of memory data may be invalid !");
402   _data = MEDCoupling::DataArrayByte::New();
403   _data->useArray(reinterpret_cast<char *>(ptr),true,MEDCoupling::DeallocType::C_DEALLOC,sz,1);
404   if(!isClosed)
405     THROW_SALOME_EXCEPTION("DriverMED_W_SMESHDS_Mesh_Mem::Perform - MED memory file id is supposed to be closed !");
406   return status;
407 }
408
409 //================================================================================
410 /*!
411  * \brief Write my mesh
412  */
413 //================================================================================
414
415 template<class LowLevelWriter>
416 Driver_Mesh::Status DriverMED_W_SMESHDS_Mesh::PerformInternal(LowLevelWriter myMed)
417 {
418   Status aResult = DRS_OK;
419   try {
420     //MESSAGE("Perform - myFile : "<<myFile);
421
422     if ( Driver_Mesh::IsMeshTooLarge< TInt >( myMesh, /*checkIDs =*/ true ))
423       return DRS_TOO_LARGE_MESH;
424
425     // Creating the MED mesh for corresponding SMDS structure
426     //-------------------------------------------------------
427     string aMeshName;
428     if (myMeshId != -1) {
429       ostringstream aMeshNameStr;
430       aMeshNameStr<<myMeshId;
431       aMeshName = aMeshNameStr.str();
432     } else {
433       aMeshName = myMeshName;
434     }
435
436     // Mesh dimension definition
437
438     TInt aMeshDimension = 0;
439     if ( myMesh->NbEdges() > 0 )
440       aMeshDimension = 1;
441     if ( myMesh->NbFaces() > 0 )
442       aMeshDimension = 2;
443     if ( myMesh->NbVolumes() > 0 )
444       aMeshDimension = 3;
445
446     TInt aSpaceDimension = 3;
447     TCoordHelperPtr aCoordHelperPtr;
448     {
449       bool anIsXDimension = false;
450       bool anIsYDimension = false;
451       bool anIsZDimension = false;
452       if ( myAutoDimension && aMeshDimension < 3 )
453       {
454         SMDS_NodeIteratorPtr aNodesIter = myMesh->nodesIterator();
455         double aBounds[6] = {0.,0.,0.,0.,0.,0.};
456         if(aNodesIter->more()){
457           const SMDS_MeshNode* aNode = aNodesIter->next();
458           aBounds[0] = aBounds[1] = aNode->X();
459           aBounds[2] = aBounds[3] = aNode->Y();
460           aBounds[4] = aBounds[5] = aNode->Z();
461         }
462         while(aNodesIter->more()){
463           const SMDS_MeshNode* aNode = aNodesIter->next();
464           aBounds[0] = min(aBounds[0],aNode->X());
465           aBounds[1] = max(aBounds[1],aNode->X());
466
467           aBounds[2] = min(aBounds[2],aNode->Y());
468           aBounds[3] = max(aBounds[3],aNode->Y());
469
470           aBounds[4] = min(aBounds[4],aNode->Z());
471           aBounds[5] = max(aBounds[5],aNode->Z());
472         }
473
474         double EPS = 1.0E-7;
475         TopoDS_Shape mainShape = myMesh->ShapeToMesh();
476         bool    hasShapeToMesh = ( myMesh->SubMeshIndices().size() > 1 );
477         if ( !mainShape.IsNull() && hasShapeToMesh )
478         {
479           // define EPS by max tolerance of the mainShape (IPAL53097)
480           TopExp_Explorer subShape;
481           for ( subShape.Init( mainShape, TopAbs_FACE ); subShape.More(); subShape.Next() ) {
482             EPS = Max( EPS, BRep_Tool::Tolerance( TopoDS::Face( subShape.Current() )));
483           }
484           for ( subShape.Init( mainShape, TopAbs_EDGE ); subShape.More(); subShape.Next() ) {
485             EPS = Max( EPS, BRep_Tool::Tolerance( TopoDS::Edge( subShape.Current() )));
486           }
487           for ( subShape.Init( mainShape, TopAbs_VERTEX ); subShape.More(); subShape.Next() ) {
488             EPS = Max( EPS, BRep_Tool::Tolerance( TopoDS::Vertex( subShape.Current() )));
489           }
490           EPS *= 2.;
491         }
492         anIsXDimension = (aBounds[1] - aBounds[0]) + abs(aBounds[1]) + abs(aBounds[0]) > EPS;
493         anIsYDimension = (aBounds[3] - aBounds[2]) + abs(aBounds[3]) + abs(aBounds[2]) > EPS;
494         anIsZDimension = (aBounds[5] - aBounds[4]) + abs(aBounds[5]) + abs(aBounds[4]) > EPS;
495         if ( myZTolerance > 0 && anIsZDimension )
496           anIsZDimension = (aBounds[5] > myZTolerance || aBounds[4] < -myZTolerance );
497         aSpaceDimension = Max( aMeshDimension, anIsXDimension + anIsYDimension + anIsZDimension );
498         if ( !aSpaceDimension )
499           aSpaceDimension = 3;
500         // PAL16857(SMESH not conform to the MED convention):
501         if ( aSpaceDimension == 2 && anIsZDimension ) // 2D only if mesh is in XOY plane
502           aSpaceDimension = 3;
503         // PAL18941(a saved study with a mesh belong Z is opened and the mesh is belong X)
504         if ( aSpaceDimension == 1 && !anIsXDimension ) {// 1D only if mesh is along OX
505           if ( anIsYDimension ) {
506             aSpaceDimension = 2;
507             anIsXDimension = true;
508           } else {
509             aSpaceDimension = 3;
510           }
511         }
512       }
513
514       SMDS_NodeIteratorPtr aNodesIter = myMesh->nodesIterator();
515       switch ( aSpaceDimension ) {
516       case 3:
517         aCoordHelperPtr.reset(new TCoordHelper(aNodesIter,aXYZGetCoord,aXYZName));
518         break;
519       case 2:
520         if(anIsXDimension && anIsYDimension)
521           aCoordHelperPtr.reset(new TCoordHelper(aNodesIter,aXYGetCoord,aXYName));
522         if(anIsYDimension && anIsZDimension)
523           aCoordHelperPtr.reset(new TCoordHelper(aNodesIter,aYZGetCoord,aYZName));
524         if(anIsXDimension && anIsZDimension)
525           aCoordHelperPtr.reset(new TCoordHelper(aNodesIter,aXZGetCoord,aXZName));
526         break;
527       case 1:
528         if(anIsXDimension)
529           aCoordHelperPtr.reset(new TCoordHelper(aNodesIter,aXGetCoord,aXName));
530         if(anIsYDimension)
531           aCoordHelperPtr.reset(new TCoordHelper(aNodesIter,aYGetCoord,aYName));
532         if(anIsZDimension)
533           aCoordHelperPtr.reset(new TCoordHelper(aNodesIter,aZGetCoord,aZName));
534         break;
535       }
536     }
537
538     PMeshInfo aMeshInfo = myMed->CrMeshInfo(aMeshDimension,aSpaceDimension,aMeshName);
539     //MESSAGE("Add - aMeshName : "<<aMeshName<<"; "<<aMeshInfo->GetName());
540     myMed->SetMeshInfo(aMeshInfo);
541
542     // Storing SMDS groups and sub-meshes as med families
543     //----------------------------------------------------
544     int myNodesDefaultFamilyId      = 0;
545     int my0DElementsDefaultFamilyId = 0;
546     int myBallsDefaultFamilyId      = 0;
547     int myEdgesDefaultFamilyId      = 0;
548     int myFacesDefaultFamilyId      = 0;
549     int myVolumesDefaultFamilyId    = 0;
550     smIdType nbNodes      = myMesh->NbNodes();
551     smIdType nb0DElements = myMesh->Nb0DElements();
552     smIdType nbBalls      = myMesh->NbBalls();
553     smIdType nbEdges      = myMesh->NbEdges();
554     smIdType nbFaces      = myMesh->NbFaces();
555     smIdType nbVolumes    = myMesh->NbVolumes();
556     if (myDoGroupOfNodes)   myNodesDefaultFamilyId      = REST_NODES_FAMILY;
557     if (myDoGroupOfEdges)   myEdgesDefaultFamilyId      = REST_EDGES_FAMILY;
558     if (myDoGroupOfFaces)   myFacesDefaultFamilyId      = REST_FACES_FAMILY;
559     if (myDoGroupOfVolumes) myVolumesDefaultFamilyId    = REST_VOLUMES_FAMILY;
560     if (myDoGroupOf0DElems) my0DElementsDefaultFamilyId = REST_0DELEM_FAMILY;
561     if (myDoGroupOfBalls)   myBallsDefaultFamilyId      = REST_BALL_FAMILY;
562     if (myDoAllInGroups )
563     {
564       if (!myDoGroupOfEdges)   myEdgesDefaultFamilyId      = NIG_EDGES_FAMILY   ;
565       if (!myDoGroupOfFaces)   myFacesDefaultFamilyId      = NIG_FACES_FAMILY   ;
566       if (!myDoGroupOfVolumes) myVolumesDefaultFamilyId    = NIG_VOLS_FAMILY    ;
567       if (!myDoGroupOf0DElems) my0DElementsDefaultFamilyId = NIG_0DELEM_FAMILY  ;
568       if (!myDoGroupOfBalls)   myBallsDefaultFamilyId      = NIG_BALL_FAMILY    ;
569     }
570
571     //MESSAGE("Perform - aFamilyInfo");
572     list<DriverMED_FamilyPtr> aFamilies;
573     if (myAllSubMeshes) {
574       aFamilies = DriverMED_Family::MakeFamilies
575         (myMesh->SubMeshes(), myGroups,
576          myDoGroupOfNodes   && nbNodes,
577          myDoGroupOfEdges   && nbEdges,
578          myDoGroupOfFaces   && nbFaces,
579          myDoGroupOfVolumes && nbVolumes,
580          myDoGroupOf0DElems && nb0DElements,
581          myDoGroupOfBalls   && nbBalls,
582          myDoAllInGroups);
583     }
584     else {
585       aFamilies = DriverMED_Family::MakeFamilies
586         (getIterator( mySubMeshes ), myGroups,
587          myDoGroupOfNodes   && nbNodes,
588          myDoGroupOfEdges   && nbEdges,
589          myDoGroupOfFaces   && nbFaces,
590          myDoGroupOfVolumes && nbVolumes,
591          myDoGroupOf0DElems && nb0DElements,
592          myDoGroupOfBalls   && nbBalls,
593          myDoAllInGroups);
594     }
595     list<DriverMED_FamilyPtr>::iterator aFamsIter;
596     for (aFamsIter = aFamilies.begin(); aFamsIter != aFamilies.end(); aFamsIter++)
597     {
598       PFamilyInfo aFamilyInfo = (*aFamsIter)->GetFamilyInfo<LowLevelWriter>(myMed,aMeshInfo);
599       myMed->SetFamilyInfo(aFamilyInfo);
600     }
601
602     // Storing SMDS nodes to the MED file for the MED mesh
603     //----------------------------------------------------
604 #ifdef _EDF_NODE_IDS_
605     typedef map<TInt,TInt> TNodeIdMap;
606     TNodeIdMap aNodeIdMap;
607 #endif
608     const EModeSwitch   theMode        = eFULL_INTERLACE;
609     const ERepere       theSystem      = eCART;
610     const EBooleen      theIsElemNum   = mySaveNumbers ? eVRAI : eFAUX;
611     const EBooleen      theIsElemNames = eFAUX;
612     const EConnectivite theConnMode    = eNOD;
613
614     TInt aNbNodes = FromSmIdType<TInt>( myMesh->NbNodes() );
615     PNodeInfo aNodeInfo = myMed->CrNodeInfo(aMeshInfo, aNbNodes,
616                                             theMode, theSystem, theIsElemNum, theIsElemNames);
617
618     // find family numbers for nodes
619     TElemFamilyMap anElemFamMap;
620     fillElemFamilyMap( anElemFamMap, aFamilies, SMDSAbs_Node );
621
622     for (TInt iNode = 0; aCoordHelperPtr->Next(); iNode++)
623     {
624       // coordinates
625       TCoordSlice aTCoordSlice = aNodeInfo->GetCoordSlice( iNode );
626       for(TInt iCoord = 0; iCoord < aSpaceDimension; iCoord++){
627         aTCoordSlice[iCoord] = aCoordHelperPtr->GetCoord(iCoord);
628       }
629       if ( aSpaceDimension == 3 &&
630            -myZTolerance < aTCoordSlice[2] && aTCoordSlice[2] < myZTolerance )
631         aTCoordSlice[2] = 0.;
632
633       // node number
634       TInt aNodeID = FromSmIdType<TInt>( aCoordHelperPtr->GetID() );
635       aNodeInfo->SetElemNum( iNode, aNodeID );
636 #ifdef _EDF_NODE_IDS_
637       aNodeIdMap.insert( aNodeIdMap.end(), make_pair( aNodeID, iNode+1 ));
638 #endif
639       // family number
640       const SMDS_MeshNode* aNode = aCoordHelperPtr->GetNode();
641       int famNum = getFamilyId( anElemFamMap, aNode, myNodesDefaultFamilyId );
642       aNodeInfo->SetFamNum( iNode, famNum );
643     }
644     anElemFamMap.Clear();
645
646     // coordinate names and units
647     for (TInt iCoord = 0; iCoord < aSpaceDimension; iCoord++) {
648       aNodeInfo->SetCoordName( iCoord, aCoordHelperPtr->GetName(iCoord));
649       aNodeInfo->SetCoordUnit( iCoord, aCoordHelperPtr->GetUnit(iCoord));
650     }
651
652     //MESSAGE("Perform - aNodeInfo->GetNbElem() = "<<aNbNodes);
653     myMed->SetNodeInfo(aNodeInfo);
654     aNodeInfo.reset(); // free memory used for arrays
655
656
657     // Storing SMDS elements to the MED file for the MED mesh
658     //-------------------------------------------------------
659     // Write one element type at once in order to minimize memory usage (PAL19276)
660
661     const SMDS_MeshInfo& nbElemInfo = myMesh->GetMeshInfo();
662
663     // poly elements are not supported by med-2.1
664     bool polyTypesSupported = ( myMed->CrPolygoneInfo(aMeshInfo,eMAILLE,ePOLYGONE,0,0).get() != 0 );
665     TInt nbPolygonNodes = 0, nbPolyhedronNodes = 0, nbPolyhedronFaces = 0;
666
667     // nodes on VERTEXes where 0D elements are absent
668     std::vector<const SMDS_MeshElement*> nodesOf0D;
669     std::vector< SMDS_ElemIteratorPtr > iterVec;
670     SMDS_ElemIteratorPtr iterVecIter;
671     if ( myAddODOnVertices && getNodesOfMissing0DOnVert( myMesh, nodesOf0D ))
672     {
673       iterVec.resize(2);
674       iterVec[0] = myMesh->elementsIterator( SMDSAbs_0DElement );
675       iterVec[1] = SMDS_ElemIteratorPtr
676         ( new SMDS_ElementVectorIterator( nodesOf0D.begin(), nodesOf0D.end() ));
677
678       typedef SMDS_IteratorOnIterators
679         < const SMDS_MeshElement *, std::vector< SMDS_ElemIteratorPtr > > TItIterator;
680       iterVecIter = SMDS_ElemIteratorPtr( new TItIterator( iterVec ));
681     }
682
683     // collect info on all geom types
684
685     list< TElemTypeData > aTElemTypeDatas;
686
687     EEntiteMaillage anEntity = eMAILLE;
688 #ifdef _ELEMENTS_BY_DIM_
689     anEntity = eNOEUD_ELEMENT;
690 #endif
691     aTElemTypeDatas.push_back(TElemTypeData(anEntity,
692                                             ePOINT1,
693                                             nbElemInfo.Nb0DElements() + nodesOf0D.size(),
694                                             SMDSAbs_0DElement));
695 #ifdef _ELEMENTS_BY_DIM_
696     anEntity = eSTRUCT_ELEMENT;
697 #endif
698     aTElemTypeDatas.push_back( TElemTypeData(anEntity,
699                                              eBALL,
700                                              FromSmIdType<TInt>(nbElemInfo.NbBalls()),
701                                              SMDSAbs_Ball));
702 #ifdef _ELEMENTS_BY_DIM_
703     anEntity = eARETE;
704 #endif
705     aTElemTypeDatas.push_back( TElemTypeData(anEntity,
706                                              eSEG2,
707                                              FromSmIdType<TInt>(nbElemInfo.NbEdges( ORDER_LINEAR )),
708                                              SMDSAbs_Edge));
709     aTElemTypeDatas.push_back( TElemTypeData(anEntity,
710                                              eSEG3,
711                                              FromSmIdType<TInt>(nbElemInfo.NbEdges( ORDER_QUADRATIC )),
712                                              SMDSAbs_Edge));
713 #ifdef _ELEMENTS_BY_DIM_
714     anEntity = eFACE;
715 #endif
716     aTElemTypeDatas.push_back( TElemTypeData(anEntity,
717                                              eTRIA3,
718                                              FromSmIdType<TInt>(nbElemInfo.NbTriangles( ORDER_LINEAR )),
719                                              SMDSAbs_Face));
720     aTElemTypeDatas.push_back( TElemTypeData(anEntity,
721                                              eTRIA6,
722                                              FromSmIdType<TInt>(nbElemInfo.NbTriangles( ORDER_QUADRATIC ) -
723                                              nbElemInfo.NbBiQuadTriangles()),
724                                              SMDSAbs_Face));
725     aTElemTypeDatas.push_back( TElemTypeData(anEntity,
726                                              eTRIA7,
727                                              FromSmIdType<TInt>(nbElemInfo.NbBiQuadTriangles()),
728                                              SMDSAbs_Face));
729     aTElemTypeDatas.push_back( TElemTypeData(anEntity,
730                                              eQUAD4,
731                                              FromSmIdType<TInt>(nbElemInfo.NbQuadrangles( ORDER_LINEAR )),
732                                              SMDSAbs_Face));
733     aTElemTypeDatas.push_back( TElemTypeData(anEntity,
734                                              eQUAD8,
735                                              FromSmIdType<TInt>(nbElemInfo.NbQuadrangles( ORDER_QUADRATIC ) -
736                                              nbElemInfo.NbBiQuadQuadrangles()),
737                                              SMDSAbs_Face));
738     aTElemTypeDatas.push_back( TElemTypeData(anEntity,
739                                              eQUAD9,
740                                              FromSmIdType<TInt>(nbElemInfo.NbBiQuadQuadrangles()),
741                                              SMDSAbs_Face));
742     if ( polyTypesSupported ) {
743       aTElemTypeDatas.push_back( TElemTypeData(anEntity,
744                                                ePOLYGONE,
745                                                FromSmIdType<TInt>(nbElemInfo.NbPolygons( ORDER_LINEAR )),
746                                                SMDSAbs_Face));
747       // we need one more loop on poly elements to count nb of their nodes
748       aTElemTypeDatas.push_back( TElemTypeData(anEntity,
749                                                ePOLYGONE,
750                                                FromSmIdType<TInt>(nbElemInfo.NbPolygons( ORDER_LINEAR )),
751                                                SMDSAbs_Face));
752       aTElemTypeDatas.push_back( TElemTypeData(anEntity,
753                                                ePOLYGON2,
754                                                FromSmIdType<TInt>(nbElemInfo.NbPolygons( ORDER_QUADRATIC )),
755                                                SMDSAbs_Face));
756       // we need one more loop on QUAD poly elements to count nb of their nodes
757       aTElemTypeDatas.push_back( TElemTypeData(anEntity,
758                                                ePOLYGON2,
759                                                FromSmIdType<TInt>(nbElemInfo.NbPolygons( ORDER_QUADRATIC )),
760                                                SMDSAbs_Face));
761     }
762 #ifdef _ELEMENTS_BY_DIM_
763     anEntity = eMAILLE;
764 #endif
765     aTElemTypeDatas.push_back( TElemTypeData(anEntity,
766                                              eTETRA4,
767                                              FromSmIdType<TInt>(nbElemInfo.NbTetras( ORDER_LINEAR )),
768                                              SMDSAbs_Volume));
769     aTElemTypeDatas.push_back( TElemTypeData(anEntity,
770                                              eTETRA10,
771                                              FromSmIdType<TInt>(nbElemInfo.NbTetras( ORDER_QUADRATIC )),
772                                              SMDSAbs_Volume));
773     aTElemTypeDatas.push_back( TElemTypeData(anEntity,
774                                              ePYRA5,
775                                              FromSmIdType<TInt>(nbElemInfo.NbPyramids( ORDER_LINEAR )),
776                                              SMDSAbs_Volume));
777     aTElemTypeDatas.push_back( TElemTypeData(anEntity,
778                                              ePYRA13,
779                                              FromSmIdType<TInt>(nbElemInfo.NbPyramids( ORDER_QUADRATIC )),
780                                              SMDSAbs_Volume));
781     aTElemTypeDatas.push_back( TElemTypeData(anEntity,
782                                              ePENTA6,
783                                              FromSmIdType<TInt>(nbElemInfo.NbPrisms( ORDER_LINEAR )),
784                                              SMDSAbs_Volume));
785     aTElemTypeDatas.push_back( TElemTypeData(anEntity,
786                                              ePENTA15,
787                                              FromSmIdType<TInt>(nbElemInfo.NbQuadPrisms()),
788                                              SMDSAbs_Volume));
789     aTElemTypeDatas.push_back( TElemTypeData(anEntity,
790                                              ePENTA18,
791                                              FromSmIdType<TInt>(nbElemInfo.NbBiQuadPrisms()),
792                                              SMDSAbs_Volume));
793     aTElemTypeDatas.push_back( TElemTypeData(anEntity,
794                                              eHEXA8,
795                                              FromSmIdType<TInt>(nbElemInfo.NbHexas( ORDER_LINEAR )),
796                                              SMDSAbs_Volume));
797     aTElemTypeDatas.push_back( TElemTypeData(anEntity,
798                                              eHEXA20,
799                                              FromSmIdType<TInt>(nbElemInfo.NbHexas( ORDER_QUADRATIC )-
800                                              nbElemInfo.NbTriQuadHexas()),
801                                              SMDSAbs_Volume));
802     aTElemTypeDatas.push_back( TElemTypeData(anEntity,
803                                              eHEXA27,
804                                              FromSmIdType<TInt>(nbElemInfo.NbTriQuadHexas()),
805                                              SMDSAbs_Volume));
806     aTElemTypeDatas.push_back( TElemTypeData(anEntity,
807                                              eOCTA12,
808                                              FromSmIdType<TInt>(nbElemInfo.NbHexPrisms()),
809                                              SMDSAbs_Volume));
810     if ( polyTypesSupported ) {
811       aTElemTypeDatas.push_back( TElemTypeData(anEntity,
812                                                ePOLYEDRE,
813                                                FromSmIdType<TInt>(nbElemInfo.NbPolyhedrons()),
814                                                SMDSAbs_Volume));
815       // we need one more loop on poly elements to count nb of their nodes
816       aTElemTypeDatas.push_back( TElemTypeData(anEntity,
817                                                ePOLYEDRE,
818                                                FromSmIdType<TInt>(nbElemInfo.NbPolyhedrons()),
819                                                SMDSAbs_Volume));
820     }
821
822     vector< bool > isElemFamMapBuilt( SMDSAbs_NbElementTypes, false );
823
824     // loop on all geom types of elements
825
826     list< TElemTypeData >::iterator aElemTypeData = aTElemTypeDatas.begin();
827     for ( ; aElemTypeData != aTElemTypeDatas.end(); ++aElemTypeData )
828     {
829       if ( aElemTypeData->_nbElems == 0 )
830         continue;
831
832       int defaultFamilyId = 0;
833       switch ( aElemTypeData->_smdsType ) {
834       case SMDSAbs_0DElement: defaultFamilyId = my0DElementsDefaultFamilyId; break;
835       case SMDSAbs_Ball:      defaultFamilyId = myBallsDefaultFamilyId;      break;
836       case SMDSAbs_Edge:      defaultFamilyId = myEdgesDefaultFamilyId;      break;
837       case SMDSAbs_Face:      defaultFamilyId = myFacesDefaultFamilyId;      break;
838       case SMDSAbs_Volume:    defaultFamilyId = myVolumesDefaultFamilyId;    break;
839       default:
840         continue;
841       }
842
843       // build map of family numbers for this type
844       if ( !isElemFamMapBuilt[ aElemTypeData->_smdsType ])
845       {
846         fillElemFamilyMap( anElemFamMap, aFamilies, aElemTypeData->_smdsType );
847         isElemFamMapBuilt[ aElemTypeData->_smdsType ] = true;
848       }
849
850       // iterator on elements of a current type
851       SMDS_ElemIteratorPtr elemIterator;
852       TInt iElem = 0;
853
854       // Treat POLYGONs
855       // ---------------
856       if ( aElemTypeData->_geomType == ePOLYGONE ||
857            aElemTypeData->_geomType == ePOLYGON2 )
858       {
859         if ( aElemTypeData->_geomType == ePOLYGONE )
860           elemIterator = myMesh->elementEntityIterator( SMDSEntity_Polygon );
861         else
862           elemIterator = myMesh->elementEntityIterator( SMDSEntity_Quad_Polygon );
863
864         if ( nbPolygonNodes == 0 ) {
865           // Count nb of nodes
866           while ( elemIterator->more() ) {
867             const SMDS_MeshElement* anElem = elemIterator->next();
868             nbPolygonNodes += anElem->NbNodes();
869             if ( ++iElem == aElemTypeData->_nbElems )
870               break;
871           }
872         }
873         else {
874           // Store in med file
875           PPolygoneInfo aPolygoneInfo = myMed->CrPolygoneInfo(aMeshInfo,
876                                                               aElemTypeData->_entity,
877                                                               aElemTypeData->_geomType,
878                                                               aElemTypeData->_nbElems,
879                                                               nbPolygonNodes,
880                                                               theConnMode, theIsElemNum,
881                                                               theIsElemNames);
882           TElemNum & index = *(aPolygoneInfo->myIndex.get());
883           index[0] = 1;
884
885           while ( elemIterator->more() )
886           {
887             const SMDS_MeshElement* anElem = elemIterator->next();
888             // index
889             TInt aNbNodes = anElem->NbNodes();
890             index[ iElem+1 ] = index[ iElem ] + aNbNodes;
891
892             // connectivity
893             TConnSlice aTConnSlice = aPolygoneInfo->GetConnSlice( iElem );
894             for(TInt iNode = 0; iNode < aNbNodes; iNode++) {
895               const SMDS_MeshElement* aNode = anElem->GetNode( iNode );
896 #ifdef _EDF_NODE_IDS_
897               aTConnSlice[ iNode ] = aNodeIdMap[FromSmIdType<TInt>(aNode->GetID())];
898 #else
899               aTConnSlice[ iNode ] = aNode->GetID();
900 #endif
901             }
902             // element number
903             aPolygoneInfo->SetElemNum( iElem, FromSmIdType<TInt>(anElem->GetID()) );
904
905             // family number
906             int famNum = getFamilyId( anElemFamMap, anElem, defaultFamilyId );
907             aPolygoneInfo->SetFamNum( iElem, famNum );
908
909             if ( ++iElem == aPolygoneInfo->GetNbElem() )
910               break;
911           }
912           myMed->SetPolygoneInfo(aPolygoneInfo);
913
914           nbPolygonNodes = 0; // to treat next polygon type
915         }
916       }
917
918       // Treat POLYEDREs
919       // ----------------
920       else if ( aElemTypeData->_geomType == ePOLYEDRE )
921       {
922         elemIterator = myMesh->elementGeomIterator( SMDSGeom_POLYHEDRA );
923
924         if ( nbPolyhedronNodes == 0 ) {
925           // Count nb of nodes
926           while ( elemIterator->more() ) {
927             const SMDS_MeshElement*  anElem = elemIterator->next();
928             nbPolyhedronNodes += anElem->NbNodes();
929             nbPolyhedronFaces += anElem->NbFaces();
930             if ( ++iElem == aElemTypeData->_nbElems )
931               break;
932           }
933         }
934         else {
935           // Store in med file
936           PPolyedreInfo aPolyhInfo = myMed->CrPolyedreInfo(aMeshInfo,
937                                                            aElemTypeData->_entity,
938                                                            aElemTypeData->_geomType,
939                                                            aElemTypeData->_nbElems,
940                                                            nbPolyhedronFaces+1,
941                                                            nbPolyhedronNodes,
942                                                            theConnMode,
943                                                            theIsElemNum,
944                                                            theIsElemNames);
945           TElemNum & index = *(aPolyhInfo->myIndex.get());
946           TElemNum & faces = *(aPolyhInfo->myFaces.get());
947           TElemNum & conn  = *(aPolyhInfo->myConn.get());
948           index[0] = 1;
949           faces[0] = 1;
950
951           TInt iFace = 0, iNode = 0;
952           while ( elemIterator->more() )
953           {
954             const SMDS_MeshElement* anElem = elemIterator->next();
955             const SMDS_MeshVolume *aPolyedre = myMesh->DownCast< SMDS_MeshVolume >( anElem );
956             if ( !aPolyedre ) continue;
957             // index
958             TInt aNbFaces = aPolyedre->NbFaces();
959             index[ iElem+1 ] = index[ iElem ] + aNbFaces;
960
961             // face index
962             for (TInt f = 1; f <= aNbFaces; ++f, ++iFace ) {
963               int aNbFaceNodes = aPolyedre->NbFaceNodes( f );
964               faces[ iFace+1 ] = faces[ iFace ] + aNbFaceNodes;
965             }
966             // connectivity
967             SMDS_ElemIteratorPtr nodeIt = anElem->nodesIterator();
968             while ( nodeIt->more() ) {
969               const SMDS_MeshElement* aNode = nodeIt->next();
970 #ifdef _EDF_NODE_IDS_
971               conn[ iNode ] = aNodeIdMap[FromSmIdType<TInt>(aNode->GetID())];
972 #else
973               conn[ iNode ] = aNode->GetID();
974 #endif
975               ++iNode;
976             }
977             // element number
978             aPolyhInfo->SetElemNum( iElem, FromSmIdType<TInt>(anElem->GetID()) );
979
980             // family number
981             int famNum = getFamilyId( anElemFamMap, anElem, defaultFamilyId );
982             aPolyhInfo->SetFamNum( iElem, famNum );
983
984             if ( ++iElem == aPolyhInfo->GetNbElem() )
985               break;
986           }
987           myMed->SetPolyedreInfo(aPolyhInfo);
988         }
989       } // if (aElemTypeData->_geomType == ePOLYEDRE )
990
991       // Treat BALLs
992       // ----------------
993       else if (aElemTypeData->_geomType == eBALL )
994       {
995         // allocate data arrays
996         PBallInfo aBallInfo = myMed->CrBallInfo( aMeshInfo, aElemTypeData->_nbElems );
997
998         elemIterator = myMesh->elementsIterator( SMDSAbs_Ball );
999         while ( elemIterator->more() )
1000         {
1001           const SMDS_MeshElement* anElem = elemIterator->next();
1002           // connectivity
1003           const SMDS_MeshElement* aNode = anElem->GetNode( 0 );
1004 #ifdef _EDF_NODE_IDS_
1005           (*aBallInfo->myConn)[ iElem ] = aNodeIdMap[FromSmIdType<TInt>(aNode->GetID())];
1006 #else
1007           (*aBallInfo->myConn)[ iElem ] = aNode->GetID();
1008 #endif
1009           // element number
1010           aBallInfo->SetElemNum( iElem, FromSmIdType<TInt>(anElem->GetID()) );
1011
1012           // diameter
1013           aBallInfo->myDiameters[ iElem ] =
1014             static_cast<const SMDS_BallElement*>( anElem )->GetDiameter();
1015
1016           // family number
1017           int famNum = getFamilyId( anElemFamMap, anElem, defaultFamilyId );
1018           aBallInfo->SetFamNum( iElem, famNum );
1019           ++iElem;
1020         }
1021         // store data in a file
1022         myMed->SetBallInfo(aBallInfo);
1023       }
1024
1025       else
1026       {
1027         // Treat standard types
1028         // ---------------------
1029         // allocate data arrays
1030         PCellInfo aCellInfo = myMed->CrCellInfo( aMeshInfo,
1031                                                  aElemTypeData->_entity,
1032                                                  aElemTypeData->_geomType,
1033                                                  aElemTypeData->_nbElems,
1034                                                  theConnMode,
1035                                                  theIsElemNum,
1036                                                  theIsElemNames);
1037
1038         TInt aNbNodes = MED::GetNbNodes(aElemTypeData->_geomType);
1039
1040         elemIterator = myMesh->elementsIterator( aElemTypeData->_smdsType );
1041         if ( aElemTypeData->_smdsType == SMDSAbs_0DElement && ! nodesOf0D.empty() )
1042           elemIterator = iterVecIter;
1043
1044         while ( elemIterator->more() )
1045         {
1046           const SMDS_MeshElement* anElem = elemIterator->next();
1047           if ( anElem->NbNodes() != aNbNodes || anElem->IsPoly() )
1048             continue; // other geometry
1049
1050           // connectivity
1051           TConnSlice aTConnSlice = aCellInfo->GetConnSlice( iElem );
1052           for (TInt iNode = 0; iNode < aNbNodes; iNode++) {
1053             const SMDS_MeshElement* aNode = anElem->GetNode( iNode );
1054 #ifdef _EDF_NODE_IDS_
1055             aTConnSlice[ iNode ] = aNodeIdMap[FromSmIdType<TInt>(aNode->GetID())];
1056 #else
1057             aTConnSlice[ iNode ] = aNode->GetID();
1058 #endif
1059           }
1060           // element number
1061           aCellInfo->SetElemNum( iElem, FromSmIdType<TInt>(anElem->GetID()) );
1062
1063           // family number
1064           int famNum = getFamilyId( anElemFamMap, anElem, defaultFamilyId );
1065           aCellInfo->SetFamNum( iElem, famNum );
1066
1067           if ( ++iElem == aCellInfo->GetNbElem() )
1068             break;
1069         }
1070         // fix numbers of added SMDSAbs_0DElement
1071         if ( aElemTypeData->_smdsType == SMDSAbs_0DElement && ! nodesOf0D.empty() )
1072         {
1073           iElem = myMesh->Nb0DElements();
1074           TInt elem0DNum = FromSmIdType<TInt>( myMesh->MaxElementID() + 1 );
1075           for ( size_t i = 0; i < nodesOf0D.size(); ++i )
1076             aCellInfo->SetElemNum( iElem++, elem0DNum++);
1077         }
1078
1079         // store data in a file
1080         myMed->SetCellInfo(aCellInfo);
1081       }
1082     } // loop on geom types
1083   }
1084   catch(const std::exception& exc) {
1085     INFOS("The following exception was caught:\n\t"<<exc.what());
1086     throw;
1087   }
1088   catch(...) {
1089     INFOS("Unknown exception was caught !!!");
1090     throw;
1091   }
1092
1093   myMeshId = -1;
1094   myGroups.clear();
1095   mySubMeshes.clear();
1096   return aResult;
1097 }
1098
1099 //================================================================================
1100 /*!
1101  * \brief Returns nodes on VERTEXes where 0D elements are absent
1102  */
1103 //================================================================================
1104
1105 bool DriverMED_W_SMESHDS_Mesh::
1106 getNodesOfMissing0DOnVert(SMESHDS_Mesh*                         meshDS,
1107                           std::vector<const SMDS_MeshElement*>& nodes)
1108 {
1109   nodes.clear();
1110   for ( int i = 1; i <= meshDS->MaxShapeIndex(); ++i )
1111   {
1112     if ( meshDS->IndexToShape( i ).ShapeType() != TopAbs_VERTEX )
1113       continue;
1114     if ( SMESHDS_SubMesh* sm = meshDS->MeshElements(i) ) {
1115       SMDS_NodeIteratorPtr nIt= sm->GetNodes();
1116       while (nIt->more())
1117       {
1118         const SMDS_MeshNode* n = nIt->next();
1119         if ( n->NbInverseElements( SMDSAbs_0DElement ) == 0 )
1120           nodes.push_back( n );
1121       }
1122     }
1123   }
1124   return !nodes.empty();
1125 }