// NETGENPlugin : C++ implementation // // Copyright (C) 2006 OPEN CASCADE, CEA/DEN, EDF R&D // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // // // File : NETGENPlugin_Mesher.cxx // Author : Michael Sazonov (OCN) // Date : 31/03/2006 // Project : SALOME // $Header$ //============================================================================= #include "NETGENPlugin_Mesher.hxx" #include "NETGENPlugin_Hypothesis_2D.hxx" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Netgen include files namespace nglib { #include } #define OCCGEOMETRY #include #include //#include namespace netgen { extern int OCCGenerateMesh (OCCGeometry&, Mesh*&, int, int, char*); extern MeshingParameters mparam; } using namespace std; //============================================================================= /*! * */ //============================================================================= NETGENPlugin_Mesher::NETGENPlugin_Mesher (SMESH_Mesh* mesh, const TopoDS_Shape& aShape, const bool isVolume) : _mesh (mesh), _shape (aShape), _isVolume(isVolume), _optimize(true) { #ifdef WNT netgen::MeshingParameters& mparams = netgen::GlobalMeshingParameters(); #else netgen::MeshingParameters& mparams = netgen::mparam; #endif // Initialize global NETGEN parameters by default values: // maximal mesh edge size mparams.maxh = NETGENPlugin_Hypothesis::GetDefaultMaxSize(); // minimal number of segments per edge mparams.segmentsperedge = NETGENPlugin_Hypothesis::GetDefaultNbSegPerEdge(); // rate of growth of size between elements mparams.grading = NETGENPlugin_Hypothesis::GetDefaultGrowthRate(); // safety factor for curvatures (elements per radius) mparams.curvaturesafety = NETGENPlugin_Hypothesis::GetDefaultNbSegPerRadius(); // create elements of second order mparams.secondorder = NETGENPlugin_Hypothesis::GetDefaultSecondOrder() ? 1 : 0; // quad-dominated surface meshing if (_isVolume) mparams.quad = 0; else mparams.quad = NETGENPlugin_Hypothesis_2D::GetDefaultQuadAllowed() ? 1 : 0; } //============================================================================= /*! * Pass parameters to NETGEN */ //============================================================================= void NETGENPlugin_Mesher::SetParameters(const NETGENPlugin_Hypothesis* hyp) { if (hyp) { #ifdef WNT netgen::MeshingParameters& mparams = netgen::GlobalMeshingParameters(); #else netgen::MeshingParameters& mparams = netgen::mparam; #endif // Initialize global NETGEN parameters: // maximal mesh segment size mparams.maxh = hyp->GetMaxSize(); // minimal number of segments per edge mparams.segmentsperedge = hyp->GetNbSegPerEdge(); // rate of growth of size between elements mparams.grading = hyp->GetGrowthRate(); // safety factor for curvatures (elements per radius) mparams.curvaturesafety = hyp->GetNbSegPerRadius(); // create elements of second order mparams.secondorder = hyp->GetSecondOrder() ? 1 : 0; // quad-dominated surface meshing // only triangles are allowed for volumic mesh if (!_isVolume) mparams.quad = static_cast (hyp)->GetQuadAllowed() ? 1 : 0; _optimize = hyp->GetOptimize(); } } //============================================================================= /*! * Link - a pair of integer numbers */ //============================================================================= struct Link { int n1, n2; Link(int _n1, int _n2) : n1(_n1), n2(_n2) {} Link() : n1(0), n2(0) {} }; int HashCode(const Link& aLink, int aLimit) { return HashCode(aLink.n1 + aLink.n2, aLimit); } Standard_Boolean IsEqual(const Link& aLink1, const Link& aLink2) { return (aLink1.n1 == aLink2.n1 && aLink1.n2 == aLink2.n2 || aLink1.n1 == aLink2.n2 && aLink1.n2 == aLink2.n1); } //================================================================================ /*! * \brief Initialize netgen::OCCGeometry with OCCT shape */ //================================================================================ void NETGENPlugin_Mesher::PrepareOCCgeometry(netgen::OCCGeometry& occgeo, const TopoDS_Shape& shape) { occgeo.shape = shape; occgeo.changed = 1; occgeo.BuildFMap(); BRepTools::Clean (shape); BRepMesh_IncrementalMesh::BRepMesh_IncrementalMesh (shape, 0.01, true); Bnd_Box bb; BRepBndLib::Add (shape, bb); double x1,y1,z1,x2,y2,z2; bb.Get (x1,y1,z1,x2,y2,z2); MESSAGE("shape bounding box:\n" << "(" << x1 << " " << y1 << " " << z1 << ") " << "(" << x2 << " " << y2 << " " << z2 << ")"); netgen::Point<3> p1 = netgen::Point<3> (x1,y1,z1); netgen::Point<3> p2 = netgen::Point<3> (x2,y2,z2); occgeo.boundingbox = netgen::Box<3> (p1,p2); } //============================================================================= /*! * Here we are going to use the NETGEN mesher */ //============================================================================= bool NETGENPlugin_Mesher::Compute() { #ifdef WNT netgen::MeshingParameters& mparams = netgen::GlobalMeshingParameters(); #else netgen::MeshingParameters& mparams = netgen::mparam; #endif MESSAGE("Compute with:\n" " max size = " << mparams.maxh << "\n" " segments per edge = " << mparams.segmentsperedge); MESSAGE("\n" " growth rate = " << mparams.grading << "\n" " elements per radius = " << mparams.curvaturesafety << "\n" " second order = " << mparams.secondorder << "\n" " quad allowed = " << mparams.quad); SMESH_ComputeErrorPtr error = SMESH_ComputeError::New(); nglib::Ng_Init(); // ------------------------- // Prepare OCC geometry // ------------------------- netgen::OCCGeometry occgeo; PrepareOCCgeometry( occgeo, _shape ); // ------------------------- // Generate the mesh // ------------------------- netgen::Mesh *ngMesh = NULL; // we start always with ANALYSE, // but end depending on _optimize and _isVolume int startWith = netgen::MESHCONST_ANALYSE; int endWith = (_optimize ? (_isVolume ? netgen::MESHCONST_OPTVOLUME : netgen::MESHCONST_OPTSURFACE) : netgen::MESHCONST_MESHSURFACE); char *optstr = 0; int err = 0; SMESH_Comment comment; try { err = netgen::OCCGenerateMesh(occgeo, ngMesh, startWith, endWith, optstr); if (err) comment << "Error in netgen::OCCGenerateMesh()"; if (!err && !_optimize) { // we have got surface mesh only, so generate volume mesh startWith = endWith = netgen::MESHCONST_MESHVOLUME; err = netgen::OCCGenerateMesh(occgeo, ngMesh, startWith, endWith, optstr); if (err) comment << "Error in netgen::OCCGenerateMesh()"; } if (!err && mparams.secondorder > 0) { netgen::OCCRefinementSurfaces ref (occgeo); ref.MakeSecondOrder (*ngMesh); } } catch (netgen::NgException exc) { error->myName = err = COMPERR_ALGO_FAILED; comment << exc.What(); } int nbNod = ngMesh->GetNP(); int nbSeg = ngMesh->GetNSeg(); int nbFac = ngMesh->GetNSE(); int nbVol = ngMesh->GetNE(); MESSAGE((err ? "Mesh Generation failure" : "End of Mesh Generation") << ", nb nodes: " << nbNod << ", nb segments: " << nbSeg << ", nb faces: " << nbFac << ", nb volumes: " << nbVol); // ----------------------------------------------------------- // Feed back the SMESHDS with the generated Nodes and Elements // ----------------------------------------------------------- SMESHDS_Mesh* meshDS = _mesh->GetMeshDS(); bool isOK = ( !err && (_isVolume ? (nbVol > 0) : (nbFac > 0)) ); if ( true /*isOK*/ ) // get whatever built { // vector of nodes in which node index == netgen ID vector< SMDS_MeshNode* > nodeVec ( nbNod + 1 ); // map of nodes assigned to submeshes NCollection_Map pindMap; // create and insert nodes into nodeVec int i; for (i = 1; i <= nbNod /*&& isOK*/; ++i ) { const netgen::MeshPoint& ngPoint = ngMesh->Point(i); SMDS_MeshNode* node = NULL; bool newNodeOnVertex = false; TopoDS_Vertex aVert; if (i <= occgeo.vmap.Extent()) { // point on vertex aVert = TopoDS::Vertex(occgeo.vmap(i)); SMESHDS_SubMesh * submesh = meshDS->MeshElements(aVert); if (submesh) { SMDS_NodeIteratorPtr it = submesh->GetNodes(); if (it->more()) { node = const_cast (it->next()); pindMap.Add(i); } } if (!node) newNodeOnVertex = true; } if (!node) node = meshDS->AddNode(ngPoint.X(), ngPoint.Y(), ngPoint.Z()); if (!node) { MESSAGE("Cannot create a mesh node"); if ( !comment.size() ) comment << "Cannot create a mesh node"; nbSeg = nbFac = nbVol = isOK = 0; break; } nodeVec.at(i) = node; if (newNodeOnVertex) { // point on vertex meshDS->SetNodeOnVertex(node, aVert); pindMap.Add(i); } } // create mesh segments along geometric edges NCollection_Map linkMap; for (i = 1; i <= nbSeg/* && isOK*/; ++i ) { const netgen::Segment& seg = ngMesh->LineSegment(i); Link link(seg.p1, seg.p2); if (linkMap.Contains(link)) continue; linkMap.Add(link); TopoDS_Edge aEdge; int pinds[3] = { seg.p1, seg.p2, seg.pmid }; int nbp = 0; double param2 = 0; for (int j=0; j < 3; ++j) { int pind = pinds[j]; if (pind <= 0) continue; ++nbp; double param; if (j < 2) { if (aEdge.IsNull()) { int aGeomEdgeInd = seg.epgeominfo[j].edgenr; if (aGeomEdgeInd > 0 && aGeomEdgeInd <= occgeo.emap.Extent()) aEdge = TopoDS::Edge(occgeo.emap(aGeomEdgeInd)); } param = seg.epgeominfo[j].dist; param2 += param; } else param = param2 * 0.5; if (pindMap.Contains(pind)) continue; if (!aEdge.IsNull()) { meshDS->SetNodeOnEdge(nodeVec.at(pind), aEdge, param); pindMap.Add(pind); } } SMDS_MeshEdge* edge; if (nbp < 3) // second order ? edge = meshDS->AddEdge(nodeVec.at(pinds[0]), nodeVec.at(pinds[1])); else edge = meshDS->AddEdge(nodeVec.at(pinds[0]), nodeVec.at(pinds[1]), nodeVec.at(pinds[2])); if (!edge) { if ( !comment.size() ) comment << "Cannot create a mesh edge"; MESSAGE("Cannot create a mesh edge"); nbSeg = nbFac = nbVol = isOK = 0; break; } if (!aEdge.IsNull()) meshDS->SetMeshElementOnShape(edge, aEdge); } // create mesh faces along geometric faces for (i = 1; i <= nbFac/* && isOK*/; ++i ) { const netgen::Element2d& elem = ngMesh->SurfaceElement(i); int aGeomFaceInd = elem.GetIndex(); TopoDS_Face aFace; if (aGeomFaceInd > 0 && aGeomFaceInd <= occgeo.fmap.Extent()) aFace = TopoDS::Face(occgeo.fmap(aGeomFaceInd)); vector nodes; for (int j=1; j <= elem.GetNP(); ++j) { int pind = elem.PNum(j); SMDS_MeshNode* node = nodeVec.at(pind); nodes.push_back(node); if (pindMap.Contains(pind)) continue; if (!aFace.IsNull()) { const netgen::PointGeomInfo& pgi = elem.GeomInfoPi(j); meshDS->SetNodeOnFace(node, aFace, pgi.u, pgi.v); pindMap.Add(pind); } } SMDS_MeshFace* face = NULL; switch (elem.GetType()) { case netgen::TRIG: face = meshDS->AddFace(nodes[0],nodes[1],nodes[2]); break; case netgen::QUAD: face = meshDS->AddFace(nodes[0],nodes[1],nodes[2],nodes[3]); break; case netgen::TRIG6: face = meshDS->AddFace(nodes[0],nodes[1],nodes[2],nodes[5],nodes[3],nodes[4]); break; case netgen::QUAD8: face = meshDS->AddFace(nodes[0],nodes[1],nodes[2],nodes[3], nodes[4],nodes[7],nodes[5],nodes[6]); break; default: MESSAGE("NETGEN created a face of unexpected type, ignoring"); continue; } if (!face) { if ( !comment.size() ) comment << "Cannot create a mesh face"; MESSAGE("Cannot create a mesh face"); nbSeg = nbFac = nbVol = isOK = 0; break; } if (!aFace.IsNull()) meshDS->SetMeshElementOnShape(face, aFace); } // create tetrahedra for (i = 1; i <= nbVol/* && isOK*/; ++i) { const netgen::Element& elem = ngMesh->VolumeElement(i); int aSolidInd = elem.GetIndex(); TopoDS_Solid aSolid; if (aSolidInd > 0 && aSolidInd <= occgeo.somap.Extent()) aSolid = TopoDS::Solid(occgeo.somap(aSolidInd)); vector nodes; for (int j=1; j <= elem.GetNP(); ++j) { int pind = elem.PNum(j); SMDS_MeshNode* node = nodeVec.at(pind); nodes.push_back(node); if (pindMap.Contains(pind)) continue; if (!aSolid.IsNull()) { // point in solid meshDS->SetNodeInVolume(node, aSolid); pindMap.Add(pind); } } SMDS_MeshVolume* vol = NULL; switch (elem.GetType()) { case netgen::TET: vol = meshDS->AddVolume(nodes[0],nodes[1],nodes[2],nodes[3]); break; case netgen::TET10: vol = meshDS->AddVolume(nodes[0],nodes[1],nodes[2],nodes[3], nodes[4],nodes[7],nodes[5],nodes[6],nodes[8],nodes[9]); break; default: MESSAGE("NETGEN created a volume of unexpected type, ignoring"); continue; } if (!vol) { if ( !comment.size() ) comment << "Cannot create a mesh volume"; MESSAGE("Cannot create a mesh volume"); nbSeg = nbFac = nbVol = isOK = 0; break; } if (!aSolid.IsNull()) meshDS->SetMeshElementOnShape(vol, aSolid); } } if ( error->IsOK() && ( !isOK || comment.size() > 0 )) error->myName = COMPERR_ALGO_FAILED; if ( !comment.empty() ) error->myComment = comment; // set bad compute error to subshapes of all failed subshapes shapes if ( !error->IsOK() && err ) { for (int i = 1; i <= occgeo.fmap.Extent(); i++) { int status = occgeo.facemeshstatus[i-1]; if (status == 1 ) continue; if ( SMESH_subMesh* sm = _mesh->GetSubMeshContaining( occgeo.fmap( i ))) { SMESH_ComputeErrorPtr& smError = sm->GetComputeError(); if ( !smError || smError->IsOK() ) { if ( status == -1 ) smError.reset( new SMESH_ComputeError( error->myName, error->myComment )); else smError.reset( new SMESH_ComputeError( COMPERR_ALGO_FAILED, "Ignored" )); } } } } nglib::Ng_DeleteMesh((nglib::Ng_Mesh*)ngMesh); nglib::Ng_Exit(); RemoveTmpFiles(); return error->IsOK(); } //================================================================================ /*! * \brief Remove "test.out" and "problemfaces" files in current directory */ //================================================================================ void NETGENPlugin_Mesher::RemoveTmpFiles() { TCollection_AsciiString str("test.out"); OSD_Path path1( str ); OSD_File file1( path1 ); file1.Remove(); str = "problemfaces"; OSD_Path path2( str ); OSD_File file2( path2 ); file2.Remove(); }