]> SALOME platform Git repositories - plugins/netgenplugin.git/blob - src/NETGENPlugin/NETGENPlugin_NETGEN_3D.cxx
Salome HOME
Working version for run_mesher for netgen 3d
[plugins/netgenplugin.git] / src / NETGENPlugin / NETGENPlugin_NETGEN_3D.cxx
1 // Copyright (C) 2007-2022  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 #include "NETGENPlugin_Provider.hxx"
35
36 #include "DriverStep.hxx"
37 #include "DriverMesh.hxx"
38 #include "netgen_param.hxx"
39
40 #include <SMDS_MeshElement.hxx>
41 #include <SMDS_MeshNode.hxx>
42 #include <SMESHDS_Mesh.hxx>
43 #include <SMESH_Comment.hxx>
44 #include <SMESH_ControlsDef.hxx>
45 #include <SMESH_Gen.hxx>
46 #include <SMESH_Mesh.hxx>
47 #include <SMESH_MeshEditor.hxx>
48 #include <SMESH_MesherHelper.hxx>
49 #include <SMESH_subMesh.hxx>
50 #include <StdMeshers_MaxElementVolume.hxx>
51 #include <StdMeshers_QuadToTriaAdaptor.hxx>
52 #include <StdMeshers_ViscousLayers.hxx>
53 #include <SMESH_subMesh.hxx>
54
55 #include <BRepGProp.hxx>
56 #include <BRep_Tool.hxx>
57 #include <GProp_GProps.hxx>
58 #include <TopExp.hxx>
59 #include <TopExp_Explorer.hxx>
60 #include <TopTools_ListIteratorOfListOfShape.hxx>
61 #include <TopoDS.hxx>
62
63 #include <Standard_Failure.hxx>
64 #include <Standard_ErrorHandler.hxx>
65
66 #include <utilities.h>
67
68 #include <list>
69 #include <vector>
70 #include <map>
71
72 #include <cstdlib>
73 #include <boost/filesystem.hpp>
74 namespace fs = boost::filesystem;
75
76 /*
77   Netgen include files
78 */
79
80 #ifndef OCCGEOMETRY
81 #define OCCGEOMETRY
82 #endif
83 #include <occgeom.hpp>
84
85 #ifdef NETGEN_V5
86 #include <ngexception.hpp>
87 #endif
88 #ifdef NETGEN_V6
89 #include <core/exception.hpp>
90 #endif
91
92 namespace nglib {
93 #include <nglib.h>
94 }
95 namespace netgen {
96
97   NETGENPLUGIN_DLL_HEADER
98
99   NETGENPLUGIN_DLL_HEADER
100   extern volatile multithreadt multithread;
101 }
102 using namespace nglib;
103 using namespace std;
104
105 //=============================================================================
106 /*!
107  *
108  */
109 //=============================================================================
110
111 NETGENPlugin_NETGEN_3D::NETGENPlugin_NETGEN_3D(int hypId, SMESH_Gen* gen)
112   : SMESH_3D_Algo(hypId, gen)
113 {
114   _name = "NETGEN_3D";
115   _shapeType = (1 << TopAbs_SHELL) | (1 << TopAbs_SOLID);// 1 bit /shape type
116   _compatibleHypothesis.push_back("MaxElementVolume");
117   _compatibleHypothesis.push_back("NETGEN_Parameters");
118   _compatibleHypothesis.push_back("ViscousLayers");
119
120   _maxElementVolume = 0.;
121
122   _hypMaxElementVolume = NULL;
123   _hypParameters = NULL;
124   _viscousLayersHyp = NULL;
125
126   _requireShape = false; // can work without shape
127 }
128
129 //=============================================================================
130 /*!
131  *
132  */
133 //=============================================================================
134
135 NETGENPlugin_NETGEN_3D::~NETGENPlugin_NETGEN_3D()
136 {
137 }
138
139 //=============================================================================
140 /*!
141  *
142  */
143 //=============================================================================
144
145 bool NETGENPlugin_NETGEN_3D::CheckHypothesis (SMESH_Mesh&         aMesh,
146                                               const TopoDS_Shape& aShape,
147                                               Hypothesis_Status&  aStatus)
148 {
149   _hypMaxElementVolume = NULL;
150   _hypParameters = NULL;
151   _viscousLayersHyp = NULL;
152   _maxElementVolume = DBL_MAX;
153
154   // for correct work of GetProgress():
155   //netgen::multithread.percent = 0.;
156   //netgen::multithread.task = "Volume meshing";
157   _progressByTic = -1.;
158
159   list<const SMESHDS_Hypothesis*>::const_iterator itl;
160   //const SMESHDS_Hypothesis* theHyp;
161
162   const list<const SMESHDS_Hypothesis*>& hyps =
163     GetUsedHypothesis(aMesh, aShape, /*ignoreAuxiliary=*/false);
164   list <const SMESHDS_Hypothesis* >::const_iterator h = hyps.begin();
165   if ( h == hyps.end())
166   {
167     aStatus = SMESH_Hypothesis::HYP_OK;
168     return true;  // can work with no hypothesis
169   }
170
171   aStatus = HYP_OK;
172   for ( ; h != hyps.end(); ++h )
173   {
174     if ( !_hypMaxElementVolume )
175       _hypMaxElementVolume = dynamic_cast< const StdMeshers_MaxElementVolume*> ( *h );
176     if ( !_viscousLayersHyp ) // several _viscousLayersHyp's allowed
177       _viscousLayersHyp = dynamic_cast< const StdMeshers_ViscousLayers*> ( *h );
178     if ( ! _hypParameters )
179       _hypParameters = dynamic_cast< const NETGENPlugin_Hypothesis*> ( *h );
180
181     if ( *h != _hypMaxElementVolume &&
182          *h != _viscousLayersHyp &&
183          *h != _hypParameters &&
184          !dynamic_cast< const StdMeshers_ViscousLayers*>(*h)) // several VL hyps allowed
185       aStatus = HYP_INCOMPATIBLE;
186   }
187   if ( _hypMaxElementVolume && _hypParameters )
188     aStatus = HYP_INCOMPATIBLE;
189   else if ( aStatus == HYP_OK && _viscousLayersHyp )
190     error( _viscousLayersHyp->CheckHypothesis( aMesh, aShape, aStatus ));
191
192   if ( _hypMaxElementVolume )
193     _maxElementVolume = _hypMaxElementVolume->GetMaxVolume();
194
195   return aStatus == HYP_OK;
196 }
197
198
199 void NETGENPlugin_NETGEN_3D::FillParameters(const NETGENPlugin_Hypothesis* hyp, netgen_params &aParams)
200 {
201   aParams.maxh               = hyp->GetMaxSize();
202   aParams.minh               = hyp->GetMinSize();
203   aParams.segmentsperedge    = hyp->GetNbSegPerEdge();
204   aParams.grading            = hyp->GetGrowthRate();
205   aParams.curvaturesafety    = hyp->GetNbSegPerRadius();
206   aParams.secondorder        = hyp->GetSecondOrder() ? 1 : 0;
207   aParams.quad               = hyp->GetQuadAllowed() ? 1 : 0;
208   aParams.optimize           = hyp->GetOptimize();
209   aParams.fineness           = hyp->GetFineness();
210   aParams.uselocalh          = hyp->GetSurfaceCurvature();
211   aParams.merge_solids       = hyp->GetFuseEdges();
212   aParams.chordalError       = hyp->GetChordalErrorEnabled() ? hyp->GetChordalError() : -1.;
213   aParams.optsteps2d         = aParams.optimize ? hyp->GetNbSurfOptSteps() : 0;
214   aParams.optsteps3d         = aParams.optimize ? hyp->GetNbVolOptSteps()  : 0;
215   aParams.elsizeweight       = hyp->GetElemSizeWeight();
216   aParams.opterrpow          = hyp->GetWorstElemMeasure();
217   aParams.delaunay           = hyp->GetUseDelauney();
218   aParams.checkoverlap       = hyp->GetCheckOverlapping();
219   aParams.checkchartboundary = hyp->GetCheckChartBoundary();
220 #ifdef NETGEN_V6
221   // std::string
222   aParams.meshsizefilename = hyp->GetMeshSizeFile();
223 #else
224   // const char*
225   aParams.meshsizefilename = hyp->GetMeshSizeFile().empty() ? 0 : hyp->GetMeshSizeFile().c_str();
226 #endif
227 #ifdef NETGEN_V6
228   aParams.closeedgefac = 2;
229 #else
230   aParams.closeedgefac = 0;
231 #endif
232 }
233
234 void NETGENPlugin_NETGEN_3D::exportElementOrientation(SMESH_Mesh& aMesh,
235                                                       const TopoDS_Shape& aShape,
236                                                       netgen_params& aParams,
237                                                       const std::string output_file)
238 {
239   SMESH_MesherHelper helper(aMesh);
240   NETGENPlugin_Internals internals( aMesh, aShape, /*is3D=*/true );
241   SMESH_ProxyMesh::Ptr proxyMesh( new SMESH_ProxyMesh( aMesh ));
242   std::map<vtkIdType, bool> elemOrientation;
243
244   for ( TopExp_Explorer exFa( aShape, TopAbs_FACE ); exFa.More(); exFa.Next())
245   {
246     const TopoDS_Shape& aShapeFace = exFa.Current();
247     int faceID = aMesh.GetMeshDS()->ShapeToIndex( aShapeFace );
248     bool isInternalFace = internals.isInternalShape( faceID );
249     bool isRev = false;
250     if ( !isInternalFace &&
251           helper.NbAncestors(aShapeFace, aMesh, aShape.ShapeType()) > 1 )
252       // IsReversedSubMesh() can work wrong on strongly curved faces,
253       // so we use it as less as possible
254       isRev = helper.IsReversedSubMesh( TopoDS::Face( aShapeFace ));
255
256     const SMESHDS_SubMesh * aSubMeshDSFace = proxyMesh->GetSubMesh( aShapeFace );
257     if ( !aSubMeshDSFace ) continue;
258
259     SMDS_ElemIteratorPtr iteratorElem = aSubMeshDSFace->GetElements();
260     if ( aParams._quadraticMesh &&
261           dynamic_cast< const SMESH_ProxyMesh::SubMesh*>( aSubMeshDSFace ))
262     {
263       // add medium nodes of proxy triangles to helper (#16843)
264       while ( iteratorElem->more() )
265         helper.AddTLinks( static_cast< const SMDS_MeshFace* >( iteratorElem->next() ));
266
267       iteratorElem = aSubMeshDSFace->GetElements();
268     }
269     while ( iteratorElem->more() ) // loop on elements on a geom face
270     {
271       // check mesh face
272       const SMDS_MeshElement* elem = iteratorElem->next();
273       if ( !elem )
274         error( COMPERR_BAD_INPUT_MESH, "Null element encounters");
275       if ( elem->NbCornerNodes() != 3 )
276         error( COMPERR_BAD_INPUT_MESH, "Not triangle element encounters");
277       elemOrientation[elem->GetID()] = isRev;
278       // Add nodes of triangles and triangles them-selves to netgen mesh
279
280       // add three nodes of triangle
281 /*      bool hasDegen = false;
282       for ( int iN = 0; iN < 3; ++iN )
283       {
284         const SMDS_MeshNode* node = elem->GetNode( iN );
285         const int shapeID = node->getshapeId();
286         if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_EDGE &&
287               helper.IsDegenShape( shapeID ))
288         {
289           // ignore all nodes on degeneraged edge and use node on its vertex instead
290           TopoDS_Shape vertex = TopoDS_Iterator( meshDS->IndexToShape( shapeID )).Value();
291           node = SMESH_Algo::VertexNode( TopoDS::Vertex( vertex ), meshDS );
292           hasDegen = true;
293         }
294         int& ngID = nodeToNetgenID.insert(TN2ID( node, invalid_ID )).first->second;
295         if ( ngID == invalid_ID )
296         {
297           ngID = ++Netgen_NbOfNodes;
298           Netgen_point [ 0 ] = node->X();
299           Netgen_point [ 1 ] = node->Y();
300           Netgen_point [ 2 ] = node->Z();
301           Ng_AddPoint(Netgen_mesh, Netgen_point);
302         }
303         Netgen_triangle[ isRev ? 2-iN : iN ] = ngID;
304       }
305       // add triangle
306       if ( hasDegen && (Netgen_triangle[0] == Netgen_triangle[1] ||
307                         Netgen_triangle[0] == Netgen_triangle[2] ||
308                         Netgen_triangle[2] == Netgen_triangle[1] ))
309         continue;
310
311       Ng_AddSurfaceElement(Netgen_mesh, NG_TRIG, Netgen_triangle);
312
313       if ( isInternalFace && !proxyMesh->IsTemporary( elem ))
314       {
315         swap( Netgen_triangle[1], Netgen_triangle[2] );
316         Ng_AddSurfaceElement(Netgen_mesh, NG_TRIG, Netgen_triangle);
317       }*/
318     } // loop on elements on a face
319   } // loop on faces of a SOLID or SHELL
320
321   std::ofstream df(output_file, ios::out|ios::binary);
322   int size=elemOrientation.size();
323   std::cout << size<< std::endl;
324   std::cout << "vtkIdType " << sizeof(vtkIdType) << std::endl;
325
326   df.write((char*)&size, sizeof(int));
327   for(auto const& [id, orient]:elemOrientation){
328     df.write((char*)&id, sizeof(vtkIdType));
329     df.write((char*)&orient, sizeof(bool));
330   }
331   df.close();
332   // for(auto const& [id, orient] : elemOrientation)
333   //   {
334   //     std::cout << id << " : " << orient << ", ";
335   //   }
336 }
337
338 int NETGENPlugin_NETGEN_3D::ParallelCompute(SMESH_Mesh&         aMesh,
339                                              const TopoDS_Shape& aShape)
340 {
341   aMesh.Lock();
342   SMESH_Hypothesis::Hypothesis_Status hypStatus;
343   CheckHypothesis(aMesh, aShape, hypStatus);
344
345   // Temporary folder for run
346   fs::path tmp_folder = aMesh.tmp_folder / fs::unique_path(fs::path("Volume-%%%%-%%%%"));
347   fs::create_directories(tmp_folder);
348   // Using MESH2D generated after all triangles where created.
349   fs::path mesh_file=aMesh.tmp_folder / fs::path("Mesh2D.med");
350   fs::path element_orientation_file=tmp_folder / fs::path("element_orientation.dat");
351   fs::path new_element_file=tmp_folder / fs::path("new_elements.dat");
352   fs::path tmp_mesh_file=tmp_folder / fs::path("tmp_mesh.med");
353   // TODO: Remove that file we do not use it
354   fs::path output_mesh_file=tmp_folder / fs::path("output_mesh.med");
355   fs::path shape_file=tmp_folder / fs::path("shape.step");
356   fs::path param_file=tmp_folder / fs::path("netgen3d_param.txt");
357   fs::path log_file=tmp_folder / fs::path("run_mesher.log");
358   //TODO: Handle variable mesh_name
359   std::string mesh_name = "Maillage_1";
360
361   //Writing Shape
362   export_shape(shape_file.string(), aShape);
363   //Writing hypo
364   netgen_params aParams;
365   FillParameters(_hypParameters, aParams);
366
367   export_netgen_params(param_file.string(), aParams);
368
369   // Exporting element orientation
370   exportElementOrientation(aMesh, aShape, aParams, element_orientation_file.string());
371
372   aMesh.Unlock();
373   // Calling run_mesher
374   std::string cmd;
375   // TODO: Add run_meher to bin
376   std::string run_mesher_exe = "/home/B61570/work_in_progress/ssmesh/run_mesher/build/src/run_mesher";
377   cmd = run_mesher_exe +
378                   " NETGEN3D " + mesh_file.string() + " "
379                                + shape_file.string() + " "
380                                + param_file.string() + " "
381                                + element_orientation_file.string() + " "
382                                + new_element_file.string() + " "
383                                + output_mesh_file.string() +
384                                " >> " + log_file.string();
385
386   std::cout << "Running command: " << std::endl;
387   std::cout << cmd << std::endl;
388
389   // Writing command in log
390   std::ofstream flog(log_file.string());
391   flog << cmd << endl;
392   flog.close();
393
394   int ret = system(cmd.c_str());
395
396   // TODO: error handling
397   if(ret != 0){
398     // Run crahed
399     //throw Exception("Meshing failed");
400     std::cerr << "Issue with command: " << std::endl;
401     std::cerr << cmd << std::endl;
402     return false;
403   }
404
405
406   aMesh.Lock();
407   std::ifstream df(new_element_file.string(), ios::binary);
408
409
410   int Netgen_NbOfNodes;
411   int Netgen_NbOfNodesNew;
412   int Netgen_NbOfTetra;
413   double Netgen_point[3];
414   int    Netgen_tetrahedron[4];
415   int nodeID;
416
417   SMESH_MesherHelper helper(aMesh);
418   // This function
419   int _quadraticMesh = helper.IsQuadraticSubMesh(aShape);
420   helper.SetElementsOnShape( true );
421
422   // Filling nodevec (correspondence netgen numbering mesh numbering)
423   // Number of nodes
424   df.read((char*) &Netgen_NbOfNodes, sizeof(int));
425   df.read((char*) &Netgen_NbOfNodesNew, sizeof(int));
426
427   vector< const SMDS_MeshNode* > nodeVec ( Netgen_NbOfNodesNew + 1 );
428
429   for (int nodeIndex = 1 ; nodeIndex <= Netgen_NbOfNodes; ++nodeIndex )
430   {
431     //Id of the point
432     df.read((char*) &nodeID, sizeof(int));
433     // std::cout << "Old Node " << nodeIndex << ": " << nodeID << std::endl;
434     // TODO: do stuff to fill nodeVec ??
435     nodeVec.at(nodeIndex) = nullptr;
436     SMDS_NodeIteratorPtr iteratorNode = aMesh.GetMeshDS()->nodesIterator();
437     while(iteratorNode->more()){
438       const SMDS_MeshNode* node = iteratorNode->next();
439       if(node->GetID() == nodeID){
440         nodeVec.at(nodeIndex) = node;
441         break;
442       }
443     }
444     if(nodeVec.at(nodeIndex) == nullptr){
445       std::cout << "Error could not identify id";
446       return false;
447     }
448   }
449
450   // Writing info on new points
451   for (int nodeIndex = Netgen_NbOfNodes +1 ; nodeIndex <= Netgen_NbOfNodesNew; ++nodeIndex )
452   {
453     // Coordinates of the point
454     df.read((char *) &Netgen_point, sizeof(double)*3);
455     // std::cout << "Node " << nodeIndex << ": ";
456     // for(auto coord:Netgen_point){
457     //   std::cout << coord << " ";
458     // }
459     // std::cout << std::endl;
460     nodeVec.at(nodeIndex) = helper.AddNode(Netgen_point[0],
461                                            Netgen_point[1],
462                                            Netgen_point[2]);
463
464   }
465
466   // create tetrahedrons
467   df.read((char*) &Netgen_NbOfTetra, sizeof(int));
468   for ( int elemIndex = 1; elemIndex <= Netgen_NbOfTetra; ++elemIndex )
469   {
470     df.read((char*) &Netgen_tetrahedron, sizeof(int)*4);
471     // std::cout << "Element " << elemIndex << ": ";
472     // for(auto elem:Netgen_tetrahedron){
473     //   std::cout << elem << " ";
474     // }
475     // std::cout << std::endl;
476     // TODO: Add tetra
477     helper.AddVolume (nodeVec.at( Netgen_tetrahedron[0] ),
478                       nodeVec.at( Netgen_tetrahedron[1] ),
479                       nodeVec.at( Netgen_tetrahedron[2] ),
480                       nodeVec.at( Netgen_tetrahedron[3] ));
481   }
482   df.close();
483
484   aMesh.Unlock();
485
486   return true;
487 }
488
489 //=============================================================================
490 /*!
491  *Here we are going to use the NETGEN mesher
492  */
493 //=============================================================================
494
495 bool NETGENPlugin_NETGEN_3D::Compute(SMESH_Mesh&         aMesh,
496                                      const TopoDS_Shape& aShape)
497 {
498
499   return ParallelCompute(aMesh, aShape);
500   aMesh.Lock();
501   SMESH_Hypothesis::Hypothesis_Status hypStatus;
502   CheckHypothesis(aMesh, aShape, hypStatus);
503   aMesh.Unlock();
504
505   //netgen::multithread.terminate = 0;
506   //netgen::multithread.task = "Volume meshing";
507   _progressByTic = -1.;
508
509   SMESHDS_Mesh* meshDS = aMesh.GetMeshDS();
510
511   SMESH_MesherHelper *helper = new SMESH_MesherHelper(aMesh);
512   _quadraticMesh = helper->IsQuadraticSubMesh(aShape);
513   helper->SetElementsOnShape( true );
514
515   int Netgen_NbOfNodes = 0;
516   double Netgen_point[3];
517   int Netgen_triangle[3];
518
519   NETGENPlugin_NetgenLibWrapper *ngLib;
520   int id_nglib = nglib_provider.take(&ngLib);
521   Ng_Mesh * Netgen_mesh = (Ng_Mesh*)ngLib->_ngMesh;
522
523   // vector of nodes in which node index == netgen ID
524   vector< const SMDS_MeshNode* > nodeVec;
525   aMesh.Lock();
526   {
527     const int invalid_ID = -1;
528
529     SMESH::Controls::Area areaControl;
530     SMESH::Controls::TSequenceOfXYZ nodesCoords;
531
532     // maps nodes to ng ID
533     typedef map< const SMDS_MeshNode*, int, TIDCompare > TNodeToIDMap;
534     typedef TNodeToIDMap::value_type                     TN2ID;
535     TNodeToIDMap nodeToNetgenID;
536
537     // find internal shapes
538     NETGENPlugin_Internals internals( aMesh, aShape, /*is3D=*/true );
539
540     // ---------------------------------
541     // Feed the Netgen with surface mesh
542     // ---------------------------------
543
544     TopAbs_ShapeEnum mainType = aMesh.GetShapeToMesh().ShapeType();
545     bool checkReverse = ( mainType == TopAbs_COMPOUND || mainType == TopAbs_COMPSOLID );
546
547     SMESH_ProxyMesh::Ptr proxyMesh( new SMESH_ProxyMesh( aMesh ));
548     if ( _viscousLayersHyp )
549     {
550       //netgen::multithread.percent = 3;
551       proxyMesh = _viscousLayersHyp->Compute( aMesh, aShape );
552       if ( !proxyMesh )
553         return false;
554     }
555     if ( aMesh.NbQuadrangles() > 0 )
556     {
557       //netgen::multithread.percent = 6;
558       StdMeshers_QuadToTriaAdaptor* Adaptor = new StdMeshers_QuadToTriaAdaptor;
559       Adaptor->Compute(aMesh,aShape,proxyMesh.get());
560       proxyMesh.reset( Adaptor );
561     }
562
563     for ( TopExp_Explorer exFa( aShape, TopAbs_FACE ); exFa.More(); exFa.Next())
564     {
565       const TopoDS_Shape& aShapeFace = exFa.Current();
566       int faceID = meshDS->ShapeToIndex( aShapeFace );
567       bool isInternalFace = internals.isInternalShape( faceID );
568       bool isRev = false;
569       if ( checkReverse && !isInternalFace &&
570            helper->NbAncestors(aShapeFace, aMesh, aShape.ShapeType()) > 1 )
571         // IsReversedSubMesh() can work wrong on strongly curved faces,
572         // so we use it as less as possible
573         isRev = helper->IsReversedSubMesh( TopoDS::Face( aShapeFace ));
574
575       const SMESHDS_SubMesh * aSubMeshDSFace = proxyMesh->GetSubMesh( aShapeFace );
576       if ( !aSubMeshDSFace ) continue;
577
578       SMDS_ElemIteratorPtr iteratorElem = aSubMeshDSFace->GetElements();
579       if ( _quadraticMesh &&
580            dynamic_cast< const SMESH_ProxyMesh::SubMesh*>( aSubMeshDSFace ))
581       {
582         // add medium nodes of proxy triangles to helper (#16843)
583         while ( iteratorElem->more() )
584           helper->AddTLinks( static_cast< const SMDS_MeshFace* >( iteratorElem->next() ));
585
586         iteratorElem = aSubMeshDSFace->GetElements();
587       }
588       while ( iteratorElem->more() ) // loop on elements on a geom face
589       {
590         // check mesh face
591         const SMDS_MeshElement* elem = iteratorElem->next();
592         if ( !elem )
593           return error( COMPERR_BAD_INPUT_MESH, "Null element encounters");
594         if ( elem->NbCornerNodes() != 3 )
595           return error( COMPERR_BAD_INPUT_MESH, "Not triangle element encounters");
596
597         // Add nodes of triangles and triangles them-selves to netgen mesh
598
599         // add three nodes of triangle
600         bool hasDegen = false;
601         for ( int iN = 0; iN < 3; ++iN )
602         {
603           const SMDS_MeshNode* node = elem->GetNode( iN );
604           const int shapeID = node->getshapeId();
605           if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_EDGE &&
606                helper->IsDegenShape( shapeID ))
607           {
608             // ignore all nodes on degeneraged edge and use node on its vertex instead
609             TopoDS_Shape vertex = TopoDS_Iterator( meshDS->IndexToShape( shapeID )).Value();
610             node = SMESH_Algo::VertexNode( TopoDS::Vertex( vertex ), meshDS );
611             hasDegen = true;
612           }
613           int& ngID = nodeToNetgenID.insert(TN2ID( node, invalid_ID )).first->second;
614           if ( ngID == invalid_ID )
615           {
616             ngID = ++Netgen_NbOfNodes;
617             Netgen_point [ 0 ] = node->X();
618             Netgen_point [ 1 ] = node->Y();
619             Netgen_point [ 2 ] = node->Z();
620             Ng_AddPoint(Netgen_mesh, Netgen_point);
621           }
622           Netgen_triangle[ isRev ? 2-iN : iN ] = ngID;
623         }
624         // add triangle
625         if ( hasDegen && (Netgen_triangle[0] == Netgen_triangle[1] ||
626                           Netgen_triangle[0] == Netgen_triangle[2] ||
627                           Netgen_triangle[2] == Netgen_triangle[1] ))
628           continue;
629
630         Ng_AddSurfaceElement(Netgen_mesh, NG_TRIG, Netgen_triangle);
631
632         if ( isInternalFace && !proxyMesh->IsTemporary( elem ))
633         {
634           swap( Netgen_triangle[1], Netgen_triangle[2] );
635           Ng_AddSurfaceElement(Netgen_mesh, NG_TRIG, Netgen_triangle);
636         }
637       } // loop on elements on a face
638     } // loop on faces of a SOLID or SHELL
639
640     // insert old nodes into nodeVec
641     nodeVec.resize( nodeToNetgenID.size() + 1, 0 );
642     TNodeToIDMap::iterator n_id = nodeToNetgenID.begin();
643     for ( ; n_id != nodeToNetgenID.end(); ++n_id )
644       nodeVec[ n_id->second ] = n_id->first;
645     nodeToNetgenID.clear();
646
647     // TODO: Handle internal vertex
648     if ( internals.hasInternalVertexInSolid() )
649     {
650       netgen::OCCGeometry occgeo;
651       NETGENPlugin_Mesher::AddIntVerticesInSolids( occgeo,
652                                                    (netgen::Mesh&) *Netgen_mesh,
653                                                    nodeVec,
654                                                    internals);
655     }
656   }
657   aMesh.Unlock();
658
659   // -------------------------
660   // Generate the volume mesh
661   // -------------------------
662   ngLib->_isComputeOk = compute( aMesh, *helper, nodeVec, *ngLib );
663   bool ret = ngLib->_isComputeOk;
664   nglib_provider.release(id_nglib, true);
665
666   return ret;
667 }
668
669 // namespace
670 // {
671 //   void limitVolumeSize( netgen::Mesh* ngMesh,
672 //                         double        maxh )
673 //   {
674 //     // get average h of faces
675 //     double faceh = 0;
676 //     int nbh = 0;
677 //     for (int i = 1; i <= ngMesh->GetNSE(); i++)
678 //     {
679 //       const netgen::Element2d& face = ngMesh->SurfaceElement(i);
680 //       for (int j=1; j <= face.GetNP(); ++j)
681 //       {
682 //         const netgen::PointIndex & i1 = face.PNumMod(j);
683 //         const netgen::PointIndex & i2 = face.PNumMod(j+1);
684 //         if ( i1 < i2 )
685 //         {
686 //           const netgen::Point3d & p1 = ngMesh->Point( i1 );
687 //           const netgen::Point3d & p2 = ngMesh->Point( i2 );
688 //           faceh += netgen::Dist2( p1, p2 );
689 //           nbh++;
690 //         }
691 //       }
692 //     }
693 //     faceh = Sqrt( faceh / nbh );
694
695 //     double compareh;
696 //     if      ( faceh < 0.5 * maxh ) compareh = -1;
697 //     else if ( faceh > 1.5 * maxh ) compareh = 1;
698 //     else                           compareh = 0;
699 //     // cerr << "faceh " << faceh << endl;
700 //     // cerr << "init maxh " << maxh << endl;
701 //     // cerr << "compareh " << compareh << endl;
702
703 //     if ( compareh > 0 )
704 //       maxh *= 1.2;
705 //     else
706 //       maxh *= 0.8;
707 //     // cerr << "maxh " << maxh << endl;
708
709 //     // get bnd box
710 //     netgen::Point3d pmin, pmax;
711 //     ngMesh->GetBox( pmin, pmax, 0 );
712 //     const double dx = pmax.X() - pmin.X();
713 //     const double dy = pmax.Y() - pmin.Y();
714 //     const double dz = pmax.Z() - pmin.Z();
715
716 //     if ( ! & ngMesh->LocalHFunction() )
717 //       ngMesh->SetLocalH( pmin, pmax, compareh <= 0 ? 0.1 : 0.5 );
718
719 //     // adjusted by SALOME_TESTS/Grids/smesh/bugs_08/I8
720 //     const int nbX = Max( 2, int( dx / maxh * 2 ));
721 //     const int nbY = Max( 2, int( dy / maxh * 2 ));
722 //     const int nbZ = Max( 2, int( dz / maxh * 2 ));
723
724 //     netgen::Point3d p;
725 //     for ( int i = 0; i <= nbX; ++i )
726 //     {
727 //       p.X() = pmin.X() +  i * dx / nbX;
728 //       for ( int j = 0; j <= nbY; ++j )
729 //       {
730 //         p.Y() = pmin.Y() +  j * dy / nbY;
731 //         for ( int k = 0; k <= nbZ; ++k )
732 //         {
733 //           p.Z() = pmin.Z() +  k * dz / nbZ;
734 //           ngMesh->RestrictLocalH( p, maxh );
735 //         }
736 //       }
737 //     }
738 //   }
739 // }
740
741 //================================================================================
742 /*!
743  * \brief set parameters and generate the volume mesh
744  */
745 //================================================================================
746
747 bool NETGENPlugin_NETGEN_3D::compute(SMESH_Mesh&                     aMesh,
748                                      SMESH_MesherHelper&             helper,
749                                      vector< const SMDS_MeshNode* >& nodeVec,
750                                      NETGENPlugin_NetgenLibWrapper&  ngLib)
751 {
752   //netgen::multithread.terminate = 0;
753
754   netgen::Mesh* ngMesh = ngLib._ngMesh;
755   Ng_Mesh* Netgen_mesh = ngLib.ngMesh();
756   int Netgen_NbOfNodes = Ng_GetNP( Netgen_mesh );
757
758   int startWith = netgen::MESHCONST_MESHVOLUME;
759   int endWith   = netgen::MESHCONST_OPTVOLUME;
760   int err = 1;
761
762   NETGENPlugin_Mesher aMesher( &aMesh, helper.GetSubShape(), /*isVolume=*/true );
763   netgen::OCCGeometry *occgeo;
764   int id_occgeo = occgeom_provider.take(&occgeo);
765   netgen::MeshingParameters mparam;
766   int id_mparam = mparam_provider.take(mparam);
767   aMesher.SetDefaultParameters(mparam);
768
769   if ( _hypParameters )
770   {
771     aMesher.SetParameters( _hypParameters, mparam );
772
773     if ( !_hypParameters->GetLocalSizesAndEntries().empty() ||
774          !_hypParameters->GetMeshSizeFile().empty() )
775     {
776       if ( ! &ngMesh->LocalHFunction() )
777       {
778         netgen::Point3d pmin, pmax;
779         ngMesh->GetBox( pmin, pmax, 0 );
780         ngMesh->SetLocalH( pmin, pmax, _hypParameters->GetGrowthRate() );
781       }
782       aMesher.SetLocalSize( *occgeo, *ngMesh );
783
784       try {
785         ngMesh->LoadLocalMeshSize( mparam.meshsizefilename );
786       } catch (netgen::NgException & ex) {
787         return error( COMPERR_BAD_PARMETERS, ex.What() );
788       }
789     }
790     if ( !_hypParameters->GetOptimize() )
791       endWith = netgen::MESHCONST_MESHVOLUME;
792   }
793   else if ( _hypMaxElementVolume )
794   {
795     mparam.maxh = pow( 72, 1/6. ) * pow( _maxElementVolume, 1/3. );
796     // limitVolumeSize( ngMesh, mparam.maxh ); // result is unpredictable
797   }
798   else if ( aMesh.HasShapeToMesh() )
799   {
800     aMesher.PrepareOCCgeometry( *occgeo, helper.GetSubShape(), aMesh );
801     mparam.maxh = occgeo->GetBoundingBox().Diam()/2;
802   }
803   else
804   {
805     netgen::Point3d pmin, pmax;
806     ngMesh->GetBox (pmin, pmax);
807     mparam.maxh = Dist(pmin, pmax)/2;
808   }
809
810   if ( !_hypParameters && aMesh.HasShapeToMesh() )
811   {
812     mparam.minh = aMesher.GetDefaultMinSize( helper.GetSubShape(), mparam.maxh );
813   }
814
815   try
816   {
817     OCC_CATCH_SIGNALS;
818
819     ngLib.CalcLocalH(ngMesh);
820     err = ngLib.GenerateMesh(*occgeo, startWith, endWith, ngMesh, mparam);
821
822     if(netgen::multithread.terminate)
823       return false;
824     if ( err )
825       error(SMESH_Comment("Error in netgen::OCCGenerateMesh() at ") << netgen::multithread.task);
826   }
827   catch (Standard_Failure& ex)
828   {
829     SMESH_Comment str("Exception in  netgen::OCCGenerateMesh()");
830     str << " at " << netgen::multithread.task
831         << ": " << ex.DynamicType()->Name();
832     if ( ex.GetMessageString() && strlen( ex.GetMessageString() ))
833       str << ": " << ex.GetMessageString();
834     error(str);
835   }
836   catch (netgen::NgException& exc)
837   {
838     SMESH_Comment str("NgException");
839     if ( strlen( netgen::multithread.task ) > 0 )
840       str << " at " << netgen::multithread.task;
841     str << ": " << exc.What();
842     error(str);
843   }
844   catch (...)
845   {
846     SMESH_Comment str("Exception in  netgen::OCCGenerateMesh()");
847     if ( strlen( netgen::multithread.task ) > 0 )
848       str << " at " << netgen::multithread.task;
849     error(str);
850   }
851
852   int Netgen_NbOfNodesNew = Ng_GetNP(Netgen_mesh);
853   int Netgen_NbOfTetra    = Ng_GetNE(Netgen_mesh);
854
855   // -------------------------------------------------------------------
856   // Feed back the SMESHDS with the generated Nodes and Volume Elements
857   // -------------------------------------------------------------------
858
859   if ( err )
860   {
861     SMESH_ComputeErrorPtr ce = NETGENPlugin_Mesher::ReadErrors(nodeVec);
862     if ( ce && ce->HasBadElems() )
863       error( ce );
864   }
865
866   mparam_provider.release(id_mparam);
867   occgeom_provider.release(id_occgeo, true);
868
869   aMesh.Lock();
870   bool isOK = ( /*status == NG_OK &&*/ Netgen_NbOfTetra > 0 );// get whatever built
871   if ( isOK )
872   {
873     double Netgen_point[3];
874     int    Netgen_tetrahedron[4];
875
876     // create and insert new nodes into nodeVec
877     nodeVec.resize( Netgen_NbOfNodesNew + 1, 0 );
878     int nodeIndex = Netgen_NbOfNodes + 1;
879     for ( ; nodeIndex <= Netgen_NbOfNodesNew; ++nodeIndex )
880     {
881       Ng_GetPoint( Netgen_mesh, nodeIndex, Netgen_point );
882       nodeVec.at(nodeIndex) = helper.AddNode(Netgen_point[0], Netgen_point[1], Netgen_point[2]);
883     }
884
885     // create tetrahedrons
886     for ( int elemIndex = 1; elemIndex <= Netgen_NbOfTetra; ++elemIndex )
887     {
888       Ng_GetVolumeElement(Netgen_mesh, elemIndex, Netgen_tetrahedron);
889       try
890       {
891         helper.AddVolume (nodeVec.at( Netgen_tetrahedron[0] ),
892                           nodeVec.at( Netgen_tetrahedron[1] ),
893                           nodeVec.at( Netgen_tetrahedron[2] ),
894                           nodeVec.at( Netgen_tetrahedron[3] ));
895       }
896       catch (...)
897       {
898       }
899     }
900   }
901   aMesh.Unlock();
902
903
904   return !err;
905 }
906
907 //================================================================================
908 /*!
909  * \brief Compute tetrahedral mesh from 2D mesh without geometry
910  */
911 //================================================================================
912
913 bool NETGENPlugin_NETGEN_3D::Compute(SMESH_Mesh&         aMesh,
914                                      SMESH_MesherHelper* aHelper)
915 {
916   const int invalid_ID = -1;
917
918   netgen::multithread.terminate = 0;
919   _progressByTic = -1.;
920
921   SMESH_MesherHelper::MType MeshType = aHelper->IsQuadraticMesh();
922   if ( MeshType == SMESH_MesherHelper::COMP )
923     return error( COMPERR_BAD_INPUT_MESH,
924                   SMESH_Comment("Mesh with linear and quadratic elements given"));
925
926   aHelper->SetIsQuadratic( MeshType == SMESH_MesherHelper::QUADRATIC );
927
928   // ---------------------------------
929   // Feed the Netgen with surface mesh
930   // ---------------------------------
931
932   int Netgen_NbOfNodes = 0;
933   double Netgen_point[3];
934   int Netgen_triangle[3];
935
936   NETGENPlugin_NetgenLibWrapper ngLib;
937   Ng_Mesh * Netgen_mesh = ngLib.ngMesh();
938
939   SMESH_ProxyMesh::Ptr proxyMesh( new SMESH_ProxyMesh( aMesh ));
940   if ( aMesh.NbQuadrangles() > 0 )
941   {
942     StdMeshers_QuadToTriaAdaptor* Adaptor = new StdMeshers_QuadToTriaAdaptor;
943     Adaptor->Compute(aMesh);
944     proxyMesh.reset( Adaptor );
945
946     if ( aHelper->IsQuadraticMesh() )
947     {
948       SMDS_ElemIteratorPtr fIt = proxyMesh->GetFaces();
949       while( fIt->more())
950         aHelper->AddTLinks( static_cast< const SMDS_MeshFace* >( fIt->next() ));
951     }
952   }
953
954   // maps nodes to ng ID
955   typedef map< const SMDS_MeshNode*, int, TIDCompare > TNodeToIDMap;
956   typedef TNodeToIDMap::value_type                     TN2ID;
957   TNodeToIDMap nodeToNetgenID;
958
959   SMDS_ElemIteratorPtr fIt = proxyMesh->GetFaces();
960   while( fIt->more())
961   {
962     // check element
963     const SMDS_MeshElement* elem = fIt->next();
964     if ( !elem )
965       return error( COMPERR_BAD_INPUT_MESH, "Null element encounters");
966     if ( elem->NbCornerNodes() != 3 )
967       return error( COMPERR_BAD_INPUT_MESH, "Not triangle element encounters");
968
969     // add three nodes of triangle
970     for ( int iN = 0; iN < 3; ++iN )
971     {
972       const SMDS_MeshNode* node = elem->GetNode( iN );
973       int& ngID = nodeToNetgenID.insert(TN2ID( node, invalid_ID )).first->second;
974       if ( ngID == invalid_ID )
975       {
976         ngID = ++Netgen_NbOfNodes;
977         Netgen_point [ 0 ] = node->X();
978         Netgen_point [ 1 ] = node->Y();
979         Netgen_point [ 2 ] = node->Z();
980         Ng_AddPoint(Netgen_mesh, Netgen_point);
981       }
982       Netgen_triangle[ iN ] = ngID;
983     }
984     Ng_AddSurfaceElement(Netgen_mesh, NG_TRIG, Netgen_triangle);
985   }
986   proxyMesh.reset(); // delete tmp faces
987
988   // vector of nodes in which node index == netgen ID
989   vector< const SMDS_MeshNode* > nodeVec ( nodeToNetgenID.size() + 1 );
990   // insert old nodes into nodeVec
991   TNodeToIDMap::iterator n_id = nodeToNetgenID.begin();
992   for ( ; n_id != nodeToNetgenID.end(); ++n_id )
993     nodeVec.at( n_id->second ) = n_id->first;
994   nodeToNetgenID.clear();
995
996   // -------------------------
997   // Generate the volume mesh
998   // -------------------------
999
1000   return ( ngLib._isComputeOk = compute( aMesh, *aHelper, nodeVec, ngLib ));
1001 }
1002
1003 void NETGENPlugin_NETGEN_3D::CancelCompute()
1004 {
1005   SMESH_Algo::CancelCompute();
1006   netgen::multithread.terminate = 1;
1007 }
1008
1009 //================================================================================
1010 /*!
1011  * \brief Return Compute progress
1012  */
1013 //================================================================================
1014
1015 double NETGENPlugin_NETGEN_3D::GetProgress() const
1016 {
1017   double res;
1018   const char* volMeshing = "Volume meshing";
1019   const char* dlnMeshing = "Delaunay meshing";
1020   const double meshingRatio = 0.15;
1021   const_cast<NETGENPlugin_NETGEN_3D*>( this )->_progressTic++;
1022
1023   if ( _progressByTic < 0. &&
1024        ( strncmp( netgen::multithread.task, dlnMeshing, 3 ) == 0 ||
1025          strncmp( netgen::multithread.task, volMeshing, 3 ) == 0 ))
1026   {
1027     res = 0.001 + meshingRatio * netgen::multithread.percent / 100.;
1028     //cout << netgen::multithread.task << " " <<_progressTic << "-" << netgen::multithread.percent << endl;
1029   }
1030   else // different otimizations
1031   {
1032     if ( _progressByTic < 0. )
1033       ((NETGENPlugin_NETGEN_3D*)this)->_progressByTic = meshingRatio / _progressTic;
1034     res = _progressByTic * _progressTic;
1035     //cout << netgen::multithread.task << " " << _progressTic << " " << res << endl;
1036   }
1037   return Min ( res, 0.98 );
1038 }
1039
1040 //=============================================================================
1041 /*!
1042  *
1043  */
1044 //=============================================================================
1045
1046 bool NETGENPlugin_NETGEN_3D::Evaluate(SMESH_Mesh& aMesh,
1047                                       const TopoDS_Shape& aShape,
1048                                       MapShapeNbElems& aResMap)
1049 {
1050   smIdType nbtri = 0, nbqua = 0;
1051   double fullArea = 0.0;
1052   for (TopExp_Explorer expF(aShape, TopAbs_FACE); expF.More(); expF.Next()) {
1053     TopoDS_Face F = TopoDS::Face( expF.Current() );
1054     SMESH_subMesh *sm = aMesh.GetSubMesh(F);
1055     MapShapeNbElemsItr anIt = aResMap.find(sm);
1056     if( anIt==aResMap.end() ) {
1057       SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
1058       smError.reset( new SMESH_ComputeError(COMPERR_ALGO_FAILED,"Submesh can not be evaluated",this));
1059       return false;
1060     }
1061     std::vector<smIdType> aVec = (*anIt).second;
1062     nbtri += std::max(aVec[SMDSEntity_Triangle],aVec[SMDSEntity_Quad_Triangle]);
1063     nbqua += std::max(aVec[SMDSEntity_Quadrangle],aVec[SMDSEntity_Quad_Quadrangle]);
1064     GProp_GProps G;
1065     BRepGProp::SurfaceProperties(F,G);
1066     double anArea = G.Mass();
1067     fullArea += anArea;
1068   }
1069
1070   // collect info from edges
1071   smIdType nb0d_e = 0, nb1d_e = 0;
1072   bool IsQuadratic = false;
1073   bool IsFirst = true;
1074   TopTools_MapOfShape tmpMap;
1075   for (TopExp_Explorer expF(aShape, TopAbs_EDGE); expF.More(); expF.Next()) {
1076     TopoDS_Edge E = TopoDS::Edge(expF.Current());
1077     if( tmpMap.Contains(E) )
1078       continue;
1079     tmpMap.Add(E);
1080     SMESH_subMesh *aSubMesh = aMesh.GetSubMesh(expF.Current());
1081     MapShapeNbElemsItr anIt = aResMap.find(aSubMesh);
1082     if( anIt==aResMap.end() ) {
1083       SMESH_ComputeErrorPtr& smError = aSubMesh->GetComputeError();
1084       smError.reset( new SMESH_ComputeError(COMPERR_ALGO_FAILED,
1085                                             "Submesh can not be evaluated",this));
1086       return false;
1087     }
1088     std::vector<smIdType> aVec = (*anIt).second;
1089     nb0d_e += aVec[SMDSEntity_Node];
1090     nb1d_e += std::max(aVec[SMDSEntity_Edge],aVec[SMDSEntity_Quad_Edge]);
1091     if(IsFirst) {
1092       IsQuadratic = (aVec[SMDSEntity_Quad_Edge] > aVec[SMDSEntity_Edge]);
1093       IsFirst = false;
1094     }
1095   }
1096   tmpMap.Clear();
1097
1098   double ELen_face = sqrt(2.* ( fullArea/double(nbtri+nbqua*2) ) / sqrt(3.0) );
1099   double ELen_vol = pow( 72, 1/6. ) * pow( _maxElementVolume, 1/3. );
1100   double ELen = Min(ELen_vol,ELen_face*2);
1101
1102   GProp_GProps G;
1103   BRepGProp::VolumeProperties(aShape,G);
1104   double aVolume = G.Mass();
1105   double tetrVol = 0.1179*ELen*ELen*ELen;
1106   double CoeffQuality = 0.9;
1107   smIdType nbVols = (smIdType)( aVolume/tetrVol/CoeffQuality );
1108   smIdType nb1d_f = (nbtri*3 + nbqua*4 - nb1d_e) / 2;
1109   smIdType nb1d_in = (nbVols*6 - nb1d_e - nb1d_f ) / 5;
1110   std::vector<smIdType> aVec(SMDSEntity_Last);
1111   for(smIdType i=SMDSEntity_Node; i<SMDSEntity_Last; i++) aVec[i]=0;
1112   if( IsQuadratic ) {
1113     aVec[SMDSEntity_Node] = nb1d_in/6 + 1 + nb1d_in;
1114     aVec[SMDSEntity_Quad_Tetra] = nbVols - nbqua*2;
1115     aVec[SMDSEntity_Quad_Pyramid] = nbqua;
1116   }
1117   else {
1118     aVec[SMDSEntity_Node] = nb1d_in/6 + 1;
1119     aVec[SMDSEntity_Tetra] = nbVols - nbqua*2;
1120     aVec[SMDSEntity_Pyramid] = nbqua;
1121   }
1122   SMESH_subMesh *sm = aMesh.GetSubMesh(aShape);
1123   aResMap.insert(std::make_pair(sm,aVec));
1124
1125   return true;
1126 }
1127
1128