Salome HOME
Merge from V5_1_3_BR branch (07/12/09)
[plugins/netgenplugin.git] / src / NETGENPlugin / NETGENPlugin_NETGEN_2D_ONLY.cxx
1 //  Copyright (C) 2007-2008  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.
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 // File      : NETGENPlugin_NETGEN_2D_ONLY.cxx
23 // Author    : Edward AGAPOV (OCC)
24 // Project   : SALOME
25 //
26 #include "NETGENPlugin_NETGEN_2D_ONLY.hxx"
27
28 #include "NETGENPlugin_Mesher.hxx"
29
30 #include "SMDS_MeshElement.hxx"
31 #include "SMDS_MeshNode.hxx"
32 #include "SMESHDS_Mesh.hxx"
33 #include "SMESH_Comment.hxx"
34 #include "SMESH_Gen.hxx"
35 #include "SMESH_Mesh.hxx"
36 #include "SMESH_MesherHelper.hxx"
37 #include "StdMeshers_FaceSide.hxx"
38 #include "StdMeshers_MaxElementArea.hxx"
39 #include "StdMeshers_LengthFromEdges.hxx"
40 #include "StdMeshers_QuadranglePreference.hxx"
41
42 #include <Precision.hxx>
43 #include <Standard_ErrorHandler.hxx>
44 #include <Standard_Failure.hxx>
45
46 #include "utilities.h"
47
48 #include <list>
49 #include <vector>
50 #include <limits>
51
52 /*
53   Netgen include files
54 */
55 namespace nglib {
56 #include <nglib.h>
57 }
58 #define OCCGEOMETRY
59 #include <occgeom.hpp>
60 #include <meshing.hpp>
61 //#include <meshtype.hpp>
62 namespace netgen {
63   extern int OCCGenerateMesh (OCCGeometry&, Mesh*&, int, int, char*);
64   /*extern*/ MeshingParameters mparam;
65 }
66
67 using namespace std;
68 using namespace netgen;
69 using namespace nglib;
70
71 //=============================================================================
72 /*!
73  *  
74  */
75 //=============================================================================
76
77 NETGENPlugin_NETGEN_2D_ONLY::NETGENPlugin_NETGEN_2D_ONLY(int hypId, int studyId,
78                                                          SMESH_Gen* gen)
79   : SMESH_2D_Algo(hypId, studyId, gen)
80 {
81   MESSAGE("NETGENPlugin_NETGEN_2D_ONLY::NETGENPlugin_NETGEN_2D_ONLY");
82   _name = "NETGEN_2D_ONLY";
83   
84   _shapeType = (1 << TopAbs_FACE);// 1 bit /shape type
85
86   _compatibleHypothesis.push_back("MaxElementArea");
87   _compatibleHypothesis.push_back("LengthFromEdges");
88   _compatibleHypothesis.push_back("QuadranglePreference");
89
90   _hypMaxElementArea = 0;
91   _hypLengthFromEdges = 0;
92   _hypQuadranglePreference = 0;
93 }
94
95 //=============================================================================
96 /*!
97  *  
98  */
99 //=============================================================================
100
101 NETGENPlugin_NETGEN_2D_ONLY::~NETGENPlugin_NETGEN_2D_ONLY()
102 {
103   MESSAGE("NETGENPlugin_NETGEN_2D_ONLY::~NETGENPlugin_NETGEN_2D_ONLY");
104 }
105
106 //=============================================================================
107 /*!
108  *  
109  */
110 //=============================================================================
111
112 bool NETGENPlugin_NETGEN_2D_ONLY::CheckHypothesis (SMESH_Mesh&         aMesh,
113                                                    const TopoDS_Shape& aShape,
114                                                    Hypothesis_Status&  aStatus)
115 {
116   _hypMaxElementArea = 0;
117   _hypLengthFromEdges = 0;
118   _hypQuadranglePreference = 0;
119
120   const list<const SMESHDS_Hypothesis*>& hyps = GetUsedHypothesis(aMesh, aShape, false);
121
122   if (hyps.empty())
123   {
124     aStatus = HYP_OK; //SMESH_Hypothesis::HYP_MISSING;
125     return true;  // (PAL13464) can work with no hypothesis, LengthFromEdges is default one
126   }
127
128   aStatus = HYP_MISSING;
129
130   list<const SMESHDS_Hypothesis*>::const_iterator ith;
131   for (ith = hyps.begin(); ith != hyps.end(); ++ith )
132   {
133     const SMESHDS_Hypothesis* hyp = (*ith);
134
135     string hypName = hyp->GetName();
136
137     if      ( hypName == "MaxElementArea")
138       _hypMaxElementArea = static_cast<const StdMeshers_MaxElementArea*> (hyp);
139     else if ( hypName == "LengthFromEdges" )
140       _hypLengthFromEdges = static_cast<const StdMeshers_LengthFromEdges*> (hyp);
141     else if ( hypName == "QuadranglePreference" )
142       _hypQuadranglePreference = static_cast<const StdMeshers_QuadranglePreference*>(hyp);
143     else {
144       aStatus = HYP_INCOMPATIBLE;
145       return false;
146     }
147   }
148
149   if ( _hypMaxElementArea && _hypLengthFromEdges ) {
150     aStatus = HYP_CONCURENT;
151     return false;
152   }
153
154   if ( _hypMaxElementArea || _hypLengthFromEdges )
155     aStatus = HYP_OK;
156
157   return aStatus == HYP_OK;
158 }
159
160 //================================================================================
161 /*!
162  * \brief Fill netgen mesh with segments
163   * \retval SMESH_ComputeErrorPtr - error description
164  */
165 //================================================================================
166
167 static TError AddSegmentsToMesh(netgen::Mesh&                    ngMesh,
168                                 OCCGeometry&                     geom,
169                                 const TSideVector&               wires,
170                                 SMESH_MesherHelper&              helper,
171                                 vector< const SMDS_MeshNode* > & nodeVec)
172 {
173   // ----------------------------
174   // Check wires and count nodes
175   // ----------------------------
176   int nbNodes = 0;
177   for ( int iW = 0; iW < wires.size(); ++iW )
178   {
179     StdMeshers_FaceSidePtr wire = wires[ iW ];
180     if ( wire->MissVertexNode() )
181       return TError
182         (new SMESH_ComputeError(COMPERR_BAD_INPUT_MESH, "Missing nodes on vertices"));
183       
184     const vector<UVPtStruct>& uvPtVec = wire->GetUVPtStruct();
185     if ( uvPtVec.size() != wire->NbPoints() )
186       return TError
187         (new SMESH_ComputeError(COMPERR_BAD_INPUT_MESH,
188                                 SMESH_Comment("Unexpected nb of points on wire ") << iW
189                                 << ": " << uvPtVec.size()<<" != "<<wire->NbPoints()));
190     nbNodes += wire->NbSegments();
191   }
192   nodeVec.reserve( nbNodes );
193
194   // -----------------
195   // Fill netgen mesh
196   // -----------------
197
198 //   netgen::Box<3> bb = geom.GetBoundingBox();
199 //   bb.Increase (bb.Diam()/10);
200 //   ngMesh.SetLocalH (bb.PMin(), bb.PMax(), 0.5); // set grading
201
202   const int faceID = 1, solidID = 0;
203   ngMesh.AddFaceDescriptor (FaceDescriptor(faceID, solidID, solidID, 0));
204
205   for ( int iW = 0; iW < wires.size(); ++iW )
206   {
207     StdMeshers_FaceSidePtr wire = wires[ iW ];
208     const vector<UVPtStruct>& uvPtVec = wire->GetUVPtStruct();
209
210     int firstPointID = ngMesh.GetNP() + 1;
211     int edgeID = 1, posID = -2;
212     for ( int i = 0; i < wire->NbSegments(); ++i ) // loop on segments
213     {
214       // Add the first point of a segment
215       const SMDS_MeshNode * n = uvPtVec[ i ].node;
216       const int posShapeID = n->GetPosition()->GetShapeId();
217
218       // skip nodes on degenerated edges
219       if ( helper.IsDegenShape( posShapeID ) &&
220            helper.IsDegenShape( uvPtVec[ i+1 ].node->GetPosition()->GetShapeId() ))
221         continue;
222
223       nodeVec.push_back( n );
224
225       MeshPoint mp( Point<3> (n->X(), n->Y(), n->Z()) );
226       ngMesh.AddPoint ( mp, 1, EDGEPOINT );
227
228       // Add the segment
229       Segment seg;
230
231 #ifdef NETGEN_NEW
232       seg.pnums[0] = ngMesh.GetNP();          // ng node id
233       seg.pnums[1] = seg.pnums[0] + 1;              // ng node id
234 #else
235       seg.p1 = ngMesh.GetNP();          // ng node id
236       seg.p2 = seg.p1 + 1;              // ng node id
237 #endif
238       seg.edgenr = ngMesh.GetNSeg() + 1;// segment id
239       seg.si = faceID;                  // = geom.fmap.FindIndex (face);
240
241       for ( int iEnd = 0; iEnd < 2; ++iEnd)
242       {
243         const UVPtStruct& pnt = uvPtVec[ i + iEnd ];
244
245         seg.epgeominfo[ iEnd ].dist = pnt.param; // param on curve
246         seg.epgeominfo[ iEnd ].u    = pnt.u;
247         seg.epgeominfo[ iEnd ].v    = pnt.v;
248
249         // find out edge id and node parameter on edge
250         bool onVertex = ( pnt.node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX );
251         if ( onVertex || posShapeID != posID )
252         {
253           // get edge id
254           double normParam = pnt.normParam;
255           if ( onVertex )
256             normParam = 0.5 * ( uvPtVec[ i ].normParam + uvPtVec[ i+1 ].normParam );
257           const TopoDS_Edge& edge = wire->Edge( wire->EdgeIndex( normParam ));
258           edgeID = geom.emap.FindIndex( edge );
259           posID  = posShapeID;
260           if ( onVertex ) // param on curve is different on each of two edges
261             seg.epgeominfo[ iEnd ].dist = helper.GetNodeU( edge, pnt.node );
262         }
263         seg.epgeominfo[ iEnd ].edgenr = edgeID; //  = geom.emap.FindIndex(edge);
264       }
265
266       ngMesh.AddSegment (seg);
267
268 //       cout << "Segment: " << seg.edgenr << endl
269 //            << "\tp1: " << seg.p1 << endl
270 //            << "\tp2: " << seg.p2 << endl
271 //            << "\tp0 param: " << seg.epgeominfo[ 0 ].dist << endl
272 //            << "\tp0 uv: " << seg.epgeominfo[ 0 ].u <<", "<< seg.epgeominfo[ 0 ].v << endl
273 //            << "\tp0 edge: " << seg.epgeominfo[ 0 ].edgenr << endl
274 //            << "\tp1 param: " << seg.epgeominfo[ 1 ].dist << endl
275 //            << "\tp1 uv: " << seg.epgeominfo[ 1 ].u <<", "<< seg.epgeominfo[ 1 ].v << endl
276 //            << "\tp1 edge: " << seg.epgeominfo[ 1 ].edgenr << endl;
277     }
278     Segment& seg = ngMesh.LineSegment( ngMesh.GetNSeg() );
279 #ifdef NETGEN_NEW
280     seg.pnums[1] = firstPointID;
281 #else
282     seg.p2 = firstPointID;
283 #endif
284   }
285
286   ngMesh.CalcSurfacesOfNode();  
287
288   return TError();
289 }
290
291 //=============================================================================
292 /*!
293  *Here we are going to use the NETGEN mesher
294  */
295 //=============================================================================
296
297 bool NETGENPlugin_NETGEN_2D_ONLY::Compute(SMESH_Mesh&         aMesh,
298                                           const TopoDS_Shape& aShape)
299 {
300   MESSAGE("NETGENPlugin_NETGEN_2D_ONLY::Compute()");
301
302   SMESHDS_Mesh* meshDS = aMesh.GetMeshDS();
303   int faceID = meshDS->ShapeToIndex( aShape );
304
305   SMESH_MesherHelper helper(aMesh);
306   _quadraticMesh = helper.IsQuadraticSubMesh(aShape);
307   helper.SetElementsOnShape( true );
308   const bool ignoreMediumNodes = _quadraticMesh;
309   
310   // ------------------------
311   // get all edges of a face
312   // ------------------------
313   const TopoDS_Face F = TopoDS::Face( aShape.Oriented( TopAbs_FORWARD ));
314   TError problem;
315   TSideVector wires = StdMeshers_FaceSide::GetFaceWires( F, aMesh, ignoreMediumNodes, problem );
316   if ( problem && !problem->IsOK() )
317     return error( problem );
318   int nbWires = wires.size();
319   if ( nbWires == 0 )
320     return error( "Problem in StdMeshers_FaceSide::GetFaceWires()");
321   if ( wires[0]->NbSegments() < 3 ) // ex: a circle with 2 segments
322     return error(COMPERR_BAD_INPUT_MESH,
323                  SMESH_Comment("Too few segments: ")<<wires[0]->NbSegments());
324
325   // -------------------------
326   // Make input netgen mesh
327   // -------------------------
328
329   Ng_Init();
330   netgen::Mesh * ngMesh = new netgen::Mesh ();
331
332   netgen::OCCGeometry occgeo;
333   NETGENPlugin_Mesher::PrepareOCCgeometry( occgeo, F, aMesh );
334   occgeo.fmap.Clear(); // face can be reversed, which is wrong in this case (issue 19978)
335   occgeo.fmap.Add( F );
336
337   vector< const SMDS_MeshNode* > nodeVec;
338   problem = AddSegmentsToMesh( *ngMesh, occgeo, wires, helper, nodeVec );
339   if ( problem && !problem->IsOK() ) {
340     delete ngMesh; Ng_Exit();
341     return error( problem );
342   }
343
344   // --------------------
345   // compute edge length
346   // --------------------
347
348   double edgeLength = 0;
349   if (_hypLengthFromEdges || !_hypLengthFromEdges && !_hypMaxElementArea)
350   {
351     int nbSegments = 0;
352     for ( int iW = 0; iW < nbWires; ++iW )
353     {
354       edgeLength += wires[ iW ]->Length();
355       nbSegments += wires[ iW ]->NbSegments();
356     }
357     if ( nbSegments )
358       edgeLength /= nbSegments;
359   }
360   if ( _hypMaxElementArea )
361   {
362     double maxArea = _hypMaxElementArea->GetMaxArea();
363     edgeLength = sqrt(2. * maxArea/sqrt(3.0));
364   }
365   if ( edgeLength < DBL_MIN )
366     edgeLength = occgeo.GetBoundingBox().Diam();
367
368   //cout << " edgeLength = " << edgeLength << endl;
369
370   netgen::mparam.maxh = edgeLength;
371   netgen::mparam.quad = _hypQuadranglePreference ? 1 : 0;
372   //ngMesh->SetGlobalH ( edgeLength );
373
374   // -------------------------
375   // Generate surface mesh
376   // -------------------------
377
378   char *optstr = 0;
379   int startWith = MESHCONST_MESHSURFACE;
380   int endWith   = MESHCONST_OPTSURFACE;
381   int err = 1;
382
383   try {
384 #if (OCC_VERSION_MAJOR << 16 | OCC_VERSION_MINOR << 8 | OCC_VERSION_MAINTENANCE) > 0x060100
385     OCC_CATCH_SIGNALS;
386 #endif
387     err = netgen::OCCGenerateMesh(occgeo, ngMesh, startWith, endWith, optstr);
388   }
389   catch (Standard_Failure& ex) {
390     string comment = ex.DynamicType()->Name();
391     if ( ex.GetMessageString() && strlen( ex.GetMessageString() )) {
392       comment += ": ";
393       comment += ex.GetMessageString();
394     }
395     error(COMPERR_OCC_EXCEPTION, comment);
396   }
397   catch (NgException exc) {
398     error( SMESH_Comment("NgException: ") << exc.What() );
399   }
400   catch (...) {
401     error(COMPERR_EXCEPTION,"Exception in netgen::OCCGenerateMesh()");
402   }
403
404   // ----------------------------------------------------
405   // Fill the SMESHDS with the generated nodes and faces
406   // ----------------------------------------------------
407
408   int nbNodes = ngMesh->GetNP();
409   int nbFaces = ngMesh->GetNSE();
410
411   int nbInputNodes = nodeVec.size();
412   nodeVec.resize( nbNodes, 0 );
413
414   // add nodes
415   for ( int i = nbInputNodes + 1; i <= nbNodes; ++i )
416   {
417     const MeshPoint& ngPoint = ngMesh->Point(i);
418 #ifdef NETGEN_NEW
419     SMDS_MeshNode * node = meshDS->AddNode(ngPoint(0), ngPoint(1), ngPoint(2));
420 #else
421     SMDS_MeshNode * node = meshDS->AddNode(ngPoint.X(), ngPoint.Y(), ngPoint.Z());
422 #endif
423     nodeVec[ i-1 ] = node;
424   }
425
426   // create faces
427   bool reverse = ( aShape.Orientation() == TopAbs_REVERSED );
428   for ( int i = 1; i <= nbFaces ; ++i )
429   {
430     const Element2d& elem = ngMesh->SurfaceElement(i);
431     vector<const SMDS_MeshNode*> nodes( elem.GetNP() );
432     for (int j=1; j <= elem.GetNP(); ++j)
433     {
434       int pind = elem.PNum(j);
435       const SMDS_MeshNode* node = nodeVec.at(pind-1);
436       if ( reverse )
437         nodes[ nodes.size()-j ] = node;
438       else
439         nodes[ j-1 ] = node;
440       if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_3DSPACE )
441       {
442         const PointGeomInfo& pgi = elem.GeomInfoPi(j);
443         meshDS->SetNodeOnFace((SMDS_MeshNode*)node, faceID, pgi.u, pgi.v);
444       }
445     }
446     SMDS_MeshFace* face = 0;
447     if ( elem.GetType() == TRIG )
448       face = helper.AddFace(nodes[0],nodes[1],nodes[2]);
449     else
450       face = helper.AddFace(nodes[0],nodes[1],nodes[2],nodes[3]);
451   }
452
453   Ng_DeleteMesh((nglib::Ng_Mesh*)ngMesh);
454   Ng_Exit();
455
456   NETGENPlugin_Mesher::RemoveTmpFiles();
457
458   return !err;
459 }
460
461
462 //=============================================================================
463 /*!
464  *
465  */
466 //=============================================================================
467
468 bool NETGENPlugin_NETGEN_2D_ONLY::Evaluate(SMESH_Mesh& aMesh,
469                                            const TopoDS_Shape& aShape,
470                                            MapShapeNbElems& aResMap)
471 {
472   TopoDS_Face F = TopoDS::Face(aShape);
473   if(F.IsNull())
474     return false;
475
476   // collect info from edges
477   int nb0d = 0, nb1d = 0;
478   bool IsQuadratic = false;
479   bool IsFirst = true;
480   double fullLen = 0.0;
481   TopTools_MapOfShape tmpMap;
482   for (TopExp_Explorer exp(F, TopAbs_EDGE); exp.More(); exp.Next()) {
483     TopoDS_Edge E = TopoDS::Edge(exp.Current());
484     if( tmpMap.Contains(E) )
485       continue;
486     tmpMap.Add(E);
487     SMESH_subMesh *aSubMesh = aMesh.GetSubMesh(exp.Current());
488     MapShapeNbElemsItr anIt = aResMap.find(aSubMesh);
489     if( anIt==aResMap.end() ) {
490       SMESH_subMesh *sm = aMesh.GetSubMesh(F);
491       SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
492       smError.reset( new SMESH_ComputeError(COMPERR_ALGO_FAILED,"Submesh can not be evaluated",this));
493       return false;
494     }
495     std::vector<int> aVec = (*anIt).second;
496     nb0d += aVec[SMDSEntity_Node];
497     nb1d += Max(aVec[SMDSEntity_Edge],aVec[SMDSEntity_Quad_Edge]);
498     double aLen = SMESH_Algo::EdgeLength(E);
499     fullLen += aLen;
500     if(IsFirst) {
501       IsQuadratic = (aVec[SMDSEntity_Quad_Edge] > aVec[SMDSEntity_Edge]);
502       IsFirst = false;
503     }
504   }
505   tmpMap.Clear();
506
507   // compute edge length
508   double ELen = 0;
509   if (_hypLengthFromEdges || !_hypLengthFromEdges && !_hypMaxElementArea) {
510     if ( nb1d > 0 )
511       ELen = fullLen / nb1d;
512   }
513   if ( _hypMaxElementArea ) {
514     double maxArea = _hypMaxElementArea->GetMaxArea();
515     ELen = sqrt(2. * maxArea/sqrt(3.0));
516   }
517   GProp_GProps G;
518   BRepGProp::SurfaceProperties(F,G);
519   double anArea = G.Mass();
520
521   const int hugeNb = numeric_limits<int>::max()/10;
522   if ( anArea / hugeNb > ELen*ELen )
523   {
524     SMESH_subMesh *sm = aMesh.GetSubMesh(F);
525     SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
526     smError.reset( new SMESH_ComputeError(COMPERR_ALGO_FAILED,"Submesh can not be evaluated.\nToo small element length",this));
527     return false;
528   }
529   int nbFaces = (int) ( anArea / ( ELen*ELen*sqrt(3.) / 4 ) );
530   int nbNodes = (int) ( ( nbFaces*3 - (nb1d-1)*2 ) / 6 + 1 );
531   std::vector<int> aVec(SMDSEntity_Last);
532   for(int i=SMDSEntity_Node; i<SMDSEntity_Last; i++) aVec[i]=0;
533   if( IsQuadratic ) {
534     aVec[SMDSEntity_Node] = nbNodes;
535     aVec[SMDSEntity_Quad_Triangle] = nbFaces;
536   }
537   else {
538     aVec[SMDSEntity_Node] = nbNodes;
539     aVec[SMDSEntity_Triangle] = nbFaces;
540   }
541   SMESH_subMesh *sm = aMesh.GetSubMesh(F);
542   aResMap.insert(std::make_pair(sm,aVec));
543
544   return true;
545 }