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