Salome HOME
de45d54d0bd11cc0ece20cbe0d069de69c7812dd
[plugins/netgenplugin.git] / src / NETGENPlugin / NETGENPlugin_NETGEN_3D.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 //=============================================================================
24 // File      : NETGENPlugin_NETGEN_3D.cxx
25 //             Moved here from SMESH_NETGEN_3D.cxx
26 // Created   : lundi 27 Janvier 2003
27 // Author    : Nadir BOUHAMOU (CEA)
28 // Project   : SALOME
29 //=============================================================================
30 //
31 #include "NETGENPlugin_NETGEN_3D.hxx"
32
33 #include "NETGENPlugin_Hypothesis.hxx"
34
35 #include <SMDS_MeshElement.hxx>
36 #include <SMDS_MeshNode.hxx>
37 #include <SMESHDS_Mesh.hxx>
38 #include <SMESH_Comment.hxx>
39 #include <SMESH_ControlsDef.hxx>
40 #include <SMESH_Gen.hxx>
41 #include <SMESH_Mesh.hxx>
42 #include <SMESH_MeshEditor.hxx>
43 #include <SMESH_MesherHelper.hxx>
44 #include <SMESH_subMesh.hxx>
45 #include <StdMeshers_MaxElementVolume.hxx>
46 #include <StdMeshers_QuadToTriaAdaptor.hxx>
47 #include <StdMeshers_ViscousLayers.hxx>
48
49 #include <BRepGProp.hxx>
50 #include <BRep_Tool.hxx>
51 #include <GProp_GProps.hxx>
52 #include <TopExp.hxx>
53 #include <TopExp_Explorer.hxx>
54 #include <TopTools_ListIteratorOfListOfShape.hxx>
55 #include <TopoDS.hxx>
56
57 #include <Standard_Failure.hxx>
58 #include <Standard_ErrorHandler.hxx>
59
60 #include <utilities.h>
61
62 #include <list>
63 #include <vector>
64 #include <map>
65
66 /*
67   Netgen include files
68 */
69
70 #ifndef OCCGEOMETRY
71 #define OCCGEOMETRY
72 #endif
73 #include <occgeom.hpp>
74
75 #ifdef NETGEN_V5
76 #include <ngexception.hpp>
77 #endif
78 #ifdef NETGEN_V6
79 #include <exception.hpp>
80 #endif
81
82 namespace nglib {
83 #include <nglib.h>
84 }
85 namespace netgen {
86 #ifdef NETGEN_V5
87   extern int OCCGenerateMesh (OCCGeometry&, Mesh*&, MeshingParameters&, int, int);
88 #else
89   extern int OCCGenerateMesh (OCCGeometry&, Mesh*&, int, int, char*);
90 #endif
91
92   NETGENPLUGIN_DLL_HEADER
93   extern MeshingParameters mparam;
94
95   NETGENPLUGIN_DLL_HEADER
96   extern volatile multithreadt multithread;
97 }
98 using namespace nglib;
99 using namespace std;
100
101 //=============================================================================
102 /*!
103  *  
104  */
105 //=============================================================================
106
107 NETGENPlugin_NETGEN_3D::NETGENPlugin_NETGEN_3D(int hypId, SMESH_Gen* gen)
108   : SMESH_3D_Algo(hypId, gen)
109 {
110   _name = "NETGEN_3D";
111   _shapeType = (1 << TopAbs_SHELL) | (1 << TopAbs_SOLID);// 1 bit /shape type
112   _compatibleHypothesis.push_back("MaxElementVolume");
113   _compatibleHypothesis.push_back("NETGEN_Parameters");
114   _compatibleHypothesis.push_back("ViscousLayers");
115
116   _maxElementVolume = 0.;
117
118   _hypMaxElementVolume = NULL;
119   _hypParameters = NULL;
120   _viscousLayersHyp = NULL;
121
122   _requireShape = false; // can work without shape
123 }
124
125 //=============================================================================
126 /*!
127  *  
128  */
129 //=============================================================================
130
131 NETGENPlugin_NETGEN_3D::~NETGENPlugin_NETGEN_3D()
132 {
133 }
134
135 //=============================================================================
136 /*!
137  *  
138  */
139 //=============================================================================
140
141 bool NETGENPlugin_NETGEN_3D::CheckHypothesis (SMESH_Mesh&         aMesh,
142                                               const TopoDS_Shape& aShape,
143                                               Hypothesis_Status&  aStatus)
144 {
145   _hypMaxElementVolume = NULL;
146   _hypParameters = NULL;
147   _viscousLayersHyp = NULL;
148   _maxElementVolume = DBL_MAX;
149
150   // for correct work of GetProgress():
151   netgen::multithread.percent = 0.;
152   netgen::multithread.task = "Volume meshing";
153   _progressByTic = -1.;
154
155   list<const SMESHDS_Hypothesis*>::const_iterator itl;
156   //const SMESHDS_Hypothesis* theHyp;
157
158   const list<const SMESHDS_Hypothesis*>& hyps =
159     GetUsedHypothesis(aMesh, aShape, /*ignoreAuxiliary=*/false);
160   list <const SMESHDS_Hypothesis* >::const_iterator h = hyps.begin();
161   if ( h == hyps.end())
162   {
163     aStatus = SMESH_Hypothesis::HYP_OK;
164     return true;  // can work with no hypothesis
165   }
166
167   aStatus = HYP_OK;
168   for ( ; h != hyps.end(); ++h )
169   {
170     if ( !_hypMaxElementVolume )
171       _hypMaxElementVolume = dynamic_cast< const StdMeshers_MaxElementVolume*> ( *h );
172     if ( !_viscousLayersHyp ) // several _viscousLayersHyp's allowed
173       _viscousLayersHyp = dynamic_cast< const StdMeshers_ViscousLayers*> ( *h );
174     if ( ! _hypParameters )
175       _hypParameters = dynamic_cast< const NETGENPlugin_Hypothesis*> ( *h );
176
177     if ( *h != _hypMaxElementVolume &&
178          *h != _viscousLayersHyp &&
179          *h != _hypParameters &&
180          !dynamic_cast< const StdMeshers_ViscousLayers*>(*h)) // several VL hyps allowed
181       aStatus = HYP_INCOMPATIBLE;
182   }
183   if ( _hypMaxElementVolume && _hypParameters )
184     aStatus = HYP_INCOMPATIBLE;
185   else if ( aStatus == HYP_OK && _viscousLayersHyp )
186     error( _viscousLayersHyp->CheckHypothesis( aMesh, aShape, aStatus ));
187
188   if ( _hypMaxElementVolume )
189     _maxElementVolume = _hypMaxElementVolume->GetMaxVolume();
190
191   return aStatus == HYP_OK;
192 }
193
194 //=============================================================================
195 /*!
196  *Here we are going to use the NETGEN mesher
197  */
198 //=============================================================================
199
200 bool NETGENPlugin_NETGEN_3D::Compute(SMESH_Mesh&         aMesh,
201                                      const TopoDS_Shape& aShape)
202 {
203   netgen::multithread.terminate = 0;
204   netgen::multithread.task = "Volume meshing";
205   _progressByTic = -1.;
206
207   SMESHDS_Mesh* meshDS = aMesh.GetMeshDS();
208
209   SMESH_MesherHelper helper(aMesh);
210   _quadraticMesh = helper.IsQuadraticSubMesh(aShape);
211   helper.SetElementsOnShape( true );
212
213   int Netgen_NbOfNodes = 0;
214   double Netgen_point[3];
215   int Netgen_triangle[3];
216
217   NETGENPlugin_NetgenLibWrapper ngLib;
218   Ng_Mesh * Netgen_mesh = (Ng_Mesh*)ngLib._ngMesh;
219
220   // vector of nodes in which node index == netgen ID
221   vector< const SMDS_MeshNode* > nodeVec;
222   {
223     const int invalid_ID = -1;
224
225     SMESH::Controls::Area areaControl;
226     SMESH::Controls::TSequenceOfXYZ nodesCoords;
227
228     // maps nodes to ng ID
229     typedef map< const SMDS_MeshNode*, int, TIDCompare > TNodeToIDMap;
230     typedef TNodeToIDMap::value_type                     TN2ID;
231     TNodeToIDMap nodeToNetgenID;
232
233     // find internal shapes
234     NETGENPlugin_Internals internals( aMesh, aShape, /*is3D=*/true );
235
236     // ---------------------------------
237     // Feed the Netgen with surface mesh
238     // ---------------------------------
239
240     TopAbs_ShapeEnum mainType = aMesh.GetShapeToMesh().ShapeType();
241     bool checkReverse = ( mainType == TopAbs_COMPOUND || mainType == TopAbs_COMPSOLID );
242
243     SMESH_ProxyMesh::Ptr proxyMesh( new SMESH_ProxyMesh( aMesh ));
244     if ( _viscousLayersHyp )
245     {
246       netgen::multithread.percent = 3;
247       proxyMesh = _viscousLayersHyp->Compute( aMesh, aShape );
248       if ( !proxyMesh )
249         return false;
250     }
251     if ( aMesh.NbQuadrangles() > 0 )
252     {
253       netgen::multithread.percent = 6;
254       StdMeshers_QuadToTriaAdaptor* Adaptor = new StdMeshers_QuadToTriaAdaptor;
255       Adaptor->Compute(aMesh,aShape,proxyMesh.get());
256       proxyMesh.reset( Adaptor );
257     }
258
259     for ( TopExp_Explorer exFa( aShape, TopAbs_FACE ); exFa.More(); exFa.Next())
260     {
261       const TopoDS_Shape& aShapeFace = exFa.Current();
262       int faceID = meshDS->ShapeToIndex( aShapeFace );
263       bool isInternalFace = internals.isInternalShape( faceID );
264       bool isRev = false;
265       if ( checkReverse && !isInternalFace &&
266            helper.NbAncestors(aShapeFace, aMesh, aShape.ShapeType()) > 1 )
267         // IsReversedSubMesh() can work wrong on strongly curved faces,
268         // so we use it as less as possible
269         isRev = helper.IsReversedSubMesh( TopoDS::Face( aShapeFace ));
270
271       const SMESHDS_SubMesh * aSubMeshDSFace = proxyMesh->GetSubMesh( aShapeFace );
272       if ( !aSubMeshDSFace ) continue;
273
274       SMDS_ElemIteratorPtr iteratorElem = aSubMeshDSFace->GetElements();
275       if ( _quadraticMesh &&
276            dynamic_cast< const SMESH_ProxyMesh::SubMesh*>( aSubMeshDSFace ))
277       {
278         // add medium nodes of proxy triangles to helper (#16843)
279         while ( iteratorElem->more() )
280           helper.AddTLinks( static_cast< const SMDS_MeshFace* >( iteratorElem->next() ));
281
282         iteratorElem = aSubMeshDSFace->GetElements();
283       }
284       while ( iteratorElem->more() ) // loop on elements on a geom face
285       {
286         // check mesh face
287         const SMDS_MeshElement* elem = iteratorElem->next();
288         if ( !elem )
289           return error( COMPERR_BAD_INPUT_MESH, "Null element encounters");
290         if ( elem->NbCornerNodes() != 3 )
291           return error( COMPERR_BAD_INPUT_MESH, "Not triangle element encounters");
292
293         // Add nodes of triangles and triangles them-selves to netgen mesh
294
295         // add three nodes of triangle
296         bool hasDegen = false;
297         for ( int iN = 0; iN < 3; ++iN )
298         {
299           const SMDS_MeshNode* node = elem->GetNode( iN );
300           const int shapeID = node->getshapeId();
301           if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_EDGE &&
302                helper.IsDegenShape( shapeID ))
303           {
304             // ignore all nodes on degeneraged edge and use node on its vertex instead
305             TopoDS_Shape vertex = TopoDS_Iterator( meshDS->IndexToShape( shapeID )).Value();
306             node = SMESH_Algo::VertexNode( TopoDS::Vertex( vertex ), meshDS );
307             hasDegen = true;
308           }
309           int& ngID = nodeToNetgenID.insert(TN2ID( node, invalid_ID )).first->second;
310           if ( ngID == invalid_ID )
311           {
312             ngID = ++Netgen_NbOfNodes;
313             Netgen_point [ 0 ] = node->X();
314             Netgen_point [ 1 ] = node->Y();
315             Netgen_point [ 2 ] = node->Z();
316             Ng_AddPoint(Netgen_mesh, Netgen_point);
317           }
318           Netgen_triangle[ isRev ? 2-iN : iN ] = ngID;
319         }
320         // add triangle
321         if ( hasDegen && (Netgen_triangle[0] == Netgen_triangle[1] ||
322                           Netgen_triangle[0] == Netgen_triangle[2] ||
323                           Netgen_triangle[2] == Netgen_triangle[1] ))
324           continue;
325
326         Ng_AddSurfaceElement(Netgen_mesh, NG_TRIG, Netgen_triangle);
327
328         if ( isInternalFace && !proxyMesh->IsTemporary( elem ))
329         {
330           swap( Netgen_triangle[1], Netgen_triangle[2] );
331           Ng_AddSurfaceElement(Netgen_mesh, NG_TRIG, Netgen_triangle);
332         }
333       } // loop on elements on a face
334     } // loop on faces of a SOLID or SHELL
335
336     // insert old nodes into nodeVec
337     nodeVec.resize( nodeToNetgenID.size() + 1, 0 );
338     TNodeToIDMap::iterator n_id = nodeToNetgenID.begin();
339     for ( ; n_id != nodeToNetgenID.end(); ++n_id )
340       nodeVec[ n_id->second ] = n_id->first;
341     nodeToNetgenID.clear();
342
343     if ( internals.hasInternalVertexInSolid() )
344     {
345       netgen::OCCGeometry occgeo;
346       NETGENPlugin_Mesher::AddIntVerticesInSolids( occgeo,
347                                                    (netgen::Mesh&) *Netgen_mesh,
348                                                    nodeVec,
349                                                    internals);
350     }
351   }
352
353   // -------------------------
354   // Generate the volume mesh
355   // -------------------------
356
357   return ( ngLib._isComputeOk = compute( aMesh, helper, nodeVec, ngLib ));
358 }
359
360 // namespace
361 // {
362 //   void limitVolumeSize( netgen::Mesh* ngMesh,
363 //                         double        maxh )
364 //   {
365 //     // get average h of faces
366 //     double faceh = 0;
367 //     int nbh = 0;
368 //     for (int i = 1; i <= ngMesh->GetNSE(); i++)
369 //     {
370 //       const netgen::Element2d& face = ngMesh->SurfaceElement(i);
371 //       for (int j=1; j <= face.GetNP(); ++j)
372 //       {
373 //         const netgen::PointIndex & i1 = face.PNumMod(j);
374 //         const netgen::PointIndex & i2 = face.PNumMod(j+1);
375 //         if ( i1 < i2 )
376 //         {
377 //           const netgen::Point3d & p1 = ngMesh->Point( i1 );
378 //           const netgen::Point3d & p2 = ngMesh->Point( i2 );
379 //           faceh += netgen::Dist2( p1, p2 );
380 //           nbh++;
381 //         }
382 //       }
383 //     }
384 //     faceh = Sqrt( faceh / nbh );
385
386 //     double compareh;
387 //     if      ( faceh < 0.5 * maxh ) compareh = -1;
388 //     else if ( faceh > 1.5 * maxh ) compareh = 1;
389 //     else                           compareh = 0;
390 //     // cerr << "faceh " << faceh << endl;
391 //     // cerr << "init maxh " << maxh << endl;
392 //     // cerr << "compareh " << compareh << endl;
393
394 //     if ( compareh > 0 )
395 //       maxh *= 1.2;
396 //     else
397 //       maxh *= 0.8;
398 //     // cerr << "maxh " << maxh << endl;
399
400 //     // get bnd box
401 //     netgen::Point3d pmin, pmax;
402 //     ngMesh->GetBox( pmin, pmax, 0 );
403 //     const double dx = pmax.X() - pmin.X();
404 //     const double dy = pmax.Y() - pmin.Y();
405 //     const double dz = pmax.Z() - pmin.Z();
406
407 //     if ( ! & ngMesh->LocalHFunction() )
408 //       ngMesh->SetLocalH( pmin, pmax, compareh <= 0 ? 0.1 : 0.5 );
409
410 //     // adjusted by SALOME_TESTS/Grids/smesh/bugs_08/I8
411 //     const int nbX = Max( 2, int( dx / maxh * 2 ));
412 //     const int nbY = Max( 2, int( dy / maxh * 2 ));
413 //     const int nbZ = Max( 2, int( dz / maxh * 2 ));
414
415 //     netgen::Point3d p;
416 //     for ( int i = 0; i <= nbX; ++i )
417 //     {
418 //       p.X() = pmin.X() +  i * dx / nbX;
419 //       for ( int j = 0; j <= nbY; ++j )
420 //       {
421 //         p.Y() = pmin.Y() +  j * dy / nbY;
422 //         for ( int k = 0; k <= nbZ; ++k )
423 //         {
424 //           p.Z() = pmin.Z() +  k * dz / nbZ;
425 //           ngMesh->RestrictLocalH( p, maxh );
426 //         }
427 //       }
428 //     }
429 //   }
430 // }
431
432 //================================================================================
433 /*!
434  * \brief set parameters and generate the volume mesh
435  */
436 //================================================================================
437
438 bool NETGENPlugin_NETGEN_3D::compute(SMESH_Mesh&                     aMesh,
439                                      SMESH_MesherHelper&             helper,
440                                      vector< const SMDS_MeshNode* >& nodeVec,
441                                      NETGENPlugin_NetgenLibWrapper&  ngLib)
442 {
443   netgen::multithread.terminate = 0;
444
445   netgen::Mesh* ngMesh = ngLib._ngMesh;
446   Ng_Mesh* Netgen_mesh = ngLib.ngMesh();
447   int Netgen_NbOfNodes = Ng_GetNP( Netgen_mesh );
448
449   int startWith = netgen::MESHCONST_MESHVOLUME;
450   int endWith   = netgen::MESHCONST_OPTVOLUME;
451   int err = 1;
452
453   NETGENPlugin_Mesher aMesher( &aMesh, helper.GetSubShape(), /*isVolume=*/true );
454   netgen::OCCGeometry occgeo;
455
456   if ( _hypParameters )
457   {
458     aMesher.SetParameters( _hypParameters );
459
460     if ( !_hypParameters->GetLocalSizesAndEntries().empty() ||
461          !_hypParameters->GetMeshSizeFile().empty() )
462     {
463       if ( ! &ngMesh->LocalHFunction() )
464       {
465         netgen::Point3d pmin, pmax;
466         ngMesh->GetBox( pmin, pmax, 0 );
467         ngMesh->SetLocalH( pmin, pmax, _hypParameters->GetGrowthRate() );
468       }
469       aMesher.SetLocalSize( occgeo, *ngMesh );
470
471       try {
472         ngMesh->LoadLocalMeshSize( netgen::mparam.meshsizefilename );
473       } catch (netgen::NgException & ex) {
474         return error( COMPERR_BAD_PARMETERS, ex.What() );
475       }
476     }
477     if ( !_hypParameters->GetOptimize() )
478       endWith = netgen::MESHCONST_MESHVOLUME;
479   }
480   else if ( _hypMaxElementVolume )
481   {
482     netgen::mparam.maxh = pow( 72, 1/6. ) * pow( _maxElementVolume, 1/3. );
483     // limitVolumeSize( ngMesh, netgen::mparam.maxh ); // result is unpredictable
484   }
485   else if ( aMesh.HasShapeToMesh() )
486   {
487     aMesher.PrepareOCCgeometry( occgeo, helper.GetSubShape(), aMesh );
488     netgen::mparam.maxh = occgeo.GetBoundingBox().Diam()/2;
489   }
490   else
491   {
492     netgen::Point3d pmin, pmax;
493     ngMesh->GetBox (pmin, pmax);
494     netgen::mparam.maxh = Dist(pmin, pmax)/2;
495   }
496
497   if ( !_hypParameters && aMesh.HasShapeToMesh() )
498   {
499     netgen::mparam.minh = aMesher.GetDefaultMinSize( helper.GetSubShape(), netgen::mparam.maxh );
500   }
501
502   try
503   {
504     OCC_CATCH_SIGNALS;
505
506     ngLib.CalcLocalH(ngMesh);
507     err = ngLib.GenerateMesh(occgeo, startWith, endWith);
508
509     if(netgen::multithread.terminate)
510       return false;
511     if ( err )
512       error(SMESH_Comment("Error in netgen::OCCGenerateMesh() at ") << netgen::multithread.task);
513   }
514   catch (Standard_Failure& ex)
515   {
516     SMESH_Comment str("Exception in  netgen::OCCGenerateMesh()");
517     str << " at " << netgen::multithread.task
518         << ": " << ex.DynamicType()->Name();
519     if ( ex.GetMessageString() && strlen( ex.GetMessageString() ))
520       str << ": " << ex.GetMessageString();
521     error(str);
522   }
523   catch (netgen::NgException& exc)
524   {
525     SMESH_Comment str("NgException");
526     if ( strlen( netgen::multithread.task ) > 0 )
527       str << " at " << netgen::multithread.task;
528     str << ": " << exc.What();
529     error(str);
530   }
531   catch (...)
532   {
533     SMESH_Comment str("Exception in  netgen::OCCGenerateMesh()");
534     if ( strlen( netgen::multithread.task ) > 0 )
535       str << " at " << netgen::multithread.task;
536     error(str);
537   }
538
539   int Netgen_NbOfNodesNew = Ng_GetNP(Netgen_mesh);
540   int Netgen_NbOfTetra    = Ng_GetNE(Netgen_mesh);
541
542   // -------------------------------------------------------------------
543   // Feed back the SMESHDS with the generated Nodes and Volume Elements
544   // -------------------------------------------------------------------
545
546   if ( err )
547   {
548     SMESH_ComputeErrorPtr ce = NETGENPlugin_Mesher::ReadErrors(nodeVec);
549     if ( ce && ce->HasBadElems() )
550       error( ce );
551   }
552
553   bool isOK = ( /*status == NG_OK &&*/ Netgen_NbOfTetra > 0 );// get whatever built
554   if ( isOK )
555   {
556     double Netgen_point[3];
557     int    Netgen_tetrahedron[4];
558
559     // create and insert new nodes into nodeVec
560     nodeVec.resize( Netgen_NbOfNodesNew + 1, 0 );
561     int nodeIndex = Netgen_NbOfNodes + 1;
562     for ( ; nodeIndex <= Netgen_NbOfNodesNew; ++nodeIndex )
563     {
564       Ng_GetPoint( Netgen_mesh, nodeIndex, Netgen_point );
565       nodeVec.at(nodeIndex) = helper.AddNode(Netgen_point[0], Netgen_point[1], Netgen_point[2]);
566     }
567
568     // create tetrahedrons
569     for ( int elemIndex = 1; elemIndex <= Netgen_NbOfTetra; ++elemIndex )
570     {
571       Ng_GetVolumeElement(Netgen_mesh, elemIndex, Netgen_tetrahedron);
572       try
573       {
574         helper.AddVolume (nodeVec.at( Netgen_tetrahedron[0] ),
575                           nodeVec.at( Netgen_tetrahedron[1] ),
576                           nodeVec.at( Netgen_tetrahedron[2] ),
577                           nodeVec.at( Netgen_tetrahedron[3] ));
578       }
579       catch (...)
580       {
581       }
582     }
583   }
584
585   return !err;
586 }
587
588 //================================================================================
589 /*!
590  * \brief Compute tetrahedral mesh from 2D mesh without geometry
591  */
592 //================================================================================
593
594 bool NETGENPlugin_NETGEN_3D::Compute(SMESH_Mesh&         aMesh,
595                                      SMESH_MesherHelper* aHelper)
596 {
597   const int invalid_ID = -1;
598
599   netgen::multithread.terminate = 0;
600   _progressByTic = -1.;
601
602   SMESH_MesherHelper::MType MeshType = aHelper->IsQuadraticMesh();
603   if ( MeshType == SMESH_MesherHelper::COMP )
604     return error( COMPERR_BAD_INPUT_MESH,
605                   SMESH_Comment("Mesh with linear and quadratic elements given"));
606
607   aHelper->SetIsQuadratic( MeshType == SMESH_MesherHelper::QUADRATIC );
608
609   // ---------------------------------
610   // Feed the Netgen with surface mesh
611   // ---------------------------------
612
613   int Netgen_NbOfNodes = 0;
614   double Netgen_point[3];
615   int Netgen_triangle[3];
616
617   NETGENPlugin_NetgenLibWrapper ngLib;
618   Ng_Mesh * Netgen_mesh = ngLib.ngMesh();
619
620   SMESH_ProxyMesh::Ptr proxyMesh( new SMESH_ProxyMesh( aMesh ));
621   if ( aMesh.NbQuadrangles() > 0 )
622   {
623     StdMeshers_QuadToTriaAdaptor* Adaptor = new StdMeshers_QuadToTriaAdaptor;
624     Adaptor->Compute(aMesh);
625     proxyMesh.reset( Adaptor );
626
627     if ( aHelper->IsQuadraticMesh() )
628     {
629       SMDS_ElemIteratorPtr fIt = proxyMesh->GetFaces();
630       while( fIt->more())
631         aHelper->AddTLinks( static_cast< const SMDS_MeshFace* >( fIt->next() ));
632     }
633   }
634
635   // maps nodes to ng ID
636   typedef map< const SMDS_MeshNode*, int, TIDCompare > TNodeToIDMap;
637   typedef TNodeToIDMap::value_type                     TN2ID;
638   TNodeToIDMap nodeToNetgenID;
639
640   SMDS_ElemIteratorPtr fIt = proxyMesh->GetFaces();
641   while( fIt->more())
642   {
643     // check element
644     const SMDS_MeshElement* elem = fIt->next();
645     if ( !elem )
646       return error( COMPERR_BAD_INPUT_MESH, "Null element encounters");
647     if ( elem->NbCornerNodes() != 3 )
648       return error( COMPERR_BAD_INPUT_MESH, "Not triangle element encounters");
649       
650     // add three nodes of triangle
651     for ( int iN = 0; iN < 3; ++iN )
652     {
653       const SMDS_MeshNode* node = elem->GetNode( iN );
654       int& ngID = nodeToNetgenID.insert(TN2ID( node, invalid_ID )).first->second;
655       if ( ngID == invalid_ID )
656       {
657         ngID = ++Netgen_NbOfNodes;
658         Netgen_point [ 0 ] = node->X();
659         Netgen_point [ 1 ] = node->Y();
660         Netgen_point [ 2 ] = node->Z();
661         Ng_AddPoint(Netgen_mesh, Netgen_point);
662       }
663       Netgen_triangle[ iN ] = ngID;
664     }
665     Ng_AddSurfaceElement(Netgen_mesh, NG_TRIG, Netgen_triangle);
666   }
667   proxyMesh.reset(); // delete tmp faces
668
669   // vector of nodes in which node index == netgen ID
670   vector< const SMDS_MeshNode* > nodeVec ( nodeToNetgenID.size() + 1 );
671   // insert old nodes into nodeVec
672   TNodeToIDMap::iterator n_id = nodeToNetgenID.begin();
673   for ( ; n_id != nodeToNetgenID.end(); ++n_id )
674     nodeVec.at( n_id->second ) = n_id->first;
675   nodeToNetgenID.clear();
676
677   // -------------------------
678   // Generate the volume mesh
679   // -------------------------
680
681   return ( ngLib._isComputeOk = compute( aMesh, *aHelper, nodeVec, ngLib ));
682 }
683
684 void NETGENPlugin_NETGEN_3D::CancelCompute()
685 {
686   SMESH_Algo::CancelCompute();
687   netgen::multithread.terminate = 1;
688 }
689
690 //================================================================================
691 /*!
692  * \brief Return Compute progress
693  */
694 //================================================================================
695
696 double NETGENPlugin_NETGEN_3D::GetProgress() const
697 {
698   double res;
699   const char* volMeshing = "Volume meshing";
700   const char* dlnMeshing = "Delaunay meshing";
701   const double meshingRatio = 0.15;
702   const_cast<NETGENPlugin_NETGEN_3D*>( this )->_progressTic++;
703
704   if ( _progressByTic < 0. &&
705        ( strncmp( netgen::multithread.task, dlnMeshing, 3 ) == 0 ||
706          strncmp( netgen::multithread.task, volMeshing, 3 ) == 0 ))
707   {
708     res = 0.001 + meshingRatio * netgen::multithread.percent / 100.;
709     //cout << netgen::multithread.task << " " <<_progressTic << "-" << netgen::multithread.percent << endl;
710   }
711   else // different otimizations
712   {
713     if ( _progressByTic < 0. )
714       ((NETGENPlugin_NETGEN_3D*)this)->_progressByTic = meshingRatio / _progressTic;
715     res = _progressByTic * _progressTic;
716     //cout << netgen::multithread.task << " " << _progressTic << " " << res << endl;
717   }
718   return Min ( res, 0.98 );
719 }
720
721 //=============================================================================
722 /*!
723  *
724  */
725 //=============================================================================
726
727 bool NETGENPlugin_NETGEN_3D::Evaluate(SMESH_Mesh& aMesh,
728                                       const TopoDS_Shape& aShape,
729                                       MapShapeNbElems& aResMap)
730 {
731   smIdType nbtri = 0, nbqua = 0;
732   double fullArea = 0.0;
733   for (TopExp_Explorer expF(aShape, TopAbs_FACE); expF.More(); expF.Next()) {
734     TopoDS_Face F = TopoDS::Face( expF.Current() );
735     SMESH_subMesh *sm = aMesh.GetSubMesh(F);
736     MapShapeNbElemsItr anIt = aResMap.find(sm);
737     if( anIt==aResMap.end() ) {
738       SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
739       smError.reset( new SMESH_ComputeError(COMPERR_ALGO_FAILED,"Submesh can not be evaluated",this));
740       return false;
741     }
742     std::vector<smIdType> aVec = (*anIt).second;
743     nbtri += std::max(aVec[SMDSEntity_Triangle],aVec[SMDSEntity_Quad_Triangle]);
744     nbqua += std::max(aVec[SMDSEntity_Quadrangle],aVec[SMDSEntity_Quad_Quadrangle]);
745     GProp_GProps G;
746     BRepGProp::SurfaceProperties(F,G);
747     double anArea = G.Mass();
748     fullArea += anArea;
749   }
750
751   // collect info from edges
752   smIdType nb0d_e = 0, nb1d_e = 0;
753   bool IsQuadratic = false;
754   bool IsFirst = true;
755   TopTools_MapOfShape tmpMap;
756   for (TopExp_Explorer expF(aShape, TopAbs_EDGE); expF.More(); expF.Next()) {
757     TopoDS_Edge E = TopoDS::Edge(expF.Current());
758     if( tmpMap.Contains(E) )
759       continue;
760     tmpMap.Add(E);
761     SMESH_subMesh *aSubMesh = aMesh.GetSubMesh(expF.Current());
762     MapShapeNbElemsItr anIt = aResMap.find(aSubMesh);
763     if( anIt==aResMap.end() ) {
764       SMESH_ComputeErrorPtr& smError = aSubMesh->GetComputeError();
765       smError.reset( new SMESH_ComputeError(COMPERR_ALGO_FAILED,
766                                             "Submesh can not be evaluated",this));
767       return false;
768     }
769     std::vector<smIdType> aVec = (*anIt).second;
770     nb0d_e += aVec[SMDSEntity_Node];
771     nb1d_e += std::max(aVec[SMDSEntity_Edge],aVec[SMDSEntity_Quad_Edge]);
772     if(IsFirst) {
773       IsQuadratic = (aVec[SMDSEntity_Quad_Edge] > aVec[SMDSEntity_Edge]);
774       IsFirst = false;
775     }
776   }
777   tmpMap.Clear();
778
779   double ELen_face = sqrt(2.* ( fullArea/double(nbtri+nbqua*2) ) / sqrt(3.0) );
780   double ELen_vol = pow( 72, 1/6. ) * pow( _maxElementVolume, 1/3. );
781   double ELen = Min(ELen_vol,ELen_face*2);
782
783   GProp_GProps G;
784   BRepGProp::VolumeProperties(aShape,G);
785   double aVolume = G.Mass();
786   double tetrVol = 0.1179*ELen*ELen*ELen;
787   double CoeffQuality = 0.9;
788   smIdType nbVols = (smIdType)( aVolume/tetrVol/CoeffQuality );
789   smIdType nb1d_f = (nbtri*3 + nbqua*4 - nb1d_e) / 2;
790   smIdType nb1d_in = (nbVols*6 - nb1d_e - nb1d_f ) / 5;
791   std::vector<smIdType> aVec(SMDSEntity_Last);
792   for(smIdType i=SMDSEntity_Node; i<SMDSEntity_Last; i++) aVec[i]=0;
793   if( IsQuadratic ) {
794     aVec[SMDSEntity_Node] = nb1d_in/6 + 1 + nb1d_in;
795     aVec[SMDSEntity_Quad_Tetra] = nbVols - nbqua*2;
796     aVec[SMDSEntity_Quad_Pyramid] = nbqua;
797   }
798   else {
799     aVec[SMDSEntity_Node] = nb1d_in/6 + 1;
800     aVec[SMDSEntity_Tetra] = nbVols - nbqua*2;
801     aVec[SMDSEntity_Pyramid] = nbqua;
802   }
803   SMESH_subMesh *sm = aMesh.GetSubMesh(aShape);
804   aResMap.insert(std::make_pair(sm,aVec));
805   
806   return true;
807 }
808
809