Salome HOME
[SALOME platform 0013410]: SubMesh not taken into account with Netgen 1D-2D et 1D...
[plugins/netgenplugin.git] / src / NETGENPlugin / NETGENPlugin_Mesher.cxx
1 //  NETGENPlugin : C++ implementation
2 //
3 //  Copyright (C) 2006  OPEN CASCADE, CEA/DEN, EDF R&D
4 // 
5 //  This library is free software; you can redistribute it and/or 
6 //  modify it under the terms of the GNU Lesser General Public 
7 //  License as published by the Free Software Foundation; either 
8 //  version 2.1 of the License. 
9 // 
10 //  This library is distributed in the hope that it will be useful, 
11 //  but WITHOUT ANY WARRANTY; without even the implied warranty of 
12 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
13 //  Lesser General Public License for more details. 
14 // 
15 //  You should have received a copy of the GNU Lesser General Public 
16 //  License along with this library; if not, write to the Free Software 
17 //  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA 
18 // 
19 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
20 //
21 //
22 // File      : NETGENPlugin_Mesher.cxx
23 // Author    : Michael Sazonov (OCN)
24 // Date      : 31/03/2006
25 // Project   : SALOME
26 // $Header$
27 //=============================================================================
28
29 #include "NETGENPlugin_Mesher.hxx"
30 #include "NETGENPlugin_Hypothesis_2D.hxx"
31 #include "NETGENPlugin_SimpleHypothesis_3D.hxx"
32
33 #include <SMESH_Mesh.hxx>
34 #include <SMESH_Comment.hxx>
35 #include <SMESH_ComputeError.hxx>
36 #include <SMESH_subMesh.hxx>
37 #include <SMESH_MesherHelper.hxx>
38 #include <SMESHDS_Mesh.hxx>
39 #include <SMDS_MeshElement.hxx>
40 #include <SMDS_MeshNode.hxx>
41 #include <utilities.h>
42
43 #include <vector>
44
45 #include <BRep_Tool.hxx>
46 #include <TopExp.hxx>
47 #include <TopExp_Explorer.hxx>
48 #include <TopoDS.hxx>
49 #include <NCollection_Map.hxx>
50 #include <OSD_Path.hxx>
51 #include <OSD_File.hxx>
52 #include <TCollection_AsciiString.hxx>
53 #include <TopTools_ListIteratorOfListOfShape.hxx>
54
55 // Netgen include files
56 namespace nglib {
57 #include <nglib.h>
58 }
59 #define OCCGEOMETRY
60 #include <occgeom.hpp>
61 #include <meshing.hpp>
62 //#include <ngexception.hpp>
63 namespace netgen {
64   extern int OCCGenerateMesh (OCCGeometry&, Mesh*&, int, int, char*);
65   extern MeshingParameters mparam;
66 }
67
68 using namespace std;
69
70 //=============================================================================
71 /*!
72  *
73  */
74 //=============================================================================
75
76 NETGENPlugin_Mesher::NETGENPlugin_Mesher (SMESH_Mesh* mesh,
77                                           const TopoDS_Shape& aShape,
78                                           const bool isVolume)
79   : _mesh    (mesh),
80     _shape   (aShape),
81     _isVolume(isVolume),
82     _optimize(true),
83     _simpleHyp(NULL)
84 {
85   defaultParameters();
86 }
87
88 //================================================================================
89 /*!
90  * \brief Initialize global NETGEN parameters with default values
91  */
92 //================================================================================
93
94 void NETGENPlugin_Mesher::defaultParameters()
95 {
96 #ifdef WNT
97   netgen::MeshingParameters& mparams = netgen::GlobalMeshingParameters();
98 #else
99   netgen::MeshingParameters& mparams = netgen::mparam;
100 #endif
101   // maximal mesh edge size
102   mparams.maxh = NETGENPlugin_Hypothesis::GetDefaultMaxSize();
103   // minimal number of segments per edge
104   mparams.segmentsperedge = NETGENPlugin_Hypothesis::GetDefaultNbSegPerEdge();
105   // rate of growth of size between elements
106   mparams.grading = NETGENPlugin_Hypothesis::GetDefaultGrowthRate();
107   // safety factor for curvatures (elements per radius)
108   mparams.curvaturesafety = NETGENPlugin_Hypothesis::GetDefaultNbSegPerRadius();
109   // create elements of second order
110   mparams.secondorder = NETGENPlugin_Hypothesis::GetDefaultSecondOrder() ? 1 : 0;
111   // quad-dominated surface meshing
112   if (_isVolume)
113     mparams.quad = 0;
114   else
115     mparams.quad = NETGENPlugin_Hypothesis_2D::GetDefaultQuadAllowed() ? 1 : 0;
116 }
117
118 //=============================================================================
119 /*!
120  * Pass parameters to NETGEN
121  */
122 //=============================================================================
123 void NETGENPlugin_Mesher::SetParameters(const NETGENPlugin_Hypothesis* hyp)
124 {
125   if (hyp)
126   {
127 #ifdef WNT
128     netgen::MeshingParameters& mparams = netgen::GlobalMeshingParameters();
129 #else
130     netgen::MeshingParameters& mparams = netgen::mparam;
131 #endif
132     // Initialize global NETGEN parameters:
133     // maximal mesh segment size
134     mparams.maxh = hyp->GetMaxSize();
135     // minimal number of segments per edge
136     mparams.segmentsperedge = hyp->GetNbSegPerEdge();
137     // rate of growth of size between elements
138     mparams.grading = hyp->GetGrowthRate();
139     // safety factor for curvatures (elements per radius)
140     mparams.curvaturesafety = hyp->GetNbSegPerRadius();
141     // create elements of second order
142     mparams.secondorder = hyp->GetSecondOrder() ? 1 : 0;
143     // quad-dominated surface meshing
144     // only triangles are allowed for volumic mesh
145     if (!_isVolume)
146       mparams.quad = static_cast<const NETGENPlugin_Hypothesis_2D*>
147         (hyp)->GetQuadAllowed() ? 1 : 0;
148     _optimize = hyp->GetOptimize();
149     _simpleHyp = NULL;
150   }
151 }
152
153 //=============================================================================
154 /*!
155  * Pass simple parameters to NETGEN
156  */
157 //=============================================================================
158
159 void NETGENPlugin_Mesher::SetParameters(const NETGENPlugin_SimpleHypothesis_2D* hyp)
160 {
161   _simpleHyp = hyp;
162   if ( _simpleHyp )
163     defaultParameters();
164 }
165
166 //=============================================================================
167 /*!
168  *  Link - a pair of integer numbers
169  */
170 //=============================================================================
171 struct Link
172 {
173   int n1, n2;
174   Link(int _n1, int _n2) : n1(_n1), n2(_n2) {}
175   Link() : n1(0), n2(0) {}
176 };
177
178 int HashCode(const Link& aLink, int aLimit)
179 {
180   return HashCode(aLink.n1 + aLink.n2, aLimit);
181 }
182
183 Standard_Boolean IsEqual(const Link& aLink1, const Link& aLink2)
184 {
185   return (aLink1.n1 == aLink2.n1 && aLink1.n2 == aLink2.n2 ||
186           aLink1.n1 == aLink2.n2 && aLink1.n2 == aLink2.n1);
187 }
188
189 //================================================================================
190 /*!
191  * \brief Initialize netgen::OCCGeometry with OCCT shape
192  */
193 //================================================================================
194
195 void NETGENPlugin_Mesher::PrepareOCCgeometry(netgen::OCCGeometry&     occgeo,
196                                              const TopoDS_Shape&      shape,
197                                              SMESH_Mesh&              mesh,
198                                              list< SMESH_subMesh* > * meshedSM)
199 {
200   BRepTools::Clean (shape);
201   BRepMesh_IncrementalMesh::BRepMesh_IncrementalMesh (shape, 0.01, true);
202   Bnd_Box bb;
203   BRepBndLib::Add (shape, bb);
204   double x1,y1,z1,x2,y2,z2;
205   bb.Get (x1,y1,z1,x2,y2,z2);
206   MESSAGE("shape bounding box:\n" <<
207           "(" << x1 << " " << y1 << " " << z1 << ") " <<
208           "(" << x2 << " " << y2 << " " << z2 << ")");
209   netgen::Point<3> p1 = netgen::Point<3> (x1,y1,z1);
210   netgen::Point<3> p2 = netgen::Point<3> (x2,y2,z2);
211   occgeo.boundingbox = netgen::Box<3> (p1,p2);
212
213   occgeo.shape = shape;
214   occgeo.changed = 1;
215   //occgeo.BuildFMap();  
216
217   // fill maps of shapes of occgeo with not yet meshed subshapes
218
219   // get root submeshes
220   list< SMESH_subMesh* > rootSM;
221   if ( SMESH_subMesh* sm = mesh.GetSubMeshContaining( shape )) {
222     rootSM.push_back( sm );
223   }
224   else {
225     for ( TopoDS_Iterator it( shape ); it.More(); it.Next() )
226       rootSM.push_back( mesh.GetSubMesh( it.Value() ));
227   }
228
229   // add subshapes of empty submeshes
230   list< SMESH_subMesh* >::iterator rootIt = rootSM.begin(), rootEnd = rootSM.end();
231   for ( ; rootIt != rootEnd; ++rootIt ) {
232     SMESH_subMesh * root = *rootIt;
233     SMESH_subMeshIteratorPtr smIt = root->getDependsOnIterator(/*includeSelf=*/true,
234                                                                /*complexShapeFirst=*/true);
235     while ( smIt->more() ) {
236       SMESH_subMesh* sm = smIt->next();
237       if ( sm->IsEmpty() ) {
238         switch ( sm->GetSubShape().ShapeType() ) {
239         case TopAbs_FACE  : occgeo.fmap.Add( sm->GetSubShape() ); break;
240         case TopAbs_EDGE  : occgeo.emap.Add( sm->GetSubShape() ); break;
241         case TopAbs_VERTEX: occgeo.vmap.Add( sm->GetSubShape() ); break;
242         case TopAbs_SOLID :occgeo.somap.Add( sm->GetSubShape() ); break;
243         default:;
244         }
245       }
246       // collect submeshes of meshed shapes
247       else if (meshedSM) {
248         meshedSM->push_back( sm );
249       }
250     }
251   }
252   occgeo.facemeshstatus.SetSize (occgeo.fmap.Extent());
253   occgeo.facemeshstatus = 0;
254
255 }
256
257 //================================================================================
258 /*!
259  * \brief return id of netgen point corresponding to SMDS node
260  */
261 //================================================================================
262
263 static int ngNodeId( const SMDS_MeshNode*              node,
264                      netgen::Mesh&                     ngMesh,
265                      map< const SMDS_MeshNode*, int >& nodeNgIdMap)
266 {
267   int newNgId = ngMesh.GetNP() + 1;
268
269   pair< map< const SMDS_MeshNode*, int >::iterator, bool > it_isNew =
270     nodeNgIdMap.insert( make_pair( node, newNgId ));
271
272   if ( it_isNew.second ) {
273     netgen::MeshPoint p( netgen::Point<3> (node->X(), node->Y(), node->Z()) );
274     ngMesh.AddPoint( p );
275   }
276   return it_isNew.first->second;
277 }
278
279 //================================================================================
280 /*!
281  * \brief fill ngMesh with nodes and elements of computed submeshes
282  */
283 //================================================================================
284
285 bool NETGENPlugin_Mesher::fillNgMesh(netgen::OCCGeometry&           occgeom,
286                                      netgen::Mesh&                  ngMesh,
287                                      vector<SMDS_MeshNode*>&        nodeVec,
288                                      const list< SMESH_subMesh* > & meshedSM)
289 {
290   map< const SMDS_MeshNode*, int > nodeNgIdMap;
291
292   TopTools_MapOfShape visitedShapes;
293
294   SMESH_MesherHelper helper (*_mesh);
295
296   int faceID = occgeom.fmap.Extent();
297   _faceDescriptors.clear();
298
299   list< SMESH_subMesh* >::const_iterator smIt, smEnd = meshedSM.end();
300   for ( smIt = meshedSM.begin(); smIt != smEnd; ++smIt )
301   {
302     SMESH_subMesh* sm = *smIt;
303     if ( !visitedShapes.Add( sm->GetSubShape() ))
304       continue;
305
306     SMESHDS_SubMesh * smDS = sm->GetSubMeshDS();
307
308     switch ( sm->GetSubShape().ShapeType() )
309     {
310     case TopAbs_EDGE: { // EDGE
311       // ----------------------
312       const TopoDS_Edge& geomEdge  = TopoDS::Edge( sm->GetSubShape() );
313
314       // Add ng segments for each not meshed face the edge bounds
315       TopTools_MapOfShape visitedAncestors;
316       const TopTools_ListOfShape& ancestors = _mesh->GetAncestors( geomEdge );
317       TopTools_ListIteratorOfListOfShape ancestorIt ( ancestors );
318       for ( ; ancestorIt.More(); ancestorIt.Next() )
319       {
320         const TopoDS_Shape & ans = ancestorIt.Value();
321         if ( ans.ShapeType() != TopAbs_FACE || !visitedAncestors.Add( ans ))
322           continue;
323         const TopoDS_Face& face = TopoDS::Face( ans );
324
325         int faceID = occgeom.fmap.FindIndex( face );
326         if ( faceID < 1 )
327           continue; // meshed face
328
329         // find out orientation of geomEdge within face
330         bool isForwad = false;
331         for ( TopExp_Explorer exp( face, TopAbs_EDGE ); exp.More(); exp.Next() ) {
332           if ( geomEdge.IsSame( exp.Current() )) {
333             isForwad = ( exp.Current().Orientation() == geomEdge.Orientation() );
334             break;
335           }
336         }
337         bool isQuad = smDS->GetElements()->next()->IsQuadratic();
338
339         // get all nodes from geomEdge
340         StdMeshers_FaceSide fSide( face, geomEdge, _mesh, isForwad, isQuad );
341         const vector<UVPtStruct>& points = fSide.GetUVPtStruct();
342         int i, nbSeg = fSide.NbSegments();
343
344         double otherSeamParam = 0;
345         helper.SetSubShape( face );
346         bool isSeam = helper.IsRealSeam( geomEdge );
347         if ( isSeam )
348           otherSeamParam =
349             helper.GetOtherParam( helper.GetPeriodicIndex() == 1 ? points[0].u : points[0].v );
350
351         // add segments
352
353         int prevNgId = ngNodeId( points[0].node, ngMesh, nodeNgIdMap );
354
355         for ( i = 0; i < nbSeg; ++i )
356         {
357           const UVPtStruct& p1 = points[ i ];
358           const UVPtStruct& p2 = points[ i+1 ];
359
360           netgen::Segment seg;
361           // ng node ids
362           seg.p1 = prevNgId;
363           seg.p2 = prevNgId = ngNodeId( p2.node, ngMesh, nodeNgIdMap );
364           // node param on curve
365           seg.epgeominfo[ 0 ].dist = p1.param;
366           seg.epgeominfo[ 1 ].dist = p2.param;
367           // uv on face
368           seg.epgeominfo[ 0 ].u = p1.u;
369           seg.epgeominfo[ 0 ].v = p1.v;
370           seg.epgeominfo[ 1 ].u = p2.u;
371           seg.epgeominfo[ 1 ].v = p2.v;
372
373           //seg.epgeominfo[ iEnd ].edgenr = edgeID; //  = geom.emap.FindIndex(edge);
374           seg.si = faceID;                   // = geom.fmap.FindIndex (face);
375           seg.edgenr = ngMesh.GetNSeg() + 1; // segment id
376           ngMesh.AddSegment (seg);
377
378           if ( isSeam )
379           {
380             if ( helper.GetPeriodicIndex() == 1 ) {
381               seg.epgeominfo[ 0 ].u = otherSeamParam;
382               seg.epgeominfo[ 1 ].u = otherSeamParam;
383               swap (seg.epgeominfo[0].v, seg.epgeominfo[1].v);
384             } else {
385               seg.epgeominfo[ 0 ].v = otherSeamParam;
386               seg.epgeominfo[ 1 ].v = otherSeamParam;
387               swap (seg.epgeominfo[0].u, seg.epgeominfo[1].u);
388             }
389             swap (seg.p1, seg.p2);
390             swap (seg.epgeominfo[0].dist, seg.epgeominfo[1].dist);
391             seg.edgenr = ngMesh.GetNSeg() + 1; // segment id
392             ngMesh.AddSegment (seg);
393           }
394         }
395       } // loop on geomEdge ancestors
396
397       break;
398     } // case TopAbs_EDGE
399
400     case TopAbs_FACE: { // FACE
401       // ----------------------
402       const TopoDS_Face& geomFace  = TopoDS::Face( sm->GetSubShape() );
403       helper.SetSubShape( geomFace );
404
405       // find solids geomFace bounds
406       int solidID1 = 0, solidID2 = 0;
407       const TopTools_ListOfShape& ancestors = _mesh->GetAncestors( geomFace );
408       TopTools_ListIteratorOfListOfShape ancestorIt ( ancestors );
409       for ( ; ancestorIt.More(); ancestorIt.Next() )
410       {
411         const TopoDS_Shape & solid = ancestorIt.Value();
412         if ( solid.ShapeType() == TopAbs_SOLID  ) {
413           int id = occgeom.somap.FindIndex ( solid );
414           if ( solidID1 && id != solidID1 ) solidID2 = id;
415           else                              solidID1 = id;
416         }
417       }
418       faceID++;
419       _faceDescriptors[ faceID ].first  = solidID1;
420       _faceDescriptors[ faceID ].second = solidID2;
421
422       // add surface elements
423       SMDS_ElemIteratorPtr faces = smDS->GetElements();
424       while ( faces->more() ) {
425
426         const SMDS_MeshElement* f = faces->next();
427         if ( f->NbNodes() % 3 != 0 ) { // not triangle
428           for ( ancestorIt.Initialize(ancestors); ancestorIt.More(); ancestorIt.Next() )
429             if ( ancestorIt.Value().ShapeType() == TopAbs_SOLID  ) {
430               sm = _mesh->GetSubMesh( ancestorIt.Value() );
431               break;
432             }
433           SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
434           smError.reset( new SMESH_ComputeError(COMPERR_BAD_INPUT_MESH,"Not triangle submesh"));
435           smError->myBadElements.push_back( f );
436           return false;
437         }
438
439         netgen::Element2d tri(3);
440         tri.SetIndex ( faceID );
441
442         for ( int i = 0; i < 3; ++i ) {
443           const SMDS_MeshNode* node = f->GetNode( i ), * inFaceNode=0;
444           if ( helper.IsSeamShape( node->GetPosition()->GetShapeId() ))
445             if ( helper.IsSeamShape( f->GetNode( i+1 )->GetPosition()->GetShapeId() ))
446               inFaceNode = f->GetNode( i-1 );
447             else 
448               inFaceNode = f->GetNode( i+1 );
449             
450           gp_XY uv = helper.GetNodeUV( geomFace, node, inFaceNode );
451           tri.GeomInfoPi(i+1).u = uv.X();
452           tri.GeomInfoPi(i+1).v = uv.Y();
453           tri.PNum(i+1) = ngNodeId( node, ngMesh, nodeNgIdMap );
454         }
455
456         ngMesh.AddSurfaceElement (tri);
457
458       }
459       break;
460     } //
461
462     case TopAbs_VERTEX: { // VERTEX
463       // --------------------------
464       SMDS_NodeIteratorPtr nodeIt = smDS->GetNodes();
465       if ( nodeIt->more() )
466         ngNodeId( nodeIt->next(), ngMesh, nodeNgIdMap );
467       break;
468     }
469     default:;
470     } // switch
471   } // loop on submeshes
472
473   // fill nodeVec
474   nodeVec.resize( ngMesh.GetNP() + 1 );
475   map< const SMDS_MeshNode*, int >::iterator node_NgId, nodeNgIdEnd = nodeNgIdMap.end();
476   for ( node_NgId = nodeNgIdMap.begin(); node_NgId != nodeNgIdEnd; ++node_NgId)
477     nodeVec[ node_NgId->second ] = (SMDS_MeshNode*) node_NgId->first;
478
479   return true;
480 }
481
482 //=============================================================================
483 /*!
484  * Here we are going to use the NETGEN mesher
485  */
486 //=============================================================================
487 bool NETGENPlugin_Mesher::Compute()
488 {
489 #ifdef WNT
490   netgen::MeshingParameters& mparams = netgen::GlobalMeshingParameters();
491 #else
492   netgen::MeshingParameters& mparams = netgen::mparam;
493 #endif  
494   MESSAGE("Compute with:\n"
495           " max size = " << mparams.maxh << "\n"
496           " segments per edge = " << mparams.segmentsperedge);
497   MESSAGE("\n"
498           " growth rate = " << mparams.grading << "\n"
499           " elements per radius = " << mparams.curvaturesafety << "\n"
500           " second order = " << mparams.secondorder << "\n"
501           " quad allowed = " << mparams.quad);
502
503   SMESH_ComputeErrorPtr error = SMESH_ComputeError::New();
504   nglib::Ng_Init();
505
506   // -------------------------
507   // Prepare OCC geometry
508   // -------------------------
509
510   netgen::OCCGeometry occgeo;
511   list< SMESH_subMesh* > meshedSM;
512   PrepareOCCgeometry( occgeo, _shape, *_mesh, &meshedSM );
513
514   // -------------------------
515   // Generate the mesh
516   // -------------------------
517
518   netgen::Mesh *ngMesh = NULL;
519
520   SMESH_Comment comment;
521   int err = 0;
522   int nbInitNod = 0;
523   int nbInitSeg = 0;
524   int nbInitFac = 0;
525   // vector of nodes in which node index == netgen ID
526   vector< SMDS_MeshNode* > nodeVec;
527   try
528   {
529     // ----------------
530     // compute 1D mesh
531     // ----------------
532     // pass 1D simple parameters to NETGEN
533     if ( _simpleHyp ) {
534       if ( int nbSeg = _simpleHyp->GetNumberOfSegments() ) {
535         // nb of segments
536         mparams.segmentsperedge = nbSeg + 0.1;
537         mparams.maxh = occgeo.boundingbox.Diam();
538         mparams.grading = 0;
539       }
540       else {
541         // segment length
542         mparams.segmentsperedge = 1;
543         mparams.maxh = _simpleHyp->GetLocalLength();
544       }
545     }
546     // let netgen create ngMesh and calculate element size on not meshed shapes
547     char *optstr = 0;
548     int startWith = netgen::MESHCONST_ANALYSE;
549     int endWith   = netgen::MESHCONST_ANALYSE;
550     err = netgen::OCCGenerateMesh(occgeo, ngMesh, startWith, endWith, optstr);
551     if (err) comment << "Error in netgen::OCCGenerateMesh() at MESHCONST_ANALYSE step";
552
553     // fill ngMesh with nodes and elements of computed submeshes
554     err = ! fillNgMesh(occgeo, *ngMesh, nodeVec, meshedSM);
555     nbInitNod = ngMesh->GetNP();
556     nbInitSeg = ngMesh->GetNSeg();
557     nbInitFac = ngMesh->GetNSE();
558
559     // compute mesh
560     if (!err)
561     {
562       startWith = endWith = netgen::MESHCONST_MESHEDGES;
563       err = netgen::OCCGenerateMesh(occgeo, ngMesh, startWith, endWith, optstr);
564       if (err) comment << "Error in netgen::OCCGenerateMesh() at 1D mesh generation";
565     }
566     // ---------------------
567     // compute surface mesh
568     // ---------------------
569     if (!err)
570     {
571       // pass 2D simple parameters to NETGEN
572       if ( _simpleHyp ) {
573         if ( double area = _simpleHyp->GetMaxElementArea() ) {
574           // face area
575           mparams.maxh = sqrt(2. * area/sqrt(3.0));
576           mparams.grading = 0.4; // moderate size growth
577         }
578         else {
579           // length from edges
580           double length = 0;
581           for ( TopExp_Explorer exp( _shape, TopAbs_EDGE ); exp.More(); exp.Next() )
582             length += SMESH_Algo::EdgeLength( TopoDS::Edge( exp.Current() ));
583           if ( ngMesh->GetNSeg() )
584             mparams.maxh = length / ngMesh->GetNSeg();
585           else
586             mparams.maxh = 1000;
587           mparams.grading = 0.2; // slow size growth
588         }
589         mparams.maxh = min( mparams.maxh, occgeo.boundingbox.Diam()/2 );
590         ngMesh->SetGlobalH (mparams.maxh);
591         netgen::Box<3> bb = occgeo.GetBoundingBox();
592         bb.Increase (bb.Diam()/20);
593         ngMesh->SetLocalH (bb.PMin(), bb.PMax(), mparams.grading);
594       }
595       // let netgen compute 2D mesh
596       startWith = netgen::MESHCONST_MESHSURFACE;
597       endWith = _optimize ? netgen::MESHCONST_OPTSURFACE : netgen::MESHCONST_MESHSURFACE;
598       err = netgen::OCCGenerateMesh(occgeo, ngMesh, startWith, endWith, optstr);
599       if (err) comment << "Error in netgen::OCCGenerateMesh() at surface mesh generation";
600     }
601     // ---------------------
602     // generate volume mesh
603     // ---------------------
604     if (!err && _isVolume)
605     {
606       // add ng face descriptors of meshed faces
607       std::map< int, std::pair<int,int> >::iterator fId_soIds = _faceDescriptors.begin();
608       for ( ; fId_soIds != _faceDescriptors.end(); ++fId_soIds ) {
609         int faceID   = fId_soIds->first;
610         int solidID1 = fId_soIds->second.first;
611         int solidID2 = fId_soIds->second.second;
612         ngMesh->AddFaceDescriptor (netgen::FaceDescriptor(faceID, solidID1, solidID2, 0));
613       }
614       // pass 3D simple parameters to NETGEN
615       const NETGENPlugin_SimpleHypothesis_3D* simple3d =
616         dynamic_cast< const NETGENPlugin_SimpleHypothesis_3D* > ( _simpleHyp );
617       if ( simple3d ) {
618         if ( double vol = simple3d->GetMaxElementVolume() ) {
619           // max volume
620           mparams.maxh = pow( 72 * vol * vol, 1/3. );
621           mparams.maxh = min( mparams.maxh, occgeo.boundingbox.Diam()/2 );
622         }
623         else {
624           // length from faces
625           mparams.maxh = ngMesh->AverageH();
626         }
627 //      netgen::ARRAY<double> maxhdom;
628 //      maxhdom.SetSize (occgeo.NrSolids());
629 //      maxhdom = mparams.maxh;
630 //      ngMesh->SetMaxHDomain (maxhdom);
631         ngMesh->SetGlobalH (mparams.maxh);
632         mparams.grading = 0.4;
633         ngMesh->CalcLocalH();
634       }
635       // let netgen compute 3D mesh
636       startWith = netgen::MESHCONST_MESHVOLUME;
637       endWith = _optimize ? netgen::MESHCONST_OPTVOLUME : netgen::MESHCONST_MESHVOLUME;
638       err = netgen::OCCGenerateMesh(occgeo, ngMesh, startWith, endWith, optstr);
639       if (err) comment << "Error in netgen::OCCGenerateMesh()";
640     }
641     if (!err && mparams.secondorder > 0)
642     {
643       netgen::OCCRefinementSurfaces ref (occgeo);
644       ref.MakeSecondOrder (*ngMesh);
645     }
646   }
647   catch (netgen::NgException exc)
648   {
649     error->myName = err = COMPERR_ALGO_FAILED;
650     comment << exc.What();
651   }
652
653   int nbNod = ngMesh->GetNP();
654   int nbSeg = ngMesh->GetNSeg();
655   int nbFac = ngMesh->GetNSE();
656   int nbVol = ngMesh->GetNE();
657
658   MESSAGE((err ? "Mesh Generation failure" : "End of Mesh Generation") <<
659           ", nb nodes: " << nbNod <<
660           ", nb segments: " << nbSeg <<
661           ", nb faces: " << nbFac <<
662           ", nb volumes: " << nbVol);
663
664   // -----------------------------------------------------------
665   // Feed back the SMESHDS with the generated Nodes and Elements
666   // -----------------------------------------------------------
667
668   SMESHDS_Mesh* meshDS = _mesh->GetMeshDS();
669   bool isOK = ( !err && (_isVolume ? (nbVol > 0) : (nbFac > 0)) );
670   if ( true /*isOK*/ ) // get whatever built
671   {
672     // map of nodes assigned to submeshes
673     NCollection_Map<int> pindMap;
674     // create and insert nodes into nodeVec
675     nodeVec.resize( nbNod + 1 );
676     int i;
677     for (i = nbInitNod+1; i <= nbNod /*&& isOK*/; ++i )
678     {
679       const netgen::MeshPoint& ngPoint = ngMesh->Point(i);
680       SMDS_MeshNode* node = NULL;
681       bool newNodeOnVertex = false;
682       TopoDS_Vertex aVert;
683       if (i-nbInitNod <= occgeo.vmap.Extent())
684       {
685         // point on vertex
686         aVert = TopoDS::Vertex(occgeo.vmap(i-nbInitNod));
687         SMESHDS_SubMesh * submesh = meshDS->MeshElements(aVert);
688         if (submesh)
689         {
690           SMDS_NodeIteratorPtr it = submesh->GetNodes();
691           if (it->more())
692           {
693             node = const_cast<SMDS_MeshNode*> (it->next());
694             pindMap.Add(i);
695           }
696         }
697         if (!node)
698           newNodeOnVertex = true;
699       }
700       if (!node)
701         node = meshDS->AddNode(ngPoint.X(), ngPoint.Y(), ngPoint.Z());
702       if (!node)
703       {
704         MESSAGE("Cannot create a mesh node");
705         if ( !comment.size() ) comment << "Cannot create a mesh node";
706         nbSeg = nbFac = nbVol = isOK = 0;
707         break;
708       }
709       nodeVec.at(i) = node;
710       if (newNodeOnVertex)
711       {
712         // point on vertex
713         meshDS->SetNodeOnVertex(node, aVert);
714         pindMap.Add(i);
715       }
716     }
717
718     // create mesh segments along geometric edges
719     NCollection_Map<Link> linkMap;
720     for (i = nbInitSeg+1; i <= nbSeg/* && isOK*/; ++i )
721     {
722       const netgen::Segment& seg = ngMesh->LineSegment(i);
723       Link link(seg.p1, seg.p2);
724       if (linkMap.Contains(link))
725         continue;
726       linkMap.Add(link);
727       TopoDS_Edge aEdge;
728       int pinds[3] = { seg.p1, seg.p2, seg.pmid };
729       int nbp = 0;
730       double param2 = 0;
731       for (int j=0; j < 3; ++j)
732       {
733         int pind = pinds[j];
734         if (pind <= 0) continue;
735         ++nbp;
736         double param;
737         if (j < 2)
738         {
739           if (aEdge.IsNull())
740           {
741             int aGeomEdgeInd = seg.epgeominfo[j].edgenr;
742             if (aGeomEdgeInd > 0 && aGeomEdgeInd <= occgeo.emap.Extent())
743               aEdge = TopoDS::Edge(occgeo.emap(aGeomEdgeInd));
744           }
745           param = seg.epgeominfo[j].dist;
746           param2 += param;
747         }
748         else
749           param = param2 * 0.5;
750         if (pind <= nbInitNod || pindMap.Contains(pind))
751           continue;
752         if (!aEdge.IsNull())
753         {
754           meshDS->SetNodeOnEdge(nodeVec.at(pind), aEdge, param);
755           pindMap.Add(pind);
756         }
757       }
758       SMDS_MeshEdge* edge;
759       if (nbp < 3) // second order ?
760         edge = meshDS->AddEdge(nodeVec.at(pinds[0]), nodeVec.at(pinds[1]));
761       else
762         edge = meshDS->AddEdge(nodeVec.at(pinds[0]), nodeVec.at(pinds[1]),
763                                 nodeVec.at(pinds[2]));
764       if (!edge)
765       {
766         if ( !comment.size() ) comment << "Cannot create a mesh edge";
767         MESSAGE("Cannot create a mesh edge");
768         nbSeg = nbFac = nbVol = isOK = 0;
769         break;
770       }
771       if (!aEdge.IsNull())
772         meshDS->SetMeshElementOnShape(edge, aEdge);
773     }
774
775     // create mesh faces along geometric faces
776     for (i = nbInitFac+1; i <= nbFac/* && isOK*/; ++i )
777     {
778       const netgen::Element2d& elem = ngMesh->SurfaceElement(i);
779       int aGeomFaceInd = elem.GetIndex();
780       TopoDS_Face aFace;
781       if (aGeomFaceInd > 0 && aGeomFaceInd <= occgeo.fmap.Extent())
782         aFace = TopoDS::Face(occgeo.fmap(aGeomFaceInd));
783       vector<SMDS_MeshNode*> nodes;
784       for (int j=1; j <= elem.GetNP(); ++j)
785       {
786         int pind = elem.PNum(j);
787         SMDS_MeshNode* node = nodeVec.at(pind);
788         nodes.push_back(node);
789         if (pind <= nbInitNod || pindMap.Contains(pind))
790           continue;
791         if (!aFace.IsNull())
792         {
793           const netgen::PointGeomInfo& pgi = elem.GeomInfoPi(j);
794           meshDS->SetNodeOnFace(node, aFace, pgi.u, pgi.v);
795           pindMap.Add(pind);
796         }
797       }
798       SMDS_MeshFace* face = NULL;
799       switch (elem.GetType())
800       {
801       case netgen::TRIG:
802         face = meshDS->AddFace(nodes[0],nodes[1],nodes[2]);
803         break;
804       case netgen::QUAD:
805         face = meshDS->AddFace(nodes[0],nodes[1],nodes[2],nodes[3]);
806         break;
807       case netgen::TRIG6:
808         face = meshDS->AddFace(nodes[0],nodes[1],nodes[2],nodes[5],nodes[3],nodes[4]);
809         break;
810       case netgen::QUAD8:
811         face = meshDS->AddFace(nodes[0],nodes[1],nodes[2],nodes[3],
812                                nodes[4],nodes[7],nodes[5],nodes[6]);
813         break;
814       default:
815         MESSAGE("NETGEN created a face of unexpected type, ignoring");
816         continue;
817       }
818       if (!face)
819       {
820         if ( !comment.size() ) comment << "Cannot create a mesh face";
821         MESSAGE("Cannot create a mesh face");
822         nbSeg = nbFac = nbVol = isOK = 0;
823         break;
824       }
825       if (!aFace.IsNull())
826         meshDS->SetMeshElementOnShape(face, aFace);
827     }
828
829     // create tetrahedra
830     for (i = 1; i <= nbVol/* && isOK*/; ++i)
831     {
832       const netgen::Element& elem = ngMesh->VolumeElement(i);      
833       int aSolidInd = elem.GetIndex();
834       TopoDS_Solid aSolid;
835       if (aSolidInd > 0 && aSolidInd <= occgeo.somap.Extent())
836         aSolid = TopoDS::Solid(occgeo.somap(aSolidInd));
837       vector<SMDS_MeshNode*> nodes;
838       for (int j=1; j <= elem.GetNP(); ++j)
839       {
840         int pind = elem.PNum(j);
841         SMDS_MeshNode* node = nodeVec.at(pind);
842         nodes.push_back(node);
843         if (pind <= nbInitNod || pindMap.Contains(pind))
844           continue;
845         if (!aSolid.IsNull())
846         {
847           // point in solid
848           meshDS->SetNodeInVolume(node, aSolid);
849           pindMap.Add(pind);
850         }
851       }
852       SMDS_MeshVolume* vol = NULL;
853       switch (elem.GetType())
854       {
855       case netgen::TET:
856         vol = meshDS->AddVolume(nodes[0],nodes[1],nodes[2],nodes[3]);
857         break;
858       case netgen::TET10:
859         vol = meshDS->AddVolume(nodes[0],nodes[1],nodes[2],nodes[3],
860                                 nodes[4],nodes[7],nodes[5],nodes[6],nodes[8],nodes[9]);
861         break;
862       default:
863         MESSAGE("NETGEN created a volume of unexpected type, ignoring");
864         continue;
865       }
866       if (!vol)
867       {
868         if ( !comment.size() ) comment << "Cannot create a mesh volume";
869         MESSAGE("Cannot create a mesh volume");
870         nbSeg = nbFac = nbVol = isOK = 0;
871         break;
872       }
873       if (!aSolid.IsNull())
874         meshDS->SetMeshElementOnShape(vol, aSolid);
875     }
876   }
877
878   if ( error->IsOK() && ( !isOK || comment.size() > 0 ))
879     error->myName = COMPERR_ALGO_FAILED;
880   if ( !comment.empty() )
881     error->myComment = comment;
882
883   // set bad compute error to subshapes of all failed subshapes shapes
884   if ( !error->IsOK() && err )
885   {
886     for (int i = 1; i <= occgeo.fmap.Extent(); i++) {
887       int status = occgeo.facemeshstatus[i-1];
888       if (status == 1 ) continue;
889       if ( SMESH_subMesh* sm = _mesh->GetSubMeshContaining( occgeo.fmap( i ))) {
890         SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
891         if ( !smError || smError->IsOK() ) {
892           if ( status == -1 )
893             smError.reset( new SMESH_ComputeError( error->myName, error->myComment ));
894           else
895             smError.reset( new SMESH_ComputeError( COMPERR_ALGO_FAILED, "Ignored" ));
896         }
897       }
898     }
899   }
900
901   nglib::Ng_DeleteMesh((nglib::Ng_Mesh*)ngMesh);
902   nglib::Ng_Exit();
903
904   RemoveTmpFiles();
905
906   return error->IsOK();
907 }
908
909 //================================================================================
910 /*!
911  * \brief Remove "test.out" and "problemfaces" files in current directory
912  */
913 //================================================================================
914
915 void NETGENPlugin_Mesher::RemoveTmpFiles()
916 {
917   TCollection_AsciiString str("test.out");
918   OSD_Path path1( str );
919   OSD_File file1( path1 );
920   file1.Remove();
921   str = "problemfaces";
922   OSD_Path path2( str );
923   OSD_File file2( path2 );
924   file2.Remove();
925 }