1 // Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
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.
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.
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
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
22 // NETGENPlugin : C++ implementation
23 // File : NETGENPlugin_Mesher.cxx
24 // Author : Michael Sazonov (OCN)
27 //=============================================================================
29 #include "NETGENPlugin_Mesher.hxx"
30 #include "NETGENPlugin_Hypothesis_2D.hxx"
31 #include "NETGENPlugin_SimpleHypothesis_3D.hxx"
33 #include <SMESH_Mesh.hxx>
34 #include <SMESH_Comment.hxx>
35 #include <SMESH_ComputeError.hxx>
36 #include <SMESH_subMesh.hxx>
37 #include <SMESH_MesherHelper.hxx>
38 #include <SMESHDS_Mesh.hxx>
39 #include <SMDS_MeshElement.hxx>
40 #include <SMDS_MeshNode.hxx>
41 #include <utilities.h>
46 #include <BRep_Tool.hxx>
48 #include <TopExp_Explorer.hxx>
50 #include <NCollection_Map.hxx>
51 #include <OSD_Path.hxx>
52 #include <OSD_File.hxx>
53 #include <TCollection_AsciiString.hxx>
54 #include <TopTools_ListIteratorOfListOfShape.hxx>
55 #include <TopTools_DataMapOfShapeInteger.hxx>
56 #include <Standard_ErrorHandler.hxx>
57 #include <Standard_ProgramError.hxx>
59 // Netgen include files
64 #include <occgeom.hpp>
65 #include <meshing.hpp>
66 //#include <ngexception.hpp>
68 extern int OCCGenerateMesh (OCCGeometry&, Mesh*&, int, int, char*);
69 extern MeshingParameters mparam;
74 static void removeFile( const TCollection_AsciiString& fileName )
77 OSD_File( fileName ).Remove();
79 catch ( Standard_ProgramError ) {
80 MESSAGE("Can't remove file: " << fileName.ToCString() << " ; file does not exist or permission denied");
84 //=============================================================================
88 //=============================================================================
90 NETGENPlugin_Mesher::NETGENPlugin_Mesher (SMESH_Mesh* mesh,
91 const TopoDS_Shape& aShape,
102 //================================================================================
104 * \brief Initialize global NETGEN parameters with default values
106 //================================================================================
108 void NETGENPlugin_Mesher::defaultParameters()
111 netgen::MeshingParameters& mparams = netgen::GlobalMeshingParameters();
113 netgen::MeshingParameters& mparams = netgen::mparam;
115 // maximal mesh edge size
116 mparams.maxh = NETGENPlugin_Hypothesis::GetDefaultMaxSize();
117 // minimal number of segments per edge
118 mparams.segmentsperedge = NETGENPlugin_Hypothesis::GetDefaultNbSegPerEdge();
119 // rate of growth of size between elements
120 mparams.grading = NETGENPlugin_Hypothesis::GetDefaultGrowthRate();
121 // safety factor for curvatures (elements per radius)
122 mparams.curvaturesafety = NETGENPlugin_Hypothesis::GetDefaultNbSegPerRadius();
123 // create elements of second order
124 mparams.secondorder = NETGENPlugin_Hypothesis::GetDefaultSecondOrder() ? 1 : 0;
125 // quad-dominated surface meshing
129 mparams.quad = NETGENPlugin_Hypothesis_2D::GetDefaultQuadAllowed() ? 1 : 0;
132 //=============================================================================
134 * Pass parameters to NETGEN
136 //=============================================================================
137 void NETGENPlugin_Mesher::SetParameters(const NETGENPlugin_Hypothesis* hyp)
142 netgen::MeshingParameters& mparams = netgen::GlobalMeshingParameters();
144 netgen::MeshingParameters& mparams = netgen::mparam;
146 // Initialize global NETGEN parameters:
147 // maximal mesh segment size
148 mparams.maxh = hyp->GetMaxSize();
149 // minimal number of segments per edge
150 mparams.segmentsperedge = hyp->GetNbSegPerEdge();
151 // rate of growth of size between elements
152 mparams.grading = hyp->GetGrowthRate();
153 // safety factor for curvatures (elements per radius)
154 mparams.curvaturesafety = hyp->GetNbSegPerRadius();
155 // create elements of second order
156 mparams.secondorder = hyp->GetSecondOrder() ? 1 : 0;
157 // quad-dominated surface meshing
158 // only triangles are allowed for volumic mesh
160 mparams.quad = static_cast<const NETGENPlugin_Hypothesis_2D*>
161 (hyp)->GetQuadAllowed() ? 1 : 0;
162 _optimize = hyp->GetOptimize();
167 //=============================================================================
169 * Pass simple parameters to NETGEN
171 //=============================================================================
173 void NETGENPlugin_Mesher::SetParameters(const NETGENPlugin_SimpleHypothesis_2D* hyp)
180 //=============================================================================
182 * Link - a pair of integer numbers
184 //=============================================================================
188 Link(int _n1, int _n2) : n1(_n1), n2(_n2) {}
189 Link() : n1(0), n2(0) {}
192 int HashCode(const Link& aLink, int aLimit)
194 return HashCode(aLink.n1 + aLink.n2, aLimit);
197 Standard_Boolean IsEqual(const Link& aLink1, const Link& aLink2)
199 return (aLink1.n1 == aLink2.n1 && aLink1.n2 == aLink2.n2 ||
200 aLink1.n1 == aLink2.n2 && aLink1.n2 == aLink2.n1);
203 //================================================================================
205 * \brief Initialize netgen::OCCGeometry with OCCT shape
207 //================================================================================
209 void NETGENPlugin_Mesher::PrepareOCCgeometry(netgen::OCCGeometry& occgeo,
210 const TopoDS_Shape& shape,
212 list< SMESH_subMesh* > * meshedSM)
214 BRepTools::Clean (shape);
216 #if (OCC_VERSION_MAJOR << 16 | OCC_VERSION_MINOR << 8 | OCC_VERSION_MAINTENANCE) > 0x060100
219 BRepMesh_IncrementalMesh::BRepMesh_IncrementalMesh (shape, 0.01, true);
220 } catch (Standard_Failure) {
223 BRepBndLib::Add (shape, bb);
224 double x1,y1,z1,x2,y2,z2;
225 bb.Get (x1,y1,z1,x2,y2,z2);
226 MESSAGE("shape bounding box:\n" <<
227 "(" << x1 << " " << y1 << " " << z1 << ") " <<
228 "(" << x2 << " " << y2 << " " << z2 << ")");
229 netgen::Point<3> p1 = netgen::Point<3> (x1,y1,z1);
230 netgen::Point<3> p2 = netgen::Point<3> (x2,y2,z2);
231 occgeo.boundingbox = netgen::Box<3> (p1,p2);
233 occgeo.shape = shape;
235 //occgeo.BuildFMap();
237 // fill maps of shapes of occgeo with not yet meshed subshapes
239 // get root submeshes
240 list< SMESH_subMesh* > rootSM;
241 if ( SMESH_subMesh* sm = mesh.GetSubMeshContaining( shape )) {
242 rootSM.push_back( sm );
245 for ( TopoDS_Iterator it( shape ); it.More(); it.Next() )
246 rootSM.push_back( mesh.GetSubMesh( it.Value() ));
249 // add subshapes of empty submeshes
250 list< SMESH_subMesh* >::iterator rootIt = rootSM.begin(), rootEnd = rootSM.end();
251 for ( ; rootIt != rootEnd; ++rootIt ) {
252 SMESH_subMesh * root = *rootIt;
253 SMESH_subMeshIteratorPtr smIt = root->getDependsOnIterator(/*includeSelf=*/true,
254 /*complexShapeFirst=*/true);
255 // to find a right orientation of subshapes (PAL20462)
256 TopTools_IndexedMapOfShape subShapes;
257 TopExp::MapShapes(root->GetSubShape(), subShapes);
258 while ( smIt->more() ) {
259 SMESH_subMesh* sm = smIt->next();
260 if ( !meshedSM || sm->IsEmpty() ) {
261 TopoDS_Shape shape = sm->GetSubShape();
262 if ( shape.ShapeType() != TopAbs_VERTEX )
263 shape = subShapes( subShapes.FindIndex( shape ));// - shape->index->oriented shape
264 switch ( shape.ShapeType() ) {
265 case TopAbs_FACE : occgeo.fmap.Add( shape ); break;
266 case TopAbs_EDGE : occgeo.emap.Add( shape ); break;
267 case TopAbs_VERTEX: occgeo.vmap.Add( shape ); break;
268 case TopAbs_SOLID :occgeo.somap.Add( shape ); break;
272 // collect submeshes of meshed shapes
274 meshedSM->push_back( sm );
278 occgeo.facemeshstatus.SetSize (occgeo.fmap.Extent());
279 occgeo.facemeshstatus = 0;
283 //================================================================================
285 * \brief return id of netgen point corresponding to SMDS node
287 //================================================================================
288 typedef map< const SMDS_MeshNode*, int > TNode2IdMap;
290 static int ngNodeId( const SMDS_MeshNode* node,
291 netgen::Mesh& ngMesh,
292 TNode2IdMap& nodeNgIdMap)
294 int newNgId = ngMesh.GetNP() + 1;
296 pair< TNode2IdMap::iterator, bool > it_isNew = nodeNgIdMap.insert( make_pair( node, newNgId ));
298 if ( it_isNew.second ) {
299 netgen::MeshPoint p( netgen::Point<3> (node->X(), node->Y(), node->Z()) );
300 ngMesh.AddPoint( p );
302 return it_isNew.first->second;
305 //================================================================================
307 * \brief fill ngMesh with nodes and elements of computed submeshes
309 //================================================================================
311 bool NETGENPlugin_Mesher::fillNgMesh(netgen::OCCGeometry& occgeom,
312 netgen::Mesh& ngMesh,
313 vector<SMDS_MeshNode*>& nodeVec,
314 const list< SMESH_subMesh* > & meshedSM)
316 TNode2IdMap nodeNgIdMap;
318 TopTools_MapOfShape visitedShapes;
320 SMESH_MesherHelper helper (*_mesh);
322 int faceID = occgeom.fmap.Extent();
323 _faceDescriptors.clear();
325 list< SMESH_subMesh* >::const_iterator smIt, smEnd = meshedSM.end();
326 for ( smIt = meshedSM.begin(); smIt != smEnd; ++smIt )
328 SMESH_subMesh* sm = *smIt;
329 if ( !visitedShapes.Add( sm->GetSubShape() ))
332 SMESHDS_SubMesh * smDS = sm->GetSubMeshDS();
334 switch ( sm->GetSubShape().ShapeType() )
336 case TopAbs_EDGE: { // EDGE
337 // ----------------------
338 const TopoDS_Edge& geomEdge = TopoDS::Edge( sm->GetSubShape() );
340 // Add ng segments for each not meshed face the edge bounds
341 TopTools_MapOfShape visitedAncestors;
342 const TopTools_ListOfShape& ancestors = _mesh->GetAncestors( geomEdge );
343 TopTools_ListIteratorOfListOfShape ancestorIt ( ancestors );
344 for ( ; ancestorIt.More(); ancestorIt.Next() )
346 const TopoDS_Shape & ans = ancestorIt.Value();
347 if ( ans.ShapeType() != TopAbs_FACE || !visitedAncestors.Add( ans ))
349 const TopoDS_Face& face = TopoDS::Face( ans );
351 int faceID = occgeom.fmap.FindIndex( face );
353 continue; // meshed face
355 // find out orientation of geomEdge within face
356 bool isForwad = false;
357 for ( TopExp_Explorer exp( face, TopAbs_EDGE ); exp.More(); exp.Next() ) {
358 if ( geomEdge.IsSame( exp.Current() )) {
359 isForwad = ( exp.Current().Orientation() == geomEdge.Orientation() );
363 bool isQuad = smDS->GetElements()->next()->IsQuadratic();
365 // get all nodes from geomEdge
366 StdMeshers_FaceSide fSide( face, geomEdge, _mesh, isForwad, isQuad );
367 const vector<UVPtStruct>& points = fSide.GetUVPtStruct();
368 int i, nbSeg = fSide.NbSegments();
370 double otherSeamParam = 0;
371 helper.SetSubShape( face );
372 bool isSeam = helper.IsRealSeam( geomEdge );
375 helper.GetOtherParam( helper.GetPeriodicIndex() == 1 ? points[0].u : points[0].v );
379 int prevNgId = ngNodeId( points[0].node, ngMesh, nodeNgIdMap );
381 for ( i = 0; i < nbSeg; ++i )
383 const UVPtStruct& p1 = points[ i ];
384 const UVPtStruct& p2 = points[ i+1 ];
389 seg.p2 = prevNgId = ngNodeId( p2.node, ngMesh, nodeNgIdMap );
390 // node param on curve
391 seg.epgeominfo[ 0 ].dist = p1.param;
392 seg.epgeominfo[ 1 ].dist = p2.param;
394 seg.epgeominfo[ 0 ].u = p1.u;
395 seg.epgeominfo[ 0 ].v = p1.v;
396 seg.epgeominfo[ 1 ].u = p2.u;
397 seg.epgeominfo[ 1 ].v = p2.v;
399 //seg.epgeominfo[ iEnd ].edgenr = edgeID; // = geom.emap.FindIndex(edge);
400 seg.si = faceID; // = geom.fmap.FindIndex (face);
401 seg.edgenr = ngMesh.GetNSeg() + 1; // segment id
402 ngMesh.AddSegment (seg);
406 if ( helper.GetPeriodicIndex() == 1 ) {
407 seg.epgeominfo[ 0 ].u = otherSeamParam;
408 seg.epgeominfo[ 1 ].u = otherSeamParam;
409 swap (seg.epgeominfo[0].v, seg.epgeominfo[1].v);
411 seg.epgeominfo[ 0 ].v = otherSeamParam;
412 seg.epgeominfo[ 1 ].v = otherSeamParam;
413 swap (seg.epgeominfo[0].u, seg.epgeominfo[1].u);
415 swap (seg.p1, seg.p2);
416 swap (seg.epgeominfo[0].dist, seg.epgeominfo[1].dist);
417 seg.edgenr = ngMesh.GetNSeg() + 1; // segment id
418 ngMesh.AddSegment (seg);
421 } // loop on geomEdge ancestors
424 } // case TopAbs_EDGE
426 case TopAbs_FACE: { // FACE
427 // ----------------------
428 const TopoDS_Face& geomFace = TopoDS::Face( sm->GetSubShape() );
429 helper.SetSubShape( geomFace );
431 // Find solids the geomFace bounds
432 int solidID1 = 0, solidID2 = 0;
433 const TopTools_ListOfShape& ancestors = _mesh->GetAncestors( geomFace );
434 TopTools_ListIteratorOfListOfShape ancestorIt ( ancestors );
435 for ( ; ancestorIt.More(); ancestorIt.Next() )
437 const TopoDS_Shape & solid = ancestorIt.Value();
438 if ( solid.ShapeType() == TopAbs_SOLID ) {
439 int id = occgeom.somap.FindIndex ( solid );
440 if ( solidID1 && id != solidID1 ) solidID2 = id;
445 _faceDescriptors[ faceID ].first = solidID1;
446 _faceDescriptors[ faceID ].second = solidID2;
448 // Orient the face correctly in solidID1 (issue 0020206)
449 bool reverse = false;
451 TopoDS_Shape solid = occgeom.somap( solidID1 );
452 for ( TopExp_Explorer f( solid, TopAbs_FACE ); f.More(); f.Next() ) {
453 if ( geomFace.IsSame( f.Current() )) {
454 reverse = SMESH_Algo::IsReversedSubMesh( TopoDS::Face( f.Current()), helper.GetMeshDS() );
460 // Add surface elements
461 SMDS_ElemIteratorPtr faces = smDS->GetElements();
462 while ( faces->more() ) {
464 const SMDS_MeshElement* f = faces->next();
465 if ( f->NbNodes() % 3 != 0 ) { // not triangle
466 for ( ancestorIt.Initialize(ancestors); ancestorIt.More(); ancestorIt.Next() )
467 if ( ancestorIt.Value().ShapeType() == TopAbs_SOLID ) {
468 sm = _mesh->GetSubMesh( ancestorIt.Value() );
471 SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
472 smError.reset( new SMESH_ComputeError(COMPERR_BAD_INPUT_MESH,"Not triangle submesh"));
473 smError->myBadElements.push_back( f );
477 netgen::Element2d tri(3);
478 tri.SetIndex ( faceID );
480 for ( int i = 0; i < 3; ++i ) {
481 const SMDS_MeshNode* node = f->GetNode( i ), * inFaceNode=0;
482 if ( helper.IsSeamShape( node->GetPosition()->GetShapeId() ))
483 if ( helper.IsSeamShape( f->GetNodeWrap( i+1 )->GetPosition()->GetShapeId() ))
484 inFaceNode = f->GetNodeWrap( i-1 );
486 inFaceNode = f->GetNodeWrap( i+1 );
488 gp_XY uv = helper.GetNodeUV( geomFace, node, inFaceNode );
490 tri.GeomInfoPi(3-i).u = uv.X();
491 tri.GeomInfoPi(3-i).v = uv.Y();
492 tri.PNum (3-i) = ngNodeId( node, ngMesh, nodeNgIdMap );
494 tri.GeomInfoPi(i+1).u = uv.X();
495 tri.GeomInfoPi(i+1).v = uv.Y();
496 tri.PNum (i+1) = ngNodeId( node, ngMesh, nodeNgIdMap );
500 ngMesh.AddSurfaceElement (tri);
506 case TopAbs_VERTEX: { // VERTEX
507 // --------------------------
508 SMDS_NodeIteratorPtr nodeIt = smDS->GetNodes();
509 if ( nodeIt->more() )
510 ngNodeId( nodeIt->next(), ngMesh, nodeNgIdMap );
515 } // loop on submeshes
518 nodeVec.resize( ngMesh.GetNP() + 1 );
519 TNode2IdMap::iterator node_NgId, nodeNgIdEnd = nodeNgIdMap.end();
520 for ( node_NgId = nodeNgIdMap.begin(); node_NgId != nodeNgIdEnd; ++node_NgId)
521 nodeVec[ node_NgId->second ] = (SMDS_MeshNode*) node_NgId->first;
526 //=============================================================================
528 * Here we are going to use the NETGEN mesher
530 //=============================================================================
531 bool NETGENPlugin_Mesher::Compute()
534 netgen::MeshingParameters& mparams = netgen::GlobalMeshingParameters();
536 netgen::MeshingParameters& mparams = netgen::mparam;
538 MESSAGE("Compute with:\n"
539 " max size = " << mparams.maxh << "\n"
540 " segments per edge = " << mparams.segmentsperedge);
542 " growth rate = " << mparams.grading << "\n"
543 " elements per radius = " << mparams.curvaturesafety << "\n"
544 " second order = " << mparams.secondorder << "\n"
545 " quad allowed = " << mparams.quad);
547 SMESH_ComputeErrorPtr error = SMESH_ComputeError::New();
550 // -------------------------
551 // Prepare OCC geometry
552 // -------------------------
554 netgen::OCCGeometry occgeo;
555 list< SMESH_subMesh* > meshedSM;
556 PrepareOCCgeometry( occgeo, _shape, *_mesh, &meshedSM );
558 // -------------------------
560 // -------------------------
562 netgen::Mesh *ngMesh = NULL;
564 SMESH_Comment comment;
569 // vector of nodes in which node index == netgen ID
570 vector< SMDS_MeshNode* > nodeVec;
576 // pass 1D simple parameters to NETGEN
578 if ( int nbSeg = _simpleHyp->GetNumberOfSegments() ) {
580 mparams.segmentsperedge = nbSeg + 0.1;
581 mparams.maxh = occgeo.boundingbox.Diam();
582 mparams.grading = 0.01;
586 mparams.segmentsperedge = 1;
587 mparams.maxh = _simpleHyp->GetLocalLength();
590 // let netgen create ngMesh and calculate element size on not meshed shapes
592 int startWith = netgen::MESHCONST_ANALYSE;
593 int endWith = netgen::MESHCONST_ANALYSE;
594 err = netgen::OCCGenerateMesh(occgeo, ngMesh, startWith, endWith, optstr);
595 if (err) comment << "Error in netgen::OCCGenerateMesh() at MESHCONST_ANALYSE step";
597 // fill ngMesh with nodes and elements of computed submeshes
598 err = ! fillNgMesh(occgeo, *ngMesh, nodeVec, meshedSM);
599 nbInitNod = ngMesh->GetNP();
600 nbInitSeg = ngMesh->GetNSeg();
601 nbInitFac = ngMesh->GetNSE();
606 startWith = endWith = netgen::MESHCONST_MESHEDGES;
607 err = netgen::OCCGenerateMesh(occgeo, ngMesh, startWith, endWith, optstr);
608 if (err) comment << "Error in netgen::OCCGenerateMesh() at 1D mesh generation";
610 // ---------------------
611 // compute surface mesh
612 // ---------------------
615 // pass 2D simple parameters to NETGEN
617 if ( double area = _simpleHyp->GetMaxElementArea() ) {
619 mparams.maxh = sqrt(2. * area/sqrt(3.0));
620 mparams.grading = 0.4; // moderate size growth
625 TopTools_MapOfShape tmpMap;
626 for ( TopExp_Explorer exp( _shape, TopAbs_EDGE ); exp.More(); exp.Next() )
627 if( tmpMap.Add(exp.Current()) )
628 length += SMESH_Algo::EdgeLength( TopoDS::Edge( exp.Current() ));
630 if ( ngMesh->GetNSeg() ) {
631 // we have to multiply length by 2 since for each TopoDS_Edge there
632 // are double set of NETGEN edges or, in other words, we have to
633 // divide ngMesh->GetNSeg() on 2.
634 mparams.maxh = 2*length / ngMesh->GetNSeg();
638 mparams.grading = 0.2; // slow size growth
640 mparams.maxh = min( mparams.maxh, occgeo.boundingbox.Diam()/2 );
641 ngMesh->SetGlobalH (mparams.maxh);
642 netgen::Box<3> bb = occgeo.GetBoundingBox();
643 bb.Increase (bb.Diam()/20);
644 ngMesh->SetLocalH (bb.PMin(), bb.PMax(), mparams.grading);
646 // let netgen compute 2D mesh
647 startWith = netgen::MESHCONST_MESHSURFACE;
648 endWith = _optimize ? netgen::MESHCONST_OPTSURFACE : netgen::MESHCONST_MESHSURFACE;
649 err = netgen::OCCGenerateMesh(occgeo, ngMesh, startWith, endWith, optstr);
650 if (err) comment << "Error in netgen::OCCGenerateMesh() at surface mesh generation";
652 // ---------------------
653 // generate volume mesh
654 // ---------------------
655 if (!err && _isVolume)
657 // add ng face descriptors of meshed faces
658 std::map< int, std::pair<int,int> >::iterator fId_soIds = _faceDescriptors.begin();
659 for ( ; fId_soIds != _faceDescriptors.end(); ++fId_soIds ) {
660 int faceID = fId_soIds->first;
661 int solidID1 = fId_soIds->second.first;
662 int solidID2 = fId_soIds->second.second;
663 ngMesh->AddFaceDescriptor (netgen::FaceDescriptor(faceID, solidID1, solidID2, 0));
665 // pass 3D simple parameters to NETGEN
666 const NETGENPlugin_SimpleHypothesis_3D* simple3d =
667 dynamic_cast< const NETGENPlugin_SimpleHypothesis_3D* > ( _simpleHyp );
669 if ( double vol = simple3d->GetMaxElementVolume() ) {
671 mparams.maxh = pow( 72, 1/6. ) * pow( vol, 1/3. );
672 mparams.maxh = min( mparams.maxh, occgeo.boundingbox.Diam()/2 );
676 mparams.maxh = ngMesh->AverageH();
678 // netgen::ARRAY<double> maxhdom;
679 // maxhdom.SetSize (occgeo.NrSolids());
680 // maxhdom = mparams.maxh;
681 // ngMesh->SetMaxHDomain (maxhdom);
682 ngMesh->SetGlobalH (mparams.maxh);
683 mparams.grading = 0.4;
684 ngMesh->CalcLocalH();
686 // let netgen compute 3D mesh
687 startWith = netgen::MESHCONST_MESHVOLUME;
688 endWith = _optimize ? netgen::MESHCONST_OPTVOLUME : netgen::MESHCONST_MESHVOLUME;
689 err = netgen::OCCGenerateMesh(occgeo, ngMesh, startWith, endWith, optstr);
690 if (err) comment << "Error in netgen::OCCGenerateMesh()";
692 if (!err && mparams.secondorder > 0)
694 netgen::OCCRefinementSurfaces ref (occgeo);
695 ref.MakeSecondOrder (*ngMesh);
698 catch (netgen::NgException exc)
700 error->myName = err = COMPERR_ALGO_FAILED;
701 comment << exc.What();
704 int nbNod = ngMesh->GetNP();
705 int nbSeg = ngMesh->GetNSeg();
706 int nbFac = ngMesh->GetNSE();
707 int nbVol = ngMesh->GetNE();
709 MESSAGE((err ? "Mesh Generation failure" : "End of Mesh Generation") <<
710 ", nb nodes: " << nbNod <<
711 ", nb segments: " << nbSeg <<
712 ", nb faces: " << nbFac <<
713 ", nb volumes: " << nbVol);
715 // -----------------------------------------------------------
716 // Feed back the SMESHDS with the generated Nodes and Elements
717 // -----------------------------------------------------------
719 SMESHDS_Mesh* meshDS = _mesh->GetMeshDS();
720 bool isOK = ( !err && (_isVolume ? (nbVol > 0) : (nbFac > 0)) );
721 if ( true /*isOK*/ ) // get whatever built
723 // map of nodes assigned to submeshes
724 NCollection_Map<int> pindMap;
725 // create and insert nodes into nodeVec
726 nodeVec.resize( nbNod + 1 );
728 for (i = nbInitNod+1; i <= nbNod /*&& isOK*/; ++i )
730 const netgen::MeshPoint& ngPoint = ngMesh->Point(i);
731 SMDS_MeshNode* node = NULL;
732 bool newNodeOnVertex = false;
734 if (i-nbInitNod <= occgeo.vmap.Extent())
737 aVert = TopoDS::Vertex(occgeo.vmap(i-nbInitNod));
738 SMESHDS_SubMesh * submesh = meshDS->MeshElements(aVert);
741 SMDS_NodeIteratorPtr it = submesh->GetNodes();
744 node = const_cast<SMDS_MeshNode*> (it->next());
749 newNodeOnVertex = true;
752 node = meshDS->AddNode(ngPoint.X(), ngPoint.Y(), ngPoint.Z());
755 MESSAGE("Cannot create a mesh node");
756 if ( !comment.size() ) comment << "Cannot create a mesh node";
757 nbSeg = nbFac = nbVol = isOK = 0;
760 nodeVec.at(i) = node;
764 meshDS->SetNodeOnVertex(node, aVert);
769 // create mesh segments along geometric edges
770 NCollection_Map<Link> linkMap;
771 for (i = nbInitSeg+1; i <= nbSeg/* && isOK*/; ++i )
773 const netgen::Segment& seg = ngMesh->LineSegment(i);
774 Link link(seg.p1, seg.p2);
775 if (linkMap.Contains(link))
779 int pinds[3] = { seg.p1, seg.p2, seg.pmid };
782 for (int j=0; j < 3; ++j)
785 if (pind <= 0) continue;
792 int aGeomEdgeInd = seg.epgeominfo[j].edgenr;
793 if (aGeomEdgeInd > 0 && aGeomEdgeInd <= occgeo.emap.Extent())
794 aEdge = TopoDS::Edge(occgeo.emap(aGeomEdgeInd));
796 param = seg.epgeominfo[j].dist;
800 param = param2 * 0.5;
801 if (pind <= nbInitNod || pindMap.Contains(pind))
805 meshDS->SetNodeOnEdge(nodeVec.at(pind), aEdge, param);
810 if (nbp < 3) // second order ?
811 edge = meshDS->AddEdge(nodeVec.at(pinds[0]), nodeVec.at(pinds[1]));
813 edge = meshDS->AddEdge(nodeVec.at(pinds[0]), nodeVec.at(pinds[1]),
814 nodeVec.at(pinds[2]));
817 if ( !comment.size() ) comment << "Cannot create a mesh edge";
818 MESSAGE("Cannot create a mesh edge");
819 nbSeg = nbFac = nbVol = isOK = 0;
823 meshDS->SetMeshElementOnShape(edge, aEdge);
826 // create mesh faces along geometric faces
827 for (i = nbInitFac+1; i <= nbFac/* && isOK*/; ++i )
829 const netgen::Element2d& elem = ngMesh->SurfaceElement(i);
830 int aGeomFaceInd = elem.GetIndex();
832 if (aGeomFaceInd > 0 && aGeomFaceInd <= occgeo.fmap.Extent())
833 aFace = TopoDS::Face(occgeo.fmap(aGeomFaceInd));
834 vector<SMDS_MeshNode*> nodes;
835 for (int j=1; j <= elem.GetNP(); ++j)
837 int pind = elem.PNum(j);
838 SMDS_MeshNode* node = nodeVec.at(pind);
839 nodes.push_back(node);
840 if (pind <= nbInitNod || pindMap.Contains(pind))
844 const netgen::PointGeomInfo& pgi = elem.GeomInfoPi(j);
845 meshDS->SetNodeOnFace(node, aFace, pgi.u, pgi.v);
849 SMDS_MeshFace* face = NULL;
850 switch (elem.GetType())
853 face = meshDS->AddFace(nodes[0],nodes[1],nodes[2]);
856 face = meshDS->AddFace(nodes[0],nodes[1],nodes[2],nodes[3]);
859 face = meshDS->AddFace(nodes[0],nodes[1],nodes[2],nodes[5],nodes[3],nodes[4]);
862 face = meshDS->AddFace(nodes[0],nodes[1],nodes[2],nodes[3],
863 nodes[4],nodes[7],nodes[5],nodes[6]);
866 MESSAGE("NETGEN created a face of unexpected type, ignoring");
871 if ( !comment.size() ) comment << "Cannot create a mesh face";
872 MESSAGE("Cannot create a mesh face");
873 nbSeg = nbFac = nbVol = isOK = 0;
877 meshDS->SetMeshElementOnShape(face, aFace);
881 for (i = 1; i <= nbVol/* && isOK*/; ++i)
883 const netgen::Element& elem = ngMesh->VolumeElement(i);
884 int aSolidInd = elem.GetIndex();
886 if (aSolidInd > 0 && aSolidInd <= occgeo.somap.Extent())
887 aSolid = TopoDS::Solid(occgeo.somap(aSolidInd));
888 vector<SMDS_MeshNode*> nodes;
889 for (int j=1; j <= elem.GetNP(); ++j)
891 int pind = elem.PNum(j);
892 SMDS_MeshNode* node = nodeVec.at(pind);
893 nodes.push_back(node);
894 if (pind <= nbInitNod || pindMap.Contains(pind))
896 if (!aSolid.IsNull())
899 meshDS->SetNodeInVolume(node, aSolid);
903 SMDS_MeshVolume* vol = NULL;
904 switch (elem.GetType())
907 vol = meshDS->AddVolume(nodes[0],nodes[1],nodes[2],nodes[3]);
910 vol = meshDS->AddVolume(nodes[0],nodes[1],nodes[2],nodes[3],
911 nodes[4],nodes[7],nodes[5],nodes[6],nodes[8],nodes[9]);
914 MESSAGE("NETGEN created a volume of unexpected type, ignoring");
919 if ( !comment.size() ) comment << "Cannot create a mesh volume";
920 MESSAGE("Cannot create a mesh volume");
921 nbSeg = nbFac = nbVol = isOK = 0;
924 if (!aSolid.IsNull())
925 meshDS->SetMeshElementOnShape(vol, aSolid);
929 if ( error->IsOK() && ( !isOK || comment.size() > 0 ))
930 error->myName = COMPERR_ALGO_FAILED;
931 if ( !comment.empty() )
932 error->myComment = comment;
934 // set bad compute error to subshapes of all failed subshapes shapes
935 if ( !error->IsOK() && err )
937 for (int i = 1; i <= occgeo.fmap.Extent(); i++) {
938 int status = occgeo.facemeshstatus[i-1];
939 if (status == 1 ) continue;
940 if ( SMESH_subMesh* sm = _mesh->GetSubMeshContaining( occgeo.fmap( i ))) {
941 SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
942 if ( !smError || smError->IsOK() ) {
944 smError.reset( new SMESH_ComputeError( error->myName, error->myComment ));
946 smError.reset( new SMESH_ComputeError( COMPERR_ALGO_FAILED, "Ignored" ));
952 nglib::Ng_DeleteMesh((nglib::Ng_Mesh*)ngMesh);
957 return error->IsOK();
960 //================================================================================
962 * \brief Remove "test.out" and "problemfaces" files in current directory
964 //================================================================================
966 void NETGENPlugin_Mesher::RemoveTmpFiles()
968 removeFile("test.out");
969 removeFile("problemfaces");
973 //=============================================================================
977 //=============================================================================
978 bool NETGENPlugin_Mesher::Evaluate(MapShapeNbElems& aResMap)
981 netgen::MeshingParameters& mparams = netgen::GlobalMeshingParameters();
983 netgen::MeshingParameters& mparams = netgen::mparam;
987 // -------------------------
988 // Prepare OCC geometry
989 // -------------------------
990 netgen::OCCGeometry occgeo;
991 list< SMESH_subMesh* > meshedSM;
992 PrepareOCCgeometry( occgeo, _shape, *_mesh, &meshedSM );
994 bool tooManyElems = false;
995 const int hugeNb = std::numeric_limits<int>::max() / 100;
1000 // pass 1D simple parameters to NETGEN
1003 if ( int nbSeg = _simpleHyp->GetNumberOfSegments() ) {
1006 mparams.segmentsperedge = nbSeg + 0.1;
1007 mparams.maxh = occgeo.boundingbox.Diam();
1008 mparams.grading = 0.01;
1012 mparams.segmentsperedge = 1;
1013 mparams.maxh = _simpleHyp->GetLocalLength();
1016 TopTools_DataMapOfShapeInteger EdgesMap;
1017 double fullLen = 0.0;
1018 double fullNbSeg = 0;
1019 for (TopExp_Explorer exp(_shape, TopAbs_EDGE); exp.More(); exp.Next()) {
1020 TopoDS_Edge E = TopoDS::Edge( exp.Current() );
1021 if( EdgesMap.IsBound(E) )
1023 SMESH_subMesh *sm = _mesh->GetSubMesh(E);
1024 std::vector<int> aVec(SMDSEntity_Last, 0);
1025 double aLen = SMESH_Algo::EdgeLength(E);
1028 tooManyElems = ( aLen/hugeNb > mparams.maxh );
1029 if(nb1d==0 && !tooManyElems) {
1030 nb1d = (int)( aLen/mparams.maxh + 1 );
1032 if ( tooManyElems ) // avoid FPE
1034 aVec[SMDSEntity_Node] = hugeNb;
1035 aVec[ mparams.secondorder > 0 ? SMDSEntity_Quad_Edge : SMDSEntity_Edge] = hugeNb;
1040 if( mparams.secondorder > 0 ) {
1041 aVec[SMDSEntity_Node] = 2*nb1d - 1;
1042 aVec[SMDSEntity_Quad_Edge] = nb1d;
1045 aVec[SMDSEntity_Node] = nb1d - 1;
1046 aVec[SMDSEntity_Edge] = nb1d;
1049 aResMap.insert(std::make_pair(sm,aVec));
1050 EdgesMap.Bind(E,nb1d);
1057 if ( double area = _simpleHyp->GetMaxElementArea() ) {
1059 mparams.maxh = sqrt(2. * area/sqrt(3.0));
1060 mparams.grading = 0.4; // moderate size growth
1063 // length from edges
1064 mparams.maxh = fullLen/fullNbSeg;
1065 mparams.grading = 0.2; // slow size growth
1067 mparams.maxh = min( mparams.maxh, occgeo.boundingbox.Diam()/2 );
1070 for (TopExp_Explorer exp(_shape, TopAbs_FACE); exp.More(); exp.Next())
1072 TopoDS_Face F = TopoDS::Face( exp.Current() );
1073 SMESH_subMesh *sm = _mesh->GetSubMesh(F);
1075 BRepGProp::SurfaceProperties(F,G);
1076 double anArea = G.Mass();
1077 tooManyElems = tooManyElems || ( anArea/hugeNb > mparams.maxh*mparams.maxh );
1079 if ( !tooManyElems )
1080 for (TopExp_Explorer exp1(F,TopAbs_EDGE); exp1.More(); exp1.Next())
1081 nb1d += EdgesMap.Find(exp1.Current());
1083 int nbFaces = tooManyElems ? hugeNb : int( 4*anArea / mparams.maxh*mparams.maxh*sqrt(3.));
1084 int nbNodes = tooManyElems ? hugeNb : (( nbFaces*3 - (nb1d-1)*2 ) / 6 + 1 );
1086 std::vector<int> aVec(SMDSEntity_Last, 0);
1087 if( mparams.secondorder > 0 ) {
1088 int nb1d_in = (nbFaces*3 - nb1d) / 2;
1089 aVec[SMDSEntity_Node] = nbNodes + nb1d_in;
1090 aVec[SMDSEntity_Quad_Triangle] = nbFaces;
1093 aVec[SMDSEntity_Node] = nbNodes;
1094 aVec[SMDSEntity_Triangle] = nbFaces;
1096 aResMap.insert(std::make_pair(sm,aVec));
1103 // pass 3D simple parameters to NETGEN
1104 const NETGENPlugin_SimpleHypothesis_3D* simple3d =
1105 dynamic_cast< const NETGENPlugin_SimpleHypothesis_3D* > ( _simpleHyp );
1107 if ( double vol = simple3d->GetMaxElementVolume() ) {
1109 mparams.maxh = pow( 72, 1/6. ) * pow( vol, 1/3. );
1110 mparams.maxh = min( mparams.maxh, occgeo.boundingbox.Diam()/2 );
1113 // using previous length from faces
1115 mparams.grading = 0.4;
1118 BRepGProp::VolumeProperties(_shape,G);
1119 double aVolume = G.Mass();
1120 double tetrVol = 0.1179*mparams.maxh*mparams.maxh*mparams.maxh;
1121 tooManyElems = tooManyElems || ( aVolume/hugeNb > tetrVol );
1122 int nbVols = tooManyElems ? hugeNb : int(aVolume/tetrVol);
1123 int nb1d_in = int(( nbVols*6 - fullNbSeg ) / 6 );
1124 std::vector<int> aVec(SMDSEntity_Last, 0 );
1125 if ( tooManyElems ) // avoid FPE
1127 aVec[SMDSEntity_Node] = hugeNb;
1128 aVec[ mparams.secondorder > 0 ? SMDSEntity_Quad_Tetra : SMDSEntity_Tetra] = hugeNb;
1132 if( mparams.secondorder > 0 ) {
1133 aVec[SMDSEntity_Node] = nb1d_in/3 + 1 + nb1d_in;
1134 aVec[SMDSEntity_Quad_Tetra] = nbVols;
1137 aVec[SMDSEntity_Node] = nb1d_in/3 + 1;
1138 aVec[SMDSEntity_Tetra] = nbVols;
1141 SMESH_subMesh *sm = _mesh->GetSubMesh(_shape);
1142 aResMap.insert(std::make_pair(sm,aVec));