]> SALOME platform Git repositories - plugins/netgenplugin.git/blob - src/NETGENPlugin/NETGENPlugin_NETGEN_2D_ONLY.cxx
Salome HOME
020700: EDF 1234 SMESH: Quadrangle preference and Netgen
[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 || _hypQuadranglePreference)
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     bool isInternalEdge = false;
213     for ( int i = 0; i < wire->NbSegments(); ++i ) // loop on segments
214     {
215       // Add the first point of a segment
216       const SMDS_MeshNode * n = uvPtVec[ i ].node;
217       const int posShapeID = n->GetPosition()->GetShapeId();
218
219       // skip nodes on degenerated edges
220       if ( helper.IsDegenShape( posShapeID ) &&
221            helper.IsDegenShape( uvPtVec[ i+1 ].node->GetPosition()->GetShapeId() ))
222         continue;
223
224       nodeVec.push_back( n );
225
226       MeshPoint mp( Point<3> (n->X(), n->Y(), n->Z()) );
227       ngMesh.AddPoint ( mp, 1, EDGEPOINT );
228
229       // Add the segment
230       Segment seg;
231
232 #ifdef NETGEN_NEW
233       seg.pnums[0] = ngMesh.GetNP();          // ng node id
234       seg.pnums[1] = seg.pnums[0] + 1;              // ng node id
235 #else
236       seg.p1 = ngMesh.GetNP();          // ng node id
237       seg.p2 = seg.p1 + 1;              // ng node id
238 #endif
239       seg.edgenr = ngMesh.GetNSeg() + 1;// segment id
240       seg.si = faceID;                  // = geom.fmap.FindIndex (face);
241
242       for ( int iEnd = 0; iEnd < 2; ++iEnd)
243       {
244         const UVPtStruct& pnt = uvPtVec[ i + iEnd ];
245
246         seg.epgeominfo[ iEnd ].dist = pnt.param; // param on curve
247         seg.epgeominfo[ iEnd ].u    = pnt.u;
248         seg.epgeominfo[ iEnd ].v    = pnt.v;
249
250         // find out edge id and node parameter on edge
251         bool onVertex = ( pnt.node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX );
252         if ( onVertex || posShapeID != posID )
253         {
254           // get edge id
255           double normParam = pnt.normParam;
256           if ( onVertex )
257             normParam = 0.5 * ( uvPtVec[ i ].normParam + uvPtVec[ i+1 ].normParam );
258           const TopoDS_Edge& edge = wire->Edge( wire->EdgeIndex( normParam ));
259           edgeID = geom.emap.FindIndex( edge );
260           posID  = posShapeID;
261           isInternalEdge = ( edge.Orientation() == TopAbs_INTERNAL );
262           if ( onVertex ) // param on curve is different on each of two edges
263             seg.epgeominfo[ iEnd ].dist = helper.GetNodeU( edge, pnt.node );
264         }
265         seg.epgeominfo[ iEnd ].edgenr = edgeID; //  = geom.emap.FindIndex(edge);
266       }
267
268       ngMesh.AddSegment (seg);
269
270 //       cout << "Segment: " << seg.edgenr << endl
271 //            << "\tp1: " << seg.p1 << endl
272 //            << "\tp2: " << seg.p2 << endl
273 //            << "\tp0 param: " << seg.epgeominfo[ 0 ].dist << endl
274 //            << "\tp0 uv: " << seg.epgeominfo[ 0 ].u <<", "<< seg.epgeominfo[ 0 ].v << endl
275 //            << "\tp0 edge: " << seg.epgeominfo[ 0 ].edgenr << endl
276 //            << "\tp1 param: " << seg.epgeominfo[ 1 ].dist << endl
277 //            << "\tp1 uv: " << seg.epgeominfo[ 1 ].u <<", "<< seg.epgeominfo[ 1 ].v << endl
278 //            << "\tp1 edge: " << seg.epgeominfo[ 1 ].edgenr << endl;
279
280       if ( isInternalEdge )
281       {
282 #ifdef NETGEN_NEW
283         swap (seg.pnums[0], seg.pnums[1]);
284 #else
285         swap (seg.p1, seg.p2);
286 #endif
287         swap( seg.epgeominfo[0], seg.epgeominfo[1] );
288         seg.edgenr = ngMesh.GetNSeg() + 1; // segment id
289         ngMesh.AddSegment (seg);
290       }
291     } // loop on segments on a wire
292
293     // close chain of segments
294     bool isClosedWire = ( wire->FirstVertex().IsSame( wire->LastVertex() ));
295     if ( isClosedWire )
296     {
297       Segment& seg = ngMesh.LineSegment( ngMesh.GetNSeg() );
298 #ifdef NETGEN_NEW
299       seg.pnums[1] = firstPointID;
300 #else
301       seg.p2 = firstPointID;
302 #endif
303     }
304     else // INTERNAL wire (Issue 0020676)
305     {
306       const SMDS_MeshNode * n = uvPtVec.back().node;
307       nodeVec.push_back( n );
308
309       MeshPoint mp( Point<3> (n->X(), n->Y(), n->Z()) );
310       ngMesh.AddPoint ( mp, 1, EDGEPOINT );
311     }
312
313   } // loop on wires of a face
314
315   ngMesh.CalcSurfacesOfNode();  
316
317   return TError();
318 }
319
320 //=============================================================================
321 /*!
322  *Here we are going to use the NETGEN mesher
323  */
324 //=============================================================================
325
326 bool NETGENPlugin_NETGEN_2D_ONLY::Compute(SMESH_Mesh&         aMesh,
327                                           const TopoDS_Shape& aShape)
328 {
329   MESSAGE("NETGENPlugin_NETGEN_2D_ONLY::Compute()");
330
331   SMESHDS_Mesh* meshDS = aMesh.GetMeshDS();
332   int faceID = meshDS->ShapeToIndex( aShape );
333
334   SMESH_MesherHelper helper(aMesh);
335   _quadraticMesh = helper.IsQuadraticSubMesh(aShape);
336   helper.SetElementsOnShape( true );
337   const bool ignoreMediumNodes = _quadraticMesh;
338   
339   // ------------------------
340   // get all edges of a face
341   // ------------------------
342   const TopoDS_Face F = TopoDS::Face( aShape.Oriented( TopAbs_FORWARD ));
343   TError problem;
344   TSideVector wires = StdMeshers_FaceSide::GetFaceWires( F, aMesh, ignoreMediumNodes, problem );
345   if ( problem && !problem->IsOK() )
346     return error( problem );
347   int nbWires = wires.size();
348   if ( nbWires == 0 )
349     return error( "Problem in StdMeshers_FaceSide::GetFaceWires()");
350   if ( wires[0]->NbSegments() < 3 ) // ex: a circle with 2 segments
351     return error(COMPERR_BAD_INPUT_MESH,
352                  SMESH_Comment("Too few segments: ")<<wires[0]->NbSegments());
353
354   // -------------------------
355   // Make input netgen mesh
356   // -------------------------
357
358   Ng_Init();
359   netgen::Mesh * ngMesh = new netgen::Mesh ();
360
361   netgen::OCCGeometry occgeo;
362   NETGENPlugin_Mesher::PrepareOCCgeometry( occgeo, F, aMesh );
363   occgeo.fmap.Clear(); // face can be reversed, which is wrong in this case (issue 19978)
364   occgeo.fmap.Add( F );
365
366   vector< const SMDS_MeshNode* > nodeVec;
367   problem = AddSegmentsToMesh( *ngMesh, occgeo, wires, helper, nodeVec );
368   if ( problem && !problem->IsOK() ) {
369     delete ngMesh; Ng_Exit();
370     return error( problem );
371   }
372
373   // --------------------
374   // compute edge length
375   // --------------------
376
377   double edgeLength = 0;
378   if (_hypLengthFromEdges || !_hypLengthFromEdges && !_hypMaxElementArea)
379   {
380     int nbSegments = 0;
381     for ( int iW = 0; iW < nbWires; ++iW )
382     {
383       edgeLength += wires[ iW ]->Length();
384       nbSegments += wires[ iW ]->NbSegments();
385     }
386     if ( nbSegments )
387       edgeLength /= nbSegments;
388   }
389   if ( _hypMaxElementArea )
390   {
391     double maxArea = _hypMaxElementArea->GetMaxArea();
392     edgeLength = sqrt(2. * maxArea/sqrt(3.0));
393   }
394   if ( edgeLength < DBL_MIN )
395     edgeLength = occgeo.GetBoundingBox().Diam();
396
397   //cout << " edgeLength = " << edgeLength << endl;
398
399   netgen::mparam.maxh = edgeLength;
400   netgen::mparam.quad = _hypQuadranglePreference ? 1 : 0;
401   //ngMesh->SetGlobalH ( edgeLength );
402
403   // -------------------------
404   // Generate surface mesh
405   // -------------------------
406
407   char *optstr = 0;
408   int startWith = MESHCONST_MESHSURFACE;
409   int endWith   = MESHCONST_OPTSURFACE;
410   int err = 1;
411
412   try {
413 #if (OCC_VERSION_MAJOR << 16 | OCC_VERSION_MINOR << 8 | OCC_VERSION_MAINTENANCE) > 0x060100
414     OCC_CATCH_SIGNALS;
415 #endif
416     err = netgen::OCCGenerateMesh(occgeo, ngMesh, startWith, endWith, optstr);
417   }
418   catch (Standard_Failure& ex) {
419     string comment = ex.DynamicType()->Name();
420     if ( ex.GetMessageString() && strlen( ex.GetMessageString() )) {
421       comment += ": ";
422       comment += ex.GetMessageString();
423     }
424     error(COMPERR_OCC_EXCEPTION, comment);
425   }
426   catch (NgException exc) {
427     error( SMESH_Comment("NgException: ") << exc.What() );
428   }
429   catch (...) {
430     error(COMPERR_EXCEPTION,"Exception in netgen::OCCGenerateMesh()");
431   }
432
433   // ----------------------------------------------------
434   // Fill the SMESHDS with the generated nodes and faces
435   // ----------------------------------------------------
436
437   int nbNodes = ngMesh->GetNP();
438   int nbFaces = ngMesh->GetNSE();
439
440   int nbInputNodes = nodeVec.size();
441   nodeVec.resize( nbNodes, 0 );
442
443   // add nodes
444   for ( int i = nbInputNodes + 1; i <= nbNodes; ++i )
445   {
446     const MeshPoint& ngPoint = ngMesh->Point(i);
447 #ifdef NETGEN_NEW
448     SMDS_MeshNode * node = meshDS->AddNode(ngPoint(0), ngPoint(1), ngPoint(2));
449 #else
450     SMDS_MeshNode * node = meshDS->AddNode(ngPoint.X(), ngPoint.Y(), ngPoint.Z());
451 #endif
452     nodeVec[ i-1 ] = node;
453   }
454
455   // create faces
456   bool reverse = ( aShape.Orientation() == TopAbs_REVERSED );
457   for ( int i = 1; i <= nbFaces ; ++i )
458   {
459     const Element2d& elem = ngMesh->SurfaceElement(i);
460     vector<const SMDS_MeshNode*> nodes( elem.GetNP() );
461     for (int j=1; j <= elem.GetNP(); ++j)
462     {
463       int pind = elem.PNum(j);
464       const SMDS_MeshNode* node = nodeVec.at(pind-1);
465       if ( reverse )
466         nodes[ nodes.size()-j ] = node;
467       else
468         nodes[ j-1 ] = node;
469       if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_3DSPACE )
470       {
471         const PointGeomInfo& pgi = elem.GeomInfoPi(j);
472         meshDS->SetNodeOnFace((SMDS_MeshNode*)node, faceID, pgi.u, pgi.v);
473       }
474     }
475     SMDS_MeshFace* face = 0;
476     if ( elem.GetType() == TRIG )
477       face = helper.AddFace(nodes[0],nodes[1],nodes[2]);
478     else
479       face = helper.AddFace(nodes[0],nodes[1],nodes[2],nodes[3]);
480   }
481
482   Ng_DeleteMesh((nglib::Ng_Mesh*)ngMesh);
483   Ng_Exit();
484
485   NETGENPlugin_Mesher::RemoveTmpFiles();
486
487   return !err;
488 }
489
490
491 //=============================================================================
492 /*!
493  *
494  */
495 //=============================================================================
496
497 bool NETGENPlugin_NETGEN_2D_ONLY::Evaluate(SMESH_Mesh& aMesh,
498                                            const TopoDS_Shape& aShape,
499                                            MapShapeNbElems& aResMap)
500 {
501   TopoDS_Face F = TopoDS::Face(aShape);
502   if(F.IsNull())
503     return false;
504
505   // collect info from edges
506   int nb0d = 0, nb1d = 0;
507   bool IsQuadratic = false;
508   bool IsFirst = true;
509   double fullLen = 0.0;
510   TopTools_MapOfShape tmpMap;
511   for (TopExp_Explorer exp(F, TopAbs_EDGE); exp.More(); exp.Next()) {
512     TopoDS_Edge E = TopoDS::Edge(exp.Current());
513     if( tmpMap.Contains(E) )
514       continue;
515     tmpMap.Add(E);
516     SMESH_subMesh *aSubMesh = aMesh.GetSubMesh(exp.Current());
517     MapShapeNbElemsItr anIt = aResMap.find(aSubMesh);
518     if( anIt==aResMap.end() ) {
519       SMESH_subMesh *sm = aMesh.GetSubMesh(F);
520       SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
521       smError.reset( new SMESH_ComputeError(COMPERR_ALGO_FAILED,"Submesh can not be evaluated",this));
522       return false;
523     }
524     std::vector<int> aVec = (*anIt).second;
525     nb0d += aVec[SMDSEntity_Node];
526     nb1d += Max(aVec[SMDSEntity_Edge],aVec[SMDSEntity_Quad_Edge]);
527     double aLen = SMESH_Algo::EdgeLength(E);
528     fullLen += aLen;
529     if(IsFirst) {
530       IsQuadratic = (aVec[SMDSEntity_Quad_Edge] > aVec[SMDSEntity_Edge]);
531       IsFirst = false;
532     }
533   }
534   tmpMap.Clear();
535
536   // compute edge length
537   double ELen = 0;
538   if (_hypLengthFromEdges || !_hypLengthFromEdges && !_hypMaxElementArea) {
539     if ( nb1d > 0 )
540       ELen = fullLen / nb1d;
541   }
542   if ( _hypMaxElementArea ) {
543     double maxArea = _hypMaxElementArea->GetMaxArea();
544     ELen = sqrt(2. * maxArea/sqrt(3.0));
545   }
546   GProp_GProps G;
547   BRepGProp::SurfaceProperties(F,G);
548   double anArea = G.Mass();
549
550   const int hugeNb = numeric_limits<int>::max()/10;
551   if ( anArea / hugeNb > ELen*ELen )
552   {
553     SMESH_subMesh *sm = aMesh.GetSubMesh(F);
554     SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
555     smError.reset( new SMESH_ComputeError(COMPERR_ALGO_FAILED,"Submesh can not be evaluated.\nToo small element length",this));
556     return false;
557   }
558   int nbFaces = (int) ( anArea / ( ELen*ELen*sqrt(3.) / 4 ) );
559   int nbNodes = (int) ( ( nbFaces*3 - (nb1d-1)*2 ) / 6 + 1 );
560   std::vector<int> aVec(SMDSEntity_Last);
561   for(int i=SMDSEntity_Node; i<SMDSEntity_Last; i++) aVec[i]=0;
562   if( IsQuadratic ) {
563     aVec[SMDSEntity_Node] = nbNodes;
564     aVec[SMDSEntity_Quad_Triangle] = nbFaces;
565   }
566   else {
567     aVec[SMDSEntity_Node] = nbNodes;
568     aVec[SMDSEntity_Triangle] = nbFaces;
569   }
570   SMESH_subMesh *sm = aMesh.GetSubMesh(F);
571   aResMap.insert(std::make_pair(sm,aVec));
572
573   return true;
574 }