From: cconopoima Date: Tue, 25 Jul 2023 19:15:25 +0000 (-0300) Subject: [bos #35147] [EDF] (2023-T1) Decompose Viscous Layer API. X-Git-Tag: V9_12_0b1~4 X-Git-Url: http://git.salome-platform.org/gitweb/?a=commitdiff_plain;h=refs%2Fheads%2Fcce%2F35147VL;p=modules%2Fsmesh.git [bos #35147] [EDF] (2023-T1) Decompose Viscous Layer API. Delete .vscode/settings.json Publish shrinkGeometry. Refactor StdMeshers_Cartesian_3D and StdMeshers_Cartesian_VL classes to allow passing an arbitrary shrink mesh to the viscous layer builder. Use StdMeshers_Cartesian_VL in StdMeshers_ViscousLayerBuilder to handle geometry shrinking and viscous layer building. Create maps to link shrink solid and the solid assign to the shrink mesh. Code cleanup. Defining map btw original shape (TopAbs_COMPOUND) and result shrink object. Refactor to support viscous layer of faces. Code clean up and add of tests. Documentation and code cleanup erase debug comment. Modif after code review. Avoid compilation warning from ViscousLayerBuilder and ViscousLayerBuilder_i classes. --- diff --git a/doc/examples/defining_hypotheses_vlapi_ex17.py b/doc/examples/defining_hypotheses_vlapi_ex17.py new file mode 100644 index 000000000..3f4ed3630 --- /dev/null +++ b/doc/examples/defining_hypotheses_vlapi_ex17.py @@ -0,0 +1,84 @@ +# Viscous layers construction + +import salome +salome.salome_init_without_session() + +import SMESH +from salome.geom import geomBuilder +from salome.smesh import smeshBuilder + +geom_builder = geomBuilder.New() +smesh_builder = smeshBuilder.New() + +X = geom_builder.MakeVectorDXDYDZ( 1,0,0 ) +O = geom_builder.MakeVertex( 100,50,50 ) +plane = geom_builder.MakePlane( O, X, 200 ) # plane YZ + +box = geom_builder.MakeBoxDXDYDZ(200,100,100) + +shape = geom_builder.MakeHalfPartition( box, plane ) + +faces = geom_builder.SubShapeAllSorted(shape, geom_builder.ShapeType["FACE"]) +face1 = faces[1] +# 4 left, 34 middle, 50 right +# Have to pass the middle face id, otherwise it is going to create two disjoint boxes +# because the common face is not going to be ignored and both boxes are going to shrink +# in this direction too +ignoreFaces = [4,34,50] + +geom_builder.addToStudy( shape, "shape" ) +geom_builder.addToStudyInFather( shape, face1, "face1") + +# 3D Viscous layers +mesh = smesh_builder.Mesh(shape, "CFD") + +ViscousBuilder = mesh.ViscousLayerBuilder() +thickness = 20 +numberOfLayers = 10 +stretchFactor = 1.5 +groupName = "Boundary layers" +ViscousBuilder.setBuilderParameters( thickness, numberOfLayers, stretchFactor, + ignoreFaces, # optional + groupName = groupName ) # optional + +Shrinkshape = ViscousBuilder.GetShrinkGeometry() + +shrinkMesh = smesh_builder.Mesh(Shrinkshape, "Shrink") +shrinkMesh.Segment().NumberOfSegments( 4 ) +faces = geom_builder.SubShapeAllSorted(Shrinkshape, geom_builder.ShapeType["FACE"]) +shrinkFace1 = faces[1] + +shrinkMesh.Triangle() +shrinkMesh.Quadrangle(shrinkFace1) +algo3D = shrinkMesh.Tetrahedron() + +if not shrinkMesh.Compute(): raise Exception("Error when computing Mesh") + +#Add viscous layer +FinalMesh = ViscousBuilder.AddLayers( shrinkMesh ) + +mesh.MakeGroup("Tetras",SMESH.VOLUME,SMESH.FT_ElemGeomType,"=",SMESH.Geom_TETRA) +mesh.MakeGroup("Pyras",SMESH.VOLUME,SMESH.FT_ElemGeomType,"=",SMESH.Geom_PYRAMID) +mesh.MakeGroup("Prims",SMESH.VOLUME,SMESH.FT_ElemGeomType,"=",SMESH.Geom_PENTA) + +# 2D Viscous layers + +# 3 edges of the 4 edges of face1 +edgeIds = geom_builder.SubShapeAllIDs( face1, geom_builder.ShapeType["EDGE"])[:-1] + +mesh = smesh_builder.Mesh(face1,"Face1") +ViscousBuilder = mesh.ViscousLayerBuilder() +ViscousBuilder.setBuilderParameters( 2, 3, 1.5, + edgeIds, True, # optional + groupName = groupName ) # optional + +#For 2D, edges are not selectable (to be developed in occt) the entire face is shrink +shrinkFace = ViscousBuilder.GetShrinkGeometry() +shrinkMesh = smesh_builder.Mesh(shrinkFace, "VicsousLayers2D") + +shrinkMesh.Segment().NumberOfSegments( 5 ) +algo2D = shrinkMesh.Triangle() + +if not shrinkMesh.Compute(): raise Exception("Error when computing Mesh of shrink face") + +FinalMeshFace = ViscousBuilder.AddLayers( shrinkMesh ) \ No newline at end of file diff --git a/doc/examples/tests.set b/doc/examples/tests.set index 86d5e8787..273c4b4ab 100644 --- a/doc/examples/tests.set +++ b/doc/examples/tests.set @@ -37,6 +37,7 @@ SET(BAD_TESTS defining_hypotheses_ex10.py defining_hypotheses_ex11.py defining_hypotheses_ex17.py + defining_hypotheses_vlapi_ex17.py defining_hypotheses_adaptive1d.py filters_ex01.py filters_ex02.py diff --git a/doc/gui/input/additional_hypo.rst b/doc/gui/input/additional_hypo.rst index 73a916bb8..21c42ea82 100644 --- a/doc/gui/input/additional_hypo.rst +++ b/doc/gui/input/additional_hypo.rst @@ -136,6 +136,27 @@ computations. **See also** a sample TUI script of a :ref:`tui_viscous_layers`. +.. _viscous_layers_api_anchor: + +Viscous Layers API +#################################### + +The Viscous layer API is available on TUI. Allows to compute a shrink version of the geometry. This shrank version can be passed to any mesher and be used to +build the viscous layer from the mesh computed in the shrank geometry. The current implementation only support the **Face offset** method to extrude the +nodes from the shrank mesh to the original geometry. + +This implementation supports 3D (Solids and Solid Compound) and 2D (Face) geometries. For the 3D case, the faces Ids are +used to identify the faces (perpendicular to it) where the solid is to be reduced. For the 2D case, the edges Ids can be provided but will not have any effect +on the geometry computation (this control is not available in the opencascade library), therefore, for this case the entire face is shrank. + +The Viscous Layer API receive the same parameters as the Viscous Layers Hypothesis and implements four methods: + +* The constructor ``ViscousLayerBuilder()`` +* The parameters definitions ``setBuilderParameters(...)`` +* The ``GetShrinkGeometry()`` method that returns the shrink version of the original geomtry. +* The ``AddLayers( shrinkMesh )`` method that returns the complet version of the mesh (shrink+viscous layer) + +**See also** a sample TUI script of a :ref:`tui_viscous_layers_api`. .. _quadratic_mesh_anchor: diff --git a/doc/gui/input/tui_defining_hypotheses.rst b/doc/gui/input/tui_defining_hypotheses.rst index 2832d9423..0121a0ce0 100644 --- a/doc/gui/input/tui_defining_hypotheses.rst +++ b/doc/gui/input/tui_defining_hypotheses.rst @@ -251,6 +251,16 @@ Viscous layers construction :download:`Download this script <../../examples/defining_hypotheses_ex17.py>` +.. _tui_viscous_layers_api: + +Viscous layers API construction +########################### + +.. literalinclude:: ../../examples/defining_hypotheses_vlapi_ex17.py + :language: python + +:download:`Download this script <../../examples/defining_hypotheses_vlapi_ex17.py>` + .. _tui_radial_prism: Radial Prism example diff --git a/idl/SMESH_BasicHypothesis.idl b/idl/SMESH_BasicHypothesis.idl index 2cd751366..30281a309 100644 --- a/idl/SMESH_BasicHypothesis.idl +++ b/idl/SMESH_BasicHypothesis.idl @@ -26,10 +26,11 @@ #ifndef _SMESH_BASICHYPOTHESIS_IDL_ #define _SMESH_BASICHYPOTHESIS_IDL_ +#include "GEOM_Gen.idl" + #include "SALOME_Exception.idl" #include "SMESH_Hypothesis.idl" #include "SMESH_Mesh.idl" - /*! * StdMeshers: interfaces to standard hypotheses and algorithms */ @@ -1247,6 +1248,39 @@ module StdMeshers interface StdMeshers_Cartesian_3D : SMESH::SMESH_3D_Algo { }; + /*! + * StdMeshers_Cartesian_3D: interface of "ViscousLayerBuilder" algorithm + */ + interface StdMeshers_ViscousLayerBuilder : SMESH::SMESH_2D_Algo + { + /*! + * Set faces to exclude from the definition of face to shrink + */ + void SetIgnoreFaces(in SMESH::long_array faceIDs) raises (SALOME::SALOME_Exception); + // SMESH::long_array GetIgnoreFaces(); + + /*! + * Set faces either to exclude from treatment or to make the offset geometry on. + */ + void SetFaces(in SMESH::long_array faceIDs, + in boolean toIgnore) raises (SALOME::SALOME_Exception); + // SMESH::long_array GetFaces(); + // boolean GetIsToIgnoreFaces(); + + void SetTotalThickness(in double thickness) raises (SALOME::SALOME_Exception); + void SetNumberLayers(in short numberOfLayers ) raises (SALOME::SALOME_Exception); + void SetStretchFactor(in double strechFactor ) raises (SALOME::SALOME_Exception); + void SetMethod( in VLExtrusionMethod how ); + void SetGroupName(in string name); + + GEOM::GEOM_Object GetShrinkGeometry( in SMESH::SMESH_Mesh finalMesh, in GEOM::GEOM_Object theObject ); + + /*! + * Build the prismatic layer from the shrink mesh + */ + boolean AddLayers( in SMESH::SMESH_Mesh sourceMesh, in SMESH::SMESH_Mesh finalMesh, in GEOM::GEOM_Object theObject ); + + }; }; diff --git a/src/SMESH_SWIG/StdMeshersBuilder.py b/src/SMESH_SWIG/StdMeshersBuilder.py index 05d3ffd8e..015050e12 100644 --- a/src/SMESH_SWIG/StdMeshersBuilder.py +++ b/src/SMESH_SWIG/StdMeshersBuilder.py @@ -25,6 +25,7 @@ LIBRARY = "libStdMeshersEngine.so" from salome.smesh.smesh_algorithm import Mesh_Algorithm import StdMeshers +from salome.geom import geomBuilder #---------------------------- # Mesh algo type identifiers @@ -2013,3 +2014,106 @@ class StdMeshersBuilder_UseExisting_2D(Mesh_Algorithm): pass pass # end of StdMeshersBuilder_UseExisting_2D class + +class StdMeshersBuilder_ViscousLayer(Mesh_Algorithm): + """ Defines the prismatic layer builder. + + It is created by calling smeshBuilder.Mesh.ViscousLayerBuilder(geom=TheGeometry) + """ + + meshMethod = "ViscousLayerBuilder" + """ + name of the dynamic method in smeshBuilder.Mesh class + """ + algoType = "ViscousLayerBuilder" + """ + type of algorithm used with helper function in smeshBuilder.Mesh class + """ + docHelper = "Viscous layer builder for 2D and 3D geometries" + """ + doc string of the method + """ + + # On create method it will call create method from mesh python class + # + def __init__(self, mesh, geom = 0 ): + """ + Private constructor. + + Parameters: + mesh: parent mesh object algorithm is assigned to + geom: geometry (shape/sub-shape) algorithm is assigned to; + if it is :code:`0` (default), the algorithm is assigned to the main shape + """ + self.thickness = None + self.numberOfLayers = None + self.stretchFactor = None + self.elementsId = [] + self.isElementToIgnore = True + self.extrMethod = StdMeshers.SURF_OFFSET_SMOOTH + self.groupName = "" + self.shrinkGeometry = None + self.algo = self.Create(mesh, geom, self.algoType) + pass + + def setBuilderParameters( self, thickness, numberOfLayers, stretchFactor, elementsId=[], + isElementToIgnore=True, extrMethod=StdMeshers.SURF_OFFSET_SMOOTH, groupName="" ): + self.thickness = thickness + self.numberOfLayers = numberOfLayers + self.stretchFactor = stretchFactor + self.elementsId = elementsId # can be faces or edges + self.isElementToIgnore = isElementToIgnore + self.extrMethod = extrMethod + self.groupName = groupName + + self.algo.SetTotalThickness( thickness ) + self.algo.SetNumberLayers( numberOfLayers ) + self.algo.SetStretchFactor( stretchFactor ) + + #Faces are set based on int ids so if a collection of face geom objects is recived cast it to int + if elementsId and isinstance( elementsId, geomBuilder.GEOM._objref_GEOM_Object ): + elementsId = [ elementsId ] + if elementsId and isinstance( elementsId[0], geomBuilder.GEOM._objref_GEOM_Object ): + elementsIDs = [] + for shape in elementsId: + try: + ff = self.mesh.geompyD.SubShapeAll( shape, self.mesh.geompyD.ShapeType["FACE"] ) + if ( len( ff ) == 0 ): + #try to get edges + ff = self.mesh.geompyD.SubShapeAll( shape, self.mesh.geompyD.ShapeType["EDGE"] ) + + for f in ff: + elementsIDs.append( self.mesh.geompyD.GetSubShapeID(self.mesh.geom, f)) + except: + # try to get the SHAPERSTUDY engine directly, because GetGen does not work because of + # simplification of access in geomBuilder: omniORB.registerObjref + from SHAPERSTUDY_utils import getEngine + gen = getEngine() + if gen: + aShapeOp = gen.GetIShapesOperations() + ff = aShapeOp.ExtractSubShapes( shape, self.mesh.geompyD.ShapeType["FACE"], False) + if (len(ff)==0): + #try to get edges + ff = aShapeOp.ExtractSubShapes( shape, self.mesh.geompyD.ShapeType["EDGE"], False) + for f in ff: + elementsIDs.append( aShapeOp.GetSubShapeIndex( self.mesh.geom, f )) + elementsId = elementsIDs + + self.algo.SetFaces( elementsId, isElementToIgnore ) + self.algo.SetGroupName( groupName ) + self.algo.SetMethod( extrMethod ) + + def GetShrinkGeometry( self ): + if isinstance(self.geom, geomBuilder.GEOM._objref_GEOM_Object): + self.shrinkGeometry = self.algo.GetShrinkGeometry( self.mesh.GetMesh(), self.geom ) + + return self.shrinkGeometry + + def AddLayers( self, shrinkMesh ): + success = self.algo.AddLayers( shrinkMesh.GetMesh(), self.mesh.GetMesh(), self.geom ) + if ( success ): + return self.mesh #Return the original mesh of the builder + else: + return shrinkMesh + + pass # end of StdMeshersBuilder_ViscousLayer class \ No newline at end of file diff --git a/src/StdMeshers/CMakeLists.txt b/src/StdMeshers/CMakeLists.txt index 447158627..349b74825 100644 --- a/src/StdMeshers/CMakeLists.txt +++ b/src/StdMeshers/CMakeLists.txt @@ -125,6 +125,7 @@ SET(StdMeshers_HEADERS StdMeshers_PolygonPerFace_2D.hxx StdMeshers_PolyhedronPerSolid_3D.hxx StdMeshers_BlockRenumber.hxx + StdMeshers_ViscousLayerBuilder.hxx ) # --- sources --- @@ -190,6 +191,7 @@ SET(StdMeshers_SOURCES StdMeshers_PolygonPerFace_2D.cxx StdMeshers_PolyhedronPerSolid_3D.cxx StdMeshers_BlockRenumber.cxx + StdMeshers_ViscousLayerBuilder.cxx ) # --- rules --- diff --git a/src/StdMeshers/StdMeshers_Cartesian_3D.cxx b/src/StdMeshers/StdMeshers_Cartesian_3D.cxx index 7fb50a747..1223736d0 100644 --- a/src/StdMeshers/StdMeshers_Cartesian_3D.cxx +++ b/src/StdMeshers/StdMeshers_Cartesian_3D.cxx @@ -182,6 +182,17 @@ bool StdMeshers_Cartesian_3D::CheckHypothesis (SMESH_Mesh& aMesh, namespace { + /*! + * \brief Temporary mesh to hold + */ + struct TmpMesh: public SMESH_Mesh + { + TmpMesh() { + _isShapeToMesh = (_id = 0); + _meshDS = new SMESHDS_Mesh( _id, true ); + } + }; + typedef int TGeomID; // IDs of sub-shapes typedef TopTools_ShapeMapHasher TShapeHasher; // non-oriented shape hasher typedef std::array< int, 3 > TIJK; @@ -6406,13 +6417,15 @@ bool StdMeshers_Cartesian_3D::Compute(SMESH_Mesh & theMesh, if ( offsetShape.IsNull() ) throw SALOME_Exception( error ); - SMESH_Mesh* offsetMesh = builder.MakeOffsetMesh(); + SMESH_Mesh* offsetMesh = new TmpMesh(); + offsetMesh->ShapeToMesh( offsetShape ); + offsetMesh->GetSubMesh( offsetShape )->DependsOn(); this->_isComputeOffset = true; if ( ! this->Compute( *offsetMesh, offsetShape )) return false; - return builder.MakeViscousLayers( theMesh, theShape ); + return builder.MakeViscousLayers( *offsetMesh, theMesh, theShape ); } // The algorithm generates the mesh in following steps: diff --git a/src/StdMeshers/StdMeshers_Cartesian_VL.cxx b/src/StdMeshers/StdMeshers_Cartesian_VL.cxx index ee1a4f0cc..784996922 100644 --- a/src/StdMeshers/StdMeshers_Cartesian_VL.cxx +++ b/src/StdMeshers/StdMeshers_Cartesian_VL.cxx @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -37,9 +38,12 @@ #include #include -#include #include +#include +#include +#include #include +#include #include #include #include @@ -138,6 +142,7 @@ namespace SMESHDS_SubMesh* sm = theOffsetMDS->MeshElements( theEOS._offsetShape ); if ( !sm || sm->NbElements() == 0 || sm->NbNodes() == 0 ) return; + theEOS._edges.resize( sm->NbNodes() ); const TopoDS_Face& initFace = TopoDS::Face( theEOS._initShape ); @@ -189,13 +194,17 @@ namespace void projectToEdge( VLEdgesOnShape & theEOS, SMESHDS_Mesh* theOffsetMDS, - TNode2VLEdge & theN2E ) + TNode2VLEdge & theN2E, + bool createVertex ) { SMESHDS_SubMesh* sm = theOffsetMDS->MeshElements( theEOS._offsetShape ); + if ( !sm || sm->NbElements() == 0 ) return; - theEOS._edges.resize( sm->NbNodes() ); + int addVertexNode = createVertex ? 1 : 0; + theEOS._edges.resize( sm->NbNodes() + addVertexNode ); // +1 to set the vertex + ShapeAnalysis_Curve projector; BRepAdaptor_Curve initCurve( TopoDS::Edge( theEOS._initShape )); const double tol = Precision::Confusion(); @@ -227,8 +236,39 @@ namespace theN2E.Bind( offP.Node(), &vlEdge ); } + + if ( createVertex ) + { + // It is possible to define the vertex projections from the existing edges + // EOS._offsetShape the edge generated from the original edge + // Get the first vertex of both edges to define the connecting edges + auto offsetEdge = TopoDS::Edge( theEOS._offsetShape ); + auto initEdge = TopoDS::Edge( theEOS._initShape ); + TopoDS_Vertex offsetVertex; + TopoDS_Vertex initVertex; + + if ( offsetEdge.Orientation() == TopAbs_FORWARD ) + { + offsetVertex = TopExp::FirstVertex ( offsetEdge ); + initVertex = TopExp::FirstVertex ( initEdge ); + } + else + { + offsetVertex = TopExp::LastVertex ( offsetEdge ); + initVertex = TopExp::LastVertex ( initEdge ); + } + + VLEdge & vlEdge = theEOS._edges[ iN ]; + vlEdge._nodes.resize( 2 ); + vlEdge._nodes[0] = SMESH_Algo::VertexNode( offsetVertex, theOffsetMDS ); + + gp_Pnt offP = BRep_Tool::Pnt( initVertex ); + + vlEdge._nodes[1] = theOffsetMDS->AddNode( offP.X(), offP.Y(), offP.Z() ); + theN2E.Bind( vlEdge._nodes[0].Node(), &vlEdge ); + } return; - } + } //================================================================================ /*! @@ -445,13 +485,12 @@ namespace prism2polyhedron( vNodes, volumElem ); if ( const SMDS_MeshElement* vol = editor.AddElement( vNodes, volumElem )) - vol->setIsMarked( true ); // to add to group + vol->setIsMarked( true ); // to add to group } } else // at inlet/outlet - { makePolyhedron( edgesVec, vNodes, editor, volumElem ); - } + editor.ClearLastCreated(); // move the face to the top of prisms, on mesh boundary @@ -467,6 +506,7 @@ namespace * \param [inout] theMesh - offset mesh to fill in * \param [inout] theN2E - map of node to VLEdge * \param [inout] theFaceID - ID of WOVL FACE for new faces to set on + * \param [in] isMainShape2D - used to identify the geometry where the new elements are included * \return bool - ok */ //================================================================================ @@ -474,7 +514,8 @@ namespace bool makeFaces( VLEdgesOnShape & theEOS, SMESH_Mesh* theMesh, TNode2VLEdge & theN2E, - const TGeomID theFaceID) + const TGeomID theFaceID, + bool isMainShape2D = false ) { SMESHDS_SubMesh* sm = theMesh->GetMeshDS()->MeshElements( theEOS._offsetShape ); if ( !sm || sm->NbElements() == 0 ) @@ -486,6 +527,27 @@ namespace std::vector< const SMDS_MeshNode* > fNodes( 4 ); std::vector foundVolum; std::vector< VLEdge*> edgesVec; + TIDSortedElemSet refSetFace; + + // Check orientation of face and re + gp_XYZ refNormalVector(0.0,0.0,0.0); + if ( isMainShape2D ) + { + SMESHDS_Mesh* offsetMDS = theMesh->GetMeshDS(); + for ( SMDS_ElemIteratorPtr eIt = offsetMDS->elementsIterator(); eIt->more(); ) + { + const SMDS_MeshElement* refFace = eIt->next(); // define the ref + if ( refFace->GetType() == SMDSAbs_Face ) + { + SMESH_MeshAlgos::FaceNormal( refFace, refNormalVector, /*normalized=*/true ); + break; + } + } + if ( refNormalVector.X() == 0.0 && refNormalVector.Y() == 0.0 && refNormalVector.Z() == 0.0 ) + throw SALOME_Exception("No 2D element found in the mesh!\n"); + + } + for ( SMDS_ElemIteratorPtr eIt = sm->GetElements(); eIt->more(); ) { const SMDS_MeshElement* edge = eIt->next(); @@ -503,6 +565,7 @@ namespace edgesVec[ i ] = theN2E( n ); } size_t nbFaces = edgesVec[0]->_nodes.size() - 1; + for ( size_t iF = 0; iF < nbFaces; ++iF ) { fNodes[ 0 ] = edgesVec[ 0 ]->_nodes[ iF ].Node(); @@ -519,13 +582,20 @@ namespace TIDSortedElemSet faces = { face }, volumes = { foundVolum[0] }; editor.Reorient2DBy3D( faces, volumes, /*outside=*/true ); } + else if ( isMainShape2D ) + { + gp_XYZ elementNormal; + SMESH_MeshAlgos::FaceNormal( face, elementNormal, /*normalized=*/true ); + if ( elementNormal * refNormalVector < 0.0 /* diff orientation from the ref element */) + editor.Reorient( face ); + } } } editor.ClearLastCreated(); // move the face to the top of prisms, on mesh boundary //theMesh->GetMeshDS()->ChangeElementNodes( face, fNodes.data(), nbNodes ); - } + } return true; } @@ -790,11 +860,16 @@ StdMeshers_Cartesian_VL::ViscousBuilder::ViscousBuilder( const StdMeshers_Viscou { const TopoDS_Shape& face = faces( i ); TGeomID fID = iniMDS->ShapeToIndex( face ); - if ( _shapesWVL.count( fID )) + bool isMainShape2D = (mainShape.ShapeType() == TopAbs_FACE) ? true : false; + + if ( _shapesWVL.count( fID ) && !isMainShape2D ) continue; + for ( TopExp_Explorer exp( face, TopAbs_EDGE ); exp.More(); exp.Next() ) _edge2facesWOVL[ iniMDS->ShapeToIndex( exp.Current() )].push_back( fID ); } + + // When 2D meshing Need to add edges where segments need to be added due to geometry shrink return; } @@ -806,7 +881,96 @@ StdMeshers_Cartesian_VL::ViscousBuilder::ViscousBuilder( const StdMeshers_Viscou StdMeshers_Cartesian_VL::ViscousBuilder::~ViscousBuilder() { - delete _offsetMesh; _offsetMesh = 0; + delete _offsetMesh; //_offsetMesh = 0; +} + +//================================================================================ +/*! + * \brief Create an offset solid from a given one + * \param [in] theShape - input shape can be a solid, solidcompound or a compound with solids + * \param [in] theMesh - main mesh + * \param [out] theError - error description + * \return TopoDS_Shape - result offset shape of the same type as the received shape + */ +//================================================================================ + +TopoDS_Shape StdMeshers_Cartesian_VL::ViscousBuilder::MakeOffsetSolid(const TopoDS_Shape & theShape, + SMESH_Mesh & theMesh, + std::string & theError ) +{ + double offset = -_hyp->GetTotalThickness(); + double tol = Precision::Confusion(); + TopAbs_ShapeEnum typeOfShape = theShape.ShapeType(); + + TopTools_IndexedMapOfShape shapeList; + TopExp::MapShapes( theShape, TopAbs_SOLID, shapeList ); + std::vector shrinkBodies; + + for ( int i = 1; i <= shapeList.Size(); ++i ) + { + auto solid = shapeList( i ); + // If Shape is solid call direct + BRepOffset_MakeOffset * makeOffset = new BRepOffset_MakeOffset(); + makeOffset->Initialize( solid, offset, tol, BRepOffset_Skin, /*Intersection=*/false, + /*selfInter=*/false, GeomAbs_Intersection); + + // exclude inlet FACEs + SMESHDS_Mesh* meshDS = theMesh.GetMeshDS(); + for ( TopExp_Explorer fEx( theShape, TopAbs_FACE ); fEx.More(); fEx.Next() ) + { + TGeomID fID = meshDS->ShapeToIndex( fEx.Current() ); + if ( !_shapesWVL.count( fID )) + makeOffset->SetOffsetOnFace( TopoDS::Face( fEx.Current()), 0 ); + } + + makeOffset->MakeOffsetShape(); + if ( makeOffset->IsDone() ) + { + shrinkBodies.push_back( makeOffset->Shape() ); + _makeOffsetCollection.push_back( makeOffset ); + } + else + { + switch ( makeOffset->Error() ) + { + case BRepOffset_NoError: + theError = "OK. Offset performed successfully.";break; + case BRepOffset_BadNormalsOnGeometry: + theError = "Degenerated normal on input data.";break; + case BRepOffset_C0Geometry: + theError = "C0 continuity of input data.";break; + case BRepOffset_NullOffset: + theError = "Null offset of all faces.";break; + case BRepOffset_NotConnectedShell: + theError = "Incorrect set of faces to remove, the remaining shell is not connected.";break; + case BRepOffset_CannotTrimEdges: + theError = "Can not trim edges.";break; + case BRepOffset_CannotFuseVertices: + theError = "Can not fuse vertices.";break; + case BRepOffset_CannotExtentEdge: + theError = "Can not extent edge.";break; + default: + theError = "operation not done."; + } + theError = "BRepOffset_MakeOffset error: " + theError; + + return TopoDS_Shape(); + } + } + + if ( typeOfShape == TopAbs_COMPOUND || typeOfShape == TopAbs_COMPSOLID ) + { + _solidCompound.SetGlue( BOPAlgo_GlueFull ); + _solidCompound.SetToFillHistory( true ); + for ( auto solid : shrinkBodies ) + _solidCompound.AddArgument( solid ); + + _solidCompound.Perform(); + return _solidCompound.Shape(); + } + else + return shrinkBodies[ 0 ]; // return one solid + } //================================================================================ @@ -824,91 +988,155 @@ StdMeshers_Cartesian_VL::ViscousBuilder::MakeOffsetShape(const TopoDS_Shape & th SMESH_Mesh & theMesh, std::string & theError ) { - double offset = -_hyp->GetTotalThickness(); - double tol = Precision::Confusion(); - _makeOffset.Initialize( theShape, offset, tol, BRepOffset_Skin, /*Intersection=*/false, - /*selfInter=*/false, GeomAbs_Intersection ); - // exclude inlet FACEs - SMESHDS_Mesh* meshDS = theMesh.GetMeshDS(); - for ( TopExp_Explorer fEx( theShape, TopAbs_FACE ); fEx.More(); fEx.Next() ) - { - TGeomID fID = meshDS->ShapeToIndex( fEx.Current() ); - if ( !_shapesWVL.count( fID )) - _makeOffset.SetOffsetOnFace( TopoDS::Face( fEx.Current()), 0 ); - } + double offset = -_hyp->GetTotalThickness(); + double tol = Precision::Confusion(); + TopAbs_ShapeEnum typeOfShape = theShape.ShapeType(); - _makeOffset.MakeOffsetShape(); - if ( _makeOffset.IsDone() ) + // Switch here for the treatment of faces + if ( typeOfShape == TopAbs_FACE ) { - _offsetShape = _makeOffset.Shape(); - SMESH_MesherHelper::WriteShape( _offsetShape );//// - - _offsetMesh->ShapeToMesh( _offsetShape ); - _offsetMesh->GetSubMesh( _offsetShape )->DependsOn(); + TopoDS_Face face = TopoDS::Face( theShape ); + GProp_GProps gprops; + BRepGProp::SurfaceProperties(face, gprops); // Stores results in gprops + double faceArea = gprops.Mass(); + + _makeFaceOffset = BRepOffsetAPI_MakeOffset( face, GeomAbs_Intersection ); + _makeFaceOffset.Perform( offset ); + TopoDS_Wire wireFrame = TopoDS::Wire( _makeFaceOffset.Shape() ); + TopoDS_Face shrinkFace = TopoDS::Face( BRepBuilderAPI_MakeFace( wireFrame, false ) ); + BRepGProp::SurfaceProperties(shrinkFace, gprops); // Stores results in gprops + double sArea = gprops.Mass(); + + if ( sArea > faceArea /*recompute the shrink face because offset was done in the contrary direction as expected*/) + { + _makeFaceOffset.Perform( -offset ); + wireFrame = TopoDS::Wire( _makeFaceOffset.Shape() ); + shrinkFace = TopoDS::Face( BRepBuilderAPI_MakeFace( wireFrame, false ) ); + } + _offsetShape = shrinkFace; return _offsetShape; } - - switch ( _makeOffset.Error() ) + else { - case BRepOffset_NoError: - theError = "OK. Offset performed successfully.";break; - case BRepOffset_BadNormalsOnGeometry: - theError = "Degenerated normal on input data.";break; - case BRepOffset_C0Geometry: - theError = "C0 continuity of input data.";break; - case BRepOffset_NullOffset: - theError = "Null offset of all faces.";break; - case BRepOffset_NotConnectedShell: - theError = "Incorrect set of faces to remove, the remaining shell is not connected.";break; - case BRepOffset_CannotTrimEdges: - theError = "Can not trim edges.";break; - case BRepOffset_CannotFuseVertices: - theError = "Can not fuse vertices.";break; - case BRepOffset_CannotExtentEdge: - theError = "Can not extent edge.";break; - default: - theError = "operation not done."; - } - theError = "BRepOffset_MakeOffset error: " + theError; - - return TopoDS_Shape(); + _offsetShape = MakeOffsetSolid( theShape, theMesh, theError ); + return _offsetShape; + } } //================================================================================ /*! - * \brief Return a sub-shape of the offset shape generated from a given initial sub-shape + * \brief Return the list of sub-shape of the same type of the offset shape generated from a given initial sub-shape */ //================================================================================ -TopoDS_Shape StdMeshers_Cartesian_VL::ViscousBuilder::getOffsetSubShape( const TopoDS_Shape& S ) +void StdMeshers_Cartesian_VL::ViscousBuilder::getOffsetSubShape( const TopoDS_Shape& S, std::vector& subShapeList ) +{ + for( auto offset : _makeOffsetCollection ) + { + const TopTools_ListOfShape& newShapes = offset->Generated( S ); + if ( newShapes.Size() == 0 ) + continue; // keep searching + + for ( const TopoDS_Shape& ns : newShapes ) + { + if ( ns.ShapeType() == S.ShapeType() ) + { + if ( _solidCompound.Arguments().Size() == 0 /* only one solid shrank*/ ) + { + subShapeList.push_back( ns ); + } + else + { + // In boolean operations the shapes are modified or deleted + const TopTools_ListOfShape& newGlueShapes = _solidCompound.Modified( ns ); + for ( TopoDS_Shape& ngs : newGlueShapes ) + if ( ngs.ShapeType() == ns.ShapeType() /*&& !ngs.Checked()*/ ) + subShapeList.push_back( ngs ); + + if ( newGlueShapes.Size() == 0 && !_solidCompound.IsDeleted( ns ) ) + subShapeList.push_back( ns ); + } + } + } + } + + // check for _makeFaceOffset in face shrink + if ( _makeOffsetCollection.size() == 0 ) + { + const TopTools_ListOfShape& newShapes = _makeFaceOffset.Generated( S ); + for ( const TopoDS_Shape& ns : newShapes ) + { + if ( ns.ShapeType() == S.ShapeType() ) + return subShapeList.push_back( ns ); + } + } +} + +bool StdMeshers_Cartesian_VL::ViscousBuilder::CheckGeometryMaps( SMESH_Mesh & offsetMesh, + const TopoDS_Shape & theShape ) { - const TopTools_ListOfShape& newShapes = _makeOffset.Generated( S ); - for ( const TopoDS_Shape& ns : newShapes ) - if ( ns.ShapeType() == S.ShapeType() ) - return ns; - return TopoDS_Shape(); + SMESHDS_Mesh* offsetMDS = offsetMesh.GetMeshDS(); + TopoDS_Shape shrinkGeomToMesh = offsetMDS->ShapeToMesh(); + + TopTools_IndexedMapOfShape shrinkGeomMap; + TopExp::MapShapes( shrinkGeomToMesh, shrinkGeomMap ); + TopTools_IndexedMapOfShape offsetGeomMap; + TopExp::MapShapes( _offsetShape, offsetGeomMap ); + + // loop on sub-shapes to project nodes from offset boundary to initial boundary + TopAbs_ShapeEnum types[3] = { TopAbs_VERTEX, TopAbs_EDGE, TopAbs_FACE }; + for ( TopAbs_ShapeEnum shType : types ) + { + TopTools_IndexedMapOfShape shapes; + TopExp::MapShapes( theShape, shType, shapes ); + for ( int i = 1; i <= shapes.Size(); ++i ) + { + // For each type of geometry check the existence of one or more equivalents + std::vector listOfShapes; + getOffsetSubShape( shapes(i), listOfShapes ); + if ( listOfShapes.size() == 0 ) return false; + } + } + return true; } //================================================================================ /*! * \brief Create prismatic mesh between _offsetShape and theShape + * \remark Build the viscous layer from the iteration of shrink geometry * \param [out] theMesh - mesh to fill in * \param [in] theShape - initial shape * \return bool - is Ok */ //================================================================================ -bool StdMeshers_Cartesian_VL::ViscousBuilder::MakeViscousLayers( SMESH_Mesh & theMesh, +bool StdMeshers_Cartesian_VL::ViscousBuilder::MakeViscousLayers( SMESH_Mesh & offsetMesh, + SMESH_Mesh & theMesh, const TopoDS_Shape & theShape ) { - SMESHDS_Mesh* offsetMDS = _offsetMesh->GetMeshDS(); - SMESHDS_Mesh* initMDS = theMesh.GetMeshDS(); + SMESHDS_Mesh* offsetMDS = offsetMesh.GetMeshDS(); + SMESHDS_Mesh* initMDS = theMesh.GetMeshDS(); + TopoDS_Shape shrinkGeomToMesh = offsetMDS->ShapeToMesh(); + bool isMainShape2D = (theShape.ShapeType() == TopAbs_FACE) ? true : false; + + // Validate map of shrink+joint geometry elements + if ( !CheckGeometryMaps(offsetMesh, theShape ) && !isMainShape2D ) + throw SALOME_Exception("All elements from the shrink geometry were not match to the original geometry\n"); + + + initMDS->ClearMesh(); // avoid mesh superposition on multiple calls of addLayers offsetMDS->SetAllCellsNotMarked(); + TopTools_IndexedMapOfShape shrinkGeomMap; + TopExp::MapShapes( shrinkGeomToMesh, shrinkGeomMap ); + + TopTools_IndexedMapOfShape offsetGeomMap; + TopExp::MapShapes( _offsetShape, offsetGeomMap ); + // Compute heights of viscous layers std::vector< double > vlH; computeVLHeight( _hyp, vlH ); - + std::vector< VLEdgesOnShape > edgesOnShape; edgesOnShape.reserve( offsetMDS->MaxShapeIndex() + 1 ); TNode2VLEdge n2e; @@ -923,55 +1151,64 @@ bool StdMeshers_Cartesian_VL::ViscousBuilder::MakeViscousLayers( SMESH_Mesh & { edgesOnShape.resize( edgesOnShape.size() + 1 ); VLEdgesOnShape& EOS = edgesOnShape.back(); - + std::vector listOfShapes; EOS._initShape = shapes( i ); - EOS._offsetShape = getOffsetSubShape( EOS._initShape ); - EOS._initShapeID = initMDS->ShapeToIndex( EOS._initShape ); - EOS._hasVL = _shapesWVL.count( EOS._initShapeID ); - EOS._toCheckCoinc = false; - if ( !EOS._hasVL ) - continue; - - // project boundary nodes of offset mesh to boundary of init mesh - // (new nodes are created in the offset mesh) - switch( EOS._offsetShape.ShapeType() ) { - case TopAbs_VERTEX: - { - EOS._edges.resize( 1 ); - EOS._edges[0]._nodes.resize( 2 ); - EOS._edges[0]._nodes[0] = SMESH_Algo::VertexNode( TopoDS::Vertex( EOS._offsetShape ), - offsetMDS ); - gp_Pnt offP = BRep_Tool::Pnt( TopoDS::Vertex( EOS._initShape )); - EOS._edges[0]._nodes[1] = offsetMDS->AddNode( offP.X(), offP.Y(), offP.Z() ); - //EOS._edges[0]._length = offP.Distance( EOS._edges[0]._nodes[0] ); - n2e.Bind( EOS._edges[0]._nodes[0].Node(), & EOS._edges[0] ); - break; - } - case TopAbs_EDGE: - { - projectToEdge( EOS, offsetMDS, n2e ); - break; - } - case TopAbs_FACE: + + // Get a list of subShapes of the same type generated from the same face + // It is the case with split objects. + getOffsetSubShape( EOS._initShape, listOfShapes ); + for ( TopoDS_Shape& shrinkShape : listOfShapes ) { - projectToFace( EOS, offsetMDS, n2e ); - break; - } - default:; - } + int shapeId = offsetGeomMap.FindIndex( shrinkShape ); + EOS._offsetShape = shrinkGeomMap.FindKey( shapeId ); + EOS._initShapeID = initMDS->ShapeToIndex( EOS._initShape ); + EOS._hasVL = _shapesWVL.count( EOS._initShapeID ); + + EOS._toCheckCoinc = false; + if ( !EOS._hasVL ) + continue; + + // project boundary nodes of offset mesh to boundary of init mesh + // (new nodes are created in the offset mesh) + switch( EOS._offsetShape.ShapeType() ) { + case TopAbs_VERTEX: + { + EOS._edges.resize( 1 ); + EOS._edges[0]._nodes.resize( 2 ); + EOS._edges[0]._nodes[0] = SMESH_Algo::VertexNode( TopoDS::Vertex( EOS._offsetShape ), + offsetMDS ); + gp_Pnt offP = BRep_Tool::Pnt( TopoDS::Vertex( EOS._initShape )); + EOS._edges[0]._nodes[1] = offsetMDS->AddNode( offP.X(), offP.Y(), offP.Z() ); + //EOS._edges[0]._length = offP.Distance( EOS._edges[0]._nodes[0] ); + n2e.Bind( EOS._edges[0]._nodes[0].Node(), & EOS._edges[0] ); + break; + } + case TopAbs_EDGE: + { + projectToEdge( EOS, offsetMDS, n2e, isMainShape2D /* add vertex from edges*/ ); + break; + } + case TopAbs_FACE: + { + projectToFace( EOS, offsetMDS, n2e ); + break; + } + default:; + } - // create nodes of layers - if ( _hyp->GetNumberLayers() > 1 ) - { - //if ( _shapesWVL.count( EOS._initShapeID )) - for ( size_t i = 0; i < EOS._edges.size(); ++i ) + // create nodes of layers + if ( _hyp->GetNumberLayers() > 1 ) { - divideVLEdge( &EOS._edges[ i ], vlH, offsetMDS ); + //if ( _shapesWVL.count( EOS._initShapeID )) + for ( size_t i = 0; i < EOS._edges.size(); ++i ) + { + divideVLEdge( &EOS._edges[ i ], vlH, offsetMDS ); + } } - } - } // loop on shapes + } // loop on generated shrink shape + }//loop on original shape } // loop on shape types - + // create prisms bool prismsOk = true; for ( size_t i = 0; i < edgesOnShape.size(); ++i ) @@ -979,10 +1216,11 @@ bool StdMeshers_Cartesian_VL::ViscousBuilder::MakeViscousLayers( SMESH_Mesh & VLEdgesOnShape& EOS = edgesOnShape[ i ]; if ( EOS._initShape.ShapeType() == TopAbs_FACE && EOS._hasVL ) { - if ( !makePrisms( EOS, _offsetMesh, n2e )) + if ( !makePrisms( EOS, &offsetMesh, n2e )) prismsOk = false; } } + if ( prismsOk ) { // create faces on FACEs WOVL @@ -994,22 +1232,40 @@ bool StdMeshers_Cartesian_VL::ViscousBuilder::MakeViscousLayers( SMESH_Mesh & auto e2f = _edge2facesWOVL.find( EOS._initShapeID ); if ( e2f != _edge2facesWOVL.end() && !e2f->second.empty() ) { - TopoDS_Shape f = initMDS->IndexToShape( e2f->second[0] ); - TopoDS_Shape f2 = getOffsetSubShape( f ); - //cout << e2f->second[0] << " OFF " << offsetMDS->ShapeToIndex( f2 ) << endl; - makeFaces( EOS, _offsetMesh, n2e, offsetMDS->ShapeToIndex( f2 ) ); + TopoDS_Shape f = initMDS->IndexToShape( e2f->second[0] ); + std::vector listOfShapes; + getOffsetSubShape( f, listOfShapes ); + for( TopoDS_Shape& subShape : listOfShapes ) + { + int shapeId = offsetGeomMap.FindIndex( subShape ); + TopoDS_Shape f2 = shrinkGeomMap.FindKey( shapeId ); + makeFaces( EOS, & offsetMesh, n2e, offsetMDS->ShapeToIndex( f2 ) ); + } } } } } - // copy offset mesh to the main one + if ( isMainShape2D ) + { + // create faces on FACEs of the inflate viscous layer in 2D faces + for ( size_t i = 0; i < edgesOnShape.size(); ++i ) + { + VLEdgesOnShape& EOS = edgesOnShape[ i ]; + if ( EOS._initShape.ShapeType() == TopAbs_EDGE && EOS._hasVL /* iterate in market edges with viscous layer*/) + { + int shapeId = offsetMDS->ShapeToIndex( shrinkGeomToMesh ); + makeFaces( EOS, & offsetMesh, n2e, shapeId, isMainShape2D ); // pass face Id of shrink geometry + } + } + } + + // copy offset mesh to the main one initMDS->Modified(); initMDS->CompactMesh(); smIdType nShift = initMDS->NbNodes(); TGeomID solidID = initMDS->ShapeToIndex( theShape ); - copyMesh( _offsetMesh, & theMesh, solidID ); - + copyMesh( & offsetMesh, & theMesh, solidID ); if ( !prismsOk ) { @@ -1028,9 +1284,9 @@ bool StdMeshers_Cartesian_VL::ViscousBuilder::MakeViscousLayers( SMESH_Mesh & { VLEdgesOnShape& EOS = edgesOnShape[ i ]; if ( EOS._hasVL ) - setBnd2Sub( EOS, &theMesh, _offsetMesh, n2e, nShift, nodesToCheckCoinc ); + setBnd2Sub( EOS, &theMesh, &offsetMesh, n2e, nShift, nodesToCheckCoinc ); else - setBnd2FVWL( EOS, &theMesh, _offsetMesh, nShift ); + setBnd2FVWL( EOS, &theMesh, &offsetMesh, nShift ); } // merge coincident nodes @@ -1052,5 +1308,6 @@ bool StdMeshers_Cartesian_VL::ViscousBuilder::MakeViscousLayers( SMESH_Mesh & } } + return prismsOk; -} +} \ No newline at end of file diff --git a/src/StdMeshers/StdMeshers_Cartesian_VL.hxx b/src/StdMeshers/StdMeshers_Cartesian_VL.hxx index f8f1c7dca..daec12361 100644 --- a/src/StdMeshers/StdMeshers_Cartesian_VL.hxx +++ b/src/StdMeshers/StdMeshers_Cartesian_VL.hxx @@ -26,11 +26,15 @@ #ifndef __StdMeshers_Cartesian_VL_HXX__ #define __StdMeshers_Cartesian_VL_HXX__ +#include #include +#include + #include #include #include +class TopoDS_Face; class StdMeshers_ViscousLayers; class SMESH_Mesh; @@ -47,23 +51,34 @@ namespace StdMeshers_Cartesian_VL TopoDS_Shape MakeOffsetShape(const TopoDS_Shape & theShape, SMESH_Mesh & theMesh, - std::string & theError ); + std::string & theError ); SMESH_Mesh* MakeOffsetMesh(); - bool MakeViscousLayers( SMESH_Mesh & theMesh, - const TopoDS_Shape & theShape ); - + bool MakeViscousLayers( SMESH_Mesh & offsetMesh, + SMESH_Mesh & theMesh, + const TopoDS_Shape & theShape ); + private: + + TopoDS_Shape MakeOffsetSolid(const TopoDS_Shape & theShape, + SMESH_Mesh & theMesh, + std::string & theError ); + + bool CheckGeometryMaps( SMESH_Mesh & offsetMesh, + const TopoDS_Shape & theShape ); - TopoDS_Shape getOffsetSubShape( const TopoDS_Shape& S ); + void getOffsetSubShape( const TopoDS_Shape& S, std::vector& listOfShapes ); const StdMeshers_ViscousLayers* _hyp; BRepOffset_MakeOffset _makeOffset; + std::vector _makeOffsetCollection; // collection to + BRepOffsetAPI_MakeOffset _makeFaceOffset; // to define shrink of planar faces. The face is shrink in all + BOPAlgo_Builder _solidCompound; // to glue solids with common faces after shrinking then with BRepOffset_MakeOffset SMESH_Mesh* _offsetMesh; TopoDS_Shape _offsetShape; - std::set< int > _shapesWVL; // shapes with viscous layers - std::map< int, std::vector< int > > _edge2facesWOVL; // EDGE 2 FACEs w/o VL + std::set< int > _shapesWVL; // shapes with viscous layers + std::map< int, std::vector< int > > _edge2facesWOVL; // EDGE 2 FACEs w/o VL }; } diff --git a/src/StdMeshers/StdMeshers_ViscousLayerBuilder.cxx b/src/StdMeshers/StdMeshers_ViscousLayerBuilder.cxx new file mode 100644 index 000000000..e6a6ff394 --- /dev/null +++ b/src/StdMeshers/StdMeshers_ViscousLayerBuilder.cxx @@ -0,0 +1,134 @@ +// Copyright (C) 2007-2023 CEA, EDF, OPEN CASCADE +// +// 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, or (at your option) any later version. +// +// 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 : StdMeshers_ViscousLayerBuilder.cxx +// Module : SMESH +// Author : Cesar Conopoima (cce) +// + +#include "SMESHDS_Mesh.hxx" +#include "SMESH_ControlsDef.hxx" +#include "SMESH_Gen.hxx" +#include "SMESH_Mesh.hxx" +#include "SMESH_MeshAlgos.hxx" +#include "SMESH_MeshEditor.hxx" +#include "SMESH_MesherHelper.hxx" +#include "StdMeshers_ViscousLayerBuilder.hxx" + +#include + +#include + +//======================================================================= +//function : StdMeshers_ViscousLayerBuilder +//purpose : Implements +//======================================================================= + +StdMeshers_ViscousLayerBuilder::StdMeshers_ViscousLayerBuilder(int hypId, + SMESH_Gen* gen) + : SMESH_2D_Algo(hypId, gen) +{ + _name = "StdMeshers_ViscousLayerBuilder"; + _hyp = new StdMeshers_ViscousLayers2D( hypId, gen ); +} + +bool StdMeshers_ViscousLayerBuilder::CheckHypothesis(SMESH_Mesh& aMesh, + const TopoDS_Shape& aShape, + SMESH_Hypothesis::Hypothesis_Status& aStatus) +{ + (void) aMesh; + (void) aShape; + (void) aStatus; + return true; +} + +bool StdMeshers_ViscousLayerBuilder::Compute(SMESH_Mesh& /*aMesh*/, const TopoDS_Shape& /*aShape*/) +{ + return true; +} + +bool StdMeshers_ViscousLayerBuilder::Evaluate(SMESH_Mesh & /*aMesh*/, const TopoDS_Shape & /*aShape*/, + MapShapeNbElems& /*aResMap*/ ) +{ + return true; +} + +bool StdMeshers_ViscousLayerBuilder::IsApplicable( const TopoDS_Shape &S, bool /*toCheckAll*/, int algoDim ) +{ + if ( S.ShapeType() > TopAbs_FACE ) + return false; + + return ( std::abs( algoDim ) == 3 || std::abs( algoDim ) == 2 ) ? true : false; +} + +void StdMeshers_ViscousLayerBuilder::SetBndShapes(const std::vector& faceIds, bool toIgnore) +{ + _hyp->SetBndShapes( faceIds, toIgnore ); +} // -------------------------------------------------------------------------------- +void StdMeshers_ViscousLayerBuilder::SetTotalThickness(double thickness) +{ + _hyp->SetTotalThickness( thickness ); +} // -------------------------------------------------------------------------------- +void StdMeshers_ViscousLayerBuilder::SetNumberLayers(int nb) +{ + _hyp->SetNumberLayers( nb ); +} // -------------------------------------------------------------------------------- +void StdMeshers_ViscousLayerBuilder::SetStretchFactor(double factor) +{ + _hyp->SetStretchFactor( factor ); +} // -------------------------------------------------------------------------------- +void StdMeshers_ViscousLayerBuilder::SetMethod( StdMeshers_ViscousLayers::ExtrusionMethod method ) +{ + _hyp->SetMethod( method ); +} // -------------------------------------------------------------------------------- +void StdMeshers_ViscousLayerBuilder::SetGroupName(const std::string& name) +{ + _hyp->SetGroupName( name ); +} + +//======================================================================= +//function : ~StdMeshers_ViscousLayerBuilder +//purpose : +//======================================================================= + +StdMeshers_ViscousLayerBuilder::~StdMeshers_ViscousLayerBuilder() +{ +} + +TopoDS_Shape StdMeshers_ViscousLayerBuilder::GetShrinkGeometry( SMESH_Mesh & theMesh, const TopoDS_Shape& theShape ) +{ + _vlBuilder = new StdMeshers_Cartesian_VL::ViscousBuilder( _hyp, theMesh, theShape ); + + std::string error = ""; + _offsetShape = _vlBuilder->MakeOffsetShape( theShape, theMesh, error ); + if ( error != "" ) + throw SALOME_Exception( error ); + + return _offsetShape; +} + +bool StdMeshers_ViscousLayerBuilder::AddLayers( SMESH_Mesh & shrinkMesh, SMESH_Mesh & theMesh, const TopoDS_Shape& theShape ) +{ + bool success = _vlBuilder->MakeViscousLayers( shrinkMesh, theMesh, theShape ); + + if ( !success ) + throw SALOME_Exception( "Error building viscous layer from shrink geometry." ); + + return success; +} diff --git a/src/StdMeshers/StdMeshers_ViscousLayerBuilder.hxx b/src/StdMeshers/StdMeshers_ViscousLayerBuilder.hxx new file mode 100644 index 000000000..2238a22c5 --- /dev/null +++ b/src/StdMeshers/StdMeshers_ViscousLayerBuilder.hxx @@ -0,0 +1,107 @@ +// Copyright (C) 2007-2023 CEA/DEN, EDF R&D, OPEN CASCADE +// +// 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, or (at your option) any later version. +// +// 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 : StdMeshers_ViscousLayersBuilder.hxx +// Module : SMESH +// +#ifndef _SMESH_ViscourLayerBuilder_HXX_ +#define _SMESH_ViscourLayerBuilder_HXX_ + +#include +#include + +#include "SMESH_StdMeshers.hxx" +#include "SMESH_Hypothesis.hxx" +#include "StdMeshers_Cartesian_VL.hxx" +#include "StdMeshers_ViscousLayers.hxx" +#include "StdMeshers_ViscousLayers2D.hxx" + + +class STDMESHERS_EXPORT StdMeshers_ViscousLayerBuilder: public SMESH_2D_Algo +{ + public: + StdMeshers_ViscousLayerBuilder(int hypId, SMESH_Gen* gen); + ~StdMeshers_ViscousLayerBuilder(); + + virtual bool CheckHypothesis(SMESH_Mesh& aMesh, + const TopoDS_Shape& aShape, + SMESH_Hypothesis::Hypothesis_Status& aStatus); + + virtual bool Compute(SMESH_Mesh& /*aMesh*/, const TopoDS_Shape& /*aShape*/ ); + + virtual bool Evaluate(SMESH_Mesh & /*aMesh*/, const TopoDS_Shape & /*aShape*/, + MapShapeNbElems& /*aResMap*/ ); + + /*! + * \brief Check if the algo is applicable to the geometry and dimension + */ + virtual bool IsApplicable( const TopoDS_Shape &S, bool /*toCheckAll*/, int algoDim ); + + void SetBndShapes(const std::vector& shapeIds, bool toIgnore); + // std::vector GetBndShapes() const { return _shapeIds; } + // bool IsToIgnoreShapes() const { return _isToIgnoreShapes; } + + void SetTotalThickness(double thickness); + // double GetTotalThickness() const { return _thickness; } + + void SetNumberLayers(int nb); + // int GetNumberLayers() const { return _nbLayers; } + + void SetStretchFactor(double factor); + // double GetStretchFactor() const { return _stretchFactor; } + + void SetMethod( StdMeshers_ViscousLayers::ExtrusionMethod how ); + // StdMeshers_ViscousLayers::ExtrusionMethod GetMethod() const { return _method; } + + // name of a group to create + void SetGroupName(const std::string& name); + // const std::string& GetGroupName() const { return _groupName; } + + /*! + * \brief Compute a shrink version of the geometry. + * Use the BRepOffset_MakeOffset to perfom the operations for Solids. + * Use BRepBuilderAPI_MakeFace to perform the operation for planar faces. + * \remark For possitive offsets, planar faces are shrink in all directions BRepBuilderAPI_MakeFace does not support coarse grained edge selection. + * \param theMesh - the built mesh + * \param theShape - the geometry to be shrink + * \retval TopoDS_Shape - a new shape of the shrink geometry + */ + TopoDS_Shape GetShrinkGeometry( SMESH_Mesh & theMesh, const TopoDS_Shape & theShape ); + + /*! + * \brief Build the elements of the viscous layer based on the shrinkMesh and copied to theMesh + * \param shrinkMesh - the mesh defined on the shrink geometry + * \param theMesh - the final mesh with the combination of the shrink mesh and the viscous layer + * \param theShape - the original geometry + * \retval bool - Ok if success in the operation + */ + bool AddLayers( SMESH_Mesh & shrinkMesh, SMESH_Mesh & theMesh, const TopoDS_Shape & theShape ); + + private: + + + StdMeshers_ViscousLayers2D* _hyp; + StdMeshers_Cartesian_VL::ViscousBuilder* _vlBuilder; + + BRepOffset_MakeOffset _makeOffset; + BRepOffset_Offset _makeFaceOffset; + TopoDS_Shape _offsetShape; +}; + +#endif diff --git a/src/StdMeshers_I/CMakeLists.txt b/src/StdMeshers_I/CMakeLists.txt index 40ec1b50d..286f6f4d1 100644 --- a/src/StdMeshers_I/CMakeLists.txt +++ b/src/StdMeshers_I/CMakeLists.txt @@ -115,6 +115,7 @@ SET(StdMeshersEngine_HEADERS StdMeshers_PolygonPerFace_2D_i.hxx StdMeshers_PolyhedronPerSolid_3D_i.hxx StdMeshers_BlockRenumber_i.hxx + StdMeshers_ViscousLayerBuilder_i.hxx ) # --- sources --- @@ -169,6 +170,7 @@ SET(StdMeshersEngine_SOURCES StdMeshers_PolygonPerFace_2D_i.cxx StdMeshers_PolyhedronPerSolid_3D_i.cxx StdMeshers_BlockRenumber_i.cxx + StdMeshers_ViscousLayerBuilder_i.cxx ) # --- rules --- diff --git a/src/StdMeshers_I/StdMeshers_ViscousLayerBuilder_i.cxx b/src/StdMeshers_I/StdMeshers_ViscousLayerBuilder_i.cxx new file mode 100644 index 000000000..3fc156116 --- /dev/null +++ b/src/StdMeshers_I/StdMeshers_ViscousLayerBuilder_i.cxx @@ -0,0 +1,205 @@ +// Copyright (C) 2007-2023 CEA/DEN, EDF R&D, OPEN CASCADE +// +// 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, or (at your option) any later version. +// +// 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 +// + +// SMESH SMESH_I : idl implementation based on 'SMESH' unit's classes +// File : StdMeshers_ViscousLayers_i.cxx +// Module : SMESH +// +#include "StdMeshers_ViscousLayerBuilder_i.hxx" + +#include "SMESH_Gen.hxx" +#include "SMESH_Gen_i.hxx" +#include "SMESH_Group.hxx" +#include "SMESH_Group_i.hxx" +#include "SMESH_PythonDump.hxx" + +#include "BRepTools.hxx" +#include "Utils_CorbaException.hxx" +#include "utilities.h" + +#include + +#include CORBA_SERVER_HEADER(SMESH_Group) + +using namespace std; + +//============================================================================= +/*! + * StdMeshers_ViscousLayerBuilder_i::StdMeshers_ViscousLayerBuilder_i + * + * Constructor + */ +//============================================================================= + +StdMeshers_ViscousLayerBuilder_i::StdMeshers_ViscousLayerBuilder_i( PortableServer::POA_ptr thePOA, + ::SMESH_Gen* theGenImpl ) + : SALOME::GenericObj_i( thePOA ), + SMESH_Hypothesis_i( thePOA ), + SMESH_Algo_i( thePOA ), + SMESH_2D_Algo_i( thePOA ) +{ + myBaseImpl = new ::StdMeshers_ViscousLayerBuilder( theGenImpl->GetANewId(), + theGenImpl ); +} + +::StdMeshers_ViscousLayerBuilder* StdMeshers_ViscousLayerBuilder_i::GetImpl() +{ + return ( ::StdMeshers_ViscousLayerBuilder* )myBaseImpl; +} + +//================================================================================ +/*! + * \brief Verify whether hypothesis supports given entity type + * \param type - dimension (see SMESH::Dimension enumeration) + * \retval CORBA::Boolean - TRUE if dimension is supported, FALSE otherwise + * + * Verify whether hypothesis supports given entity type (see SMESH::Dimension enumeration) + */ +//================================================================================ +CORBA::Boolean StdMeshers_ViscousLayerBuilder_i::IsDimSupported( SMESH::Dimension type ) +{ + return type == SMESH::DIM_3D || type == SMESH::DIM_2D; +} + +void StdMeshers_ViscousLayerBuilder_i::SetFaces(const ::SMESH::long_array& faceIDs, + CORBA::Boolean toIgnore) +{ + vector ids( faceIDs.length() ); + for ( unsigned i = 0; i < ids.size(); ++i ) + if (( ids[i] = faceIDs[i] ) < 1 ) + THROW_SALOME_CORBA_EXCEPTION( "Invalid face id", SALOME::BAD_PARAM ); + + GetImpl()->SetBndShapes( ids, toIgnore ); + + SMESH::TPythonDump() << _this() << ".SetFaces( " << faceIDs << ", " << toIgnore << " )"; +} + + +void StdMeshers_ViscousLayerBuilder_i::SetIgnoreFaces(const ::SMESH::long_array& faceIDs) +{ + vector ids( faceIDs.length() ); + for ( unsigned i = 0; i < ids.size(); ++i ) + if (( ids[i] = faceIDs[i] ) < 1 ) + THROW_SALOME_CORBA_EXCEPTION( "Invalid face id", SALOME::BAD_PARAM ); + GetImpl()->SetBndShapes( ids, /*toIgnore=*/true ); + SMESH::TPythonDump() << _this() << ".SetIgnoreFaces( " << faceIDs << " )"; +} + +void StdMeshers_ViscousLayerBuilder_i::SetTotalThickness(::CORBA::Double thickness) +{ + if ( thickness < 1e-100 ) + THROW_SALOME_CORBA_EXCEPTION( "Invalid thickness", SALOME::BAD_PARAM ); + GetImpl()->SetTotalThickness(thickness); + SMESH::TPythonDump() << _this() << ".SetTotalThickness( " << SMESH::TVar(thickness) << " )"; +} + +void StdMeshers_ViscousLayerBuilder_i::SetNumberLayers(::CORBA::Short nb) +{ + if ( nb < 1 ) + THROW_SALOME_CORBA_EXCEPTION( "Invalid number of layers", SALOME::BAD_PARAM ); + GetImpl()->SetNumberLayers( nb ); + SMESH::TPythonDump() << _this() << ".SetNumberLayers( " << SMESH::TVar(nb) << " )"; +} + +void StdMeshers_ViscousLayerBuilder_i::SetStretchFactor(::CORBA::Double factor) +{ + if ( factor < 1 ) + THROW_SALOME_CORBA_EXCEPTION( "Invalid stretch factor, it must be >= 1.0", SALOME::BAD_PARAM ); + GetImpl()->SetStretchFactor(factor); + SMESH::TPythonDump() << _this() << ".SetStretchFactor( " << SMESH::TVar(factor) << " )"; +} + +void StdMeshers_ViscousLayerBuilder_i::SetMethod( ::StdMeshers::VLExtrusionMethod how ) +{ + GetImpl()->SetMethod( ::StdMeshers_ViscousLayers::ExtrusionMethod( how )); + const char* methNames[3] = { "SURF_OFFSET_SMOOTH", + "FACE_OFFSET", + "NODE_OFFSET" }; + if ( how >= 0 && how < 3 ) + SMESH::TPythonDump() << _this() << ".SetMethod( StdMeshers." << methNames[ how ]<< " )"; +} + +void StdMeshers_ViscousLayerBuilder_i::SetGroupName(const char* name) +{ + GetImpl()->SetGroupName( name ); + SMESH::TPythonDump() << _this() << ".SetGroupName( '" << name << "' )"; +} + +GEOM::GEOM_Object_ptr StdMeshers_ViscousLayerBuilder_i::GetShrinkGeometry( SMESH::SMESH_Mesh_ptr finalMesh, GEOM::GEOM_Object_ptr theShapeObject ) +{ + + GEOM::GEOM_Object_var aShapeObj; + TopoDS_Shape theShape = StdMeshers_ObjRefUlils::GeomObjectToShape( theShapeObject ); + SMESH_Mesh_i* theFinalMesh_i = SMESH::DownCast< SMESH_Mesh_i* >( finalMesh ); + TopoDS_Shape shrinkGeometry; + + try + { + shrinkGeometry = GetImpl()->GetShrinkGeometry( theFinalMesh_i->GetImpl(), theShape ); + } + catch ( std::exception& exc ) + { + std::cout << exc.what() << "\n"; + THROW_SALOME_CORBA_EXCEPTION( exc.what(), SALOME::INTERNAL_ERROR ); + return aShapeObj; // Maybe better to return a init and empty object(?) + } + + if ( !shrinkGeometry.IsNull() ) + { + std::ostringstream streamShape; + //Write TopoDS_Shape in ASCII format to the stream + BRepTools::Write(shrinkGeometry, streamShape); + //Returns the number of bytes that have been stored in the stream's buffer. + int size = streamShape.str().size(); + //Allocate octect buffer of required size + CORBA::Octet* OctetBuf = SALOMEDS::TMPFile::allocbuf(size); + //Copy ostrstream content to the octect buffer + memcpy(OctetBuf, streamShape.str().c_str(), size); + //Create and return TMPFile + SALOMEDS::TMPFile_var SeqFile = new SALOMEDS::TMPFile(size,size,OctetBuf,1); + // Get the geom engine + GEOM::GEOM_Gen_var geomEngine = theShapeObject->GetGen(); + auto iOp = geomEngine->GetIInsertOperations(); + aShapeObj = iOp->RestoreShape( SeqFile ); + geomEngine->AddInStudy( aShapeObj, "Shrink", GEOM::GEOM_Object::_nil()); + } + + return aShapeObj; +} + +CORBA::Boolean StdMeshers_ViscousLayerBuilder_i::AddLayers( SMESH::SMESH_Mesh_ptr shrinkMesh, SMESH::SMESH_Mesh_ptr finalMesh, GEOM::GEOM_Object_ptr theShapeObject ) +{ + TopoDS_Shape theShape = StdMeshers_ObjRefUlils::GeomObjectToShape( theShapeObject ); + SMESH_Mesh_i* shrinkMesh_i = SMESH::DownCast< SMESH_Mesh_i* >( shrinkMesh ); + SMESH_Mesh_i* theFinalMesh_i = SMESH::DownCast< SMESH_Mesh_i* >( finalMesh ); + + bool success = GetImpl()->AddLayers( shrinkMesh_i->GetImpl(), theFinalMesh_i->GetImpl(), theShape ); + + return success; +} +//============================================================================= +/*! + * StdMeshers_ViscousLayerBuilder_i::~StdMeshers_ViscousLayerBuilder_i + * + * Destructor + */ +//============================================================================= +StdMeshers_ViscousLayerBuilder_i::~StdMeshers_ViscousLayerBuilder_i() +{ +} \ No newline at end of file diff --git a/src/StdMeshers_I/StdMeshers_ViscousLayerBuilder_i.hxx b/src/StdMeshers_I/StdMeshers_ViscousLayerBuilder_i.hxx new file mode 100644 index 000000000..b6957f170 --- /dev/null +++ b/src/StdMeshers_I/StdMeshers_ViscousLayerBuilder_i.hxx @@ -0,0 +1,73 @@ +// Copyright (C) 2007-2023 CEA/DEN, EDF R&D, OPEN CASCADE +// +// 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, or (at your option) any later version. +// +// 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 : StdMeshers_ViscousLayerBuilder_i.hxx +// Module : SMESH +// +#ifndef _SMESH_ViscousLayersBuilder_I_HXX_ +#define _SMESH_ViscousLayersBuilder_I_HXX_ + +#include "SMESH_StdMeshers_I.hxx" + +#include +#include CORBA_SERVER_HEADER(SMESH_BasicHypothesis) + +#include "SMESH_2D_Algo_i.hxx" +#include "SMESH_Hypothesis_i.hxx" +#include "SMESH_Mesh_i.hxx" +#include "StdMeshers_ViscousLayerBuilder.hxx" +#include "StdMeshers_ObjRefUlils.hxx" + +class SMESH_Gen; + +class STDMESHERS_I_EXPORT StdMeshers_ViscousLayerBuilder_i: + public virtual POA_StdMeshers::StdMeshers_ViscousLayerBuilder, + public virtual SMESH_2D_Algo_i +{ + public: + // Constructor + StdMeshers_ViscousLayerBuilder_i( PortableServer::POA_ptr thePOA, + ::SMESH_Gen* theGenImpl ); + // Destructor + virtual ~StdMeshers_ViscousLayerBuilder_i(); + + // Verify whether algorithm supports given entity type + CORBA::Boolean IsDimSupported( SMESH::Dimension type ); + + // Get implementation + ::StdMeshers_ViscousLayerBuilder* GetImpl(); + + void SetIgnoreFaces(const ::SMESH::long_array& faceIDs); + void SetFaces(const SMESH::long_array& faceIDs, + CORBA::Boolean toIgnore); + void SetTotalThickness(::CORBA::Double thickness); + void SetNumberLayers(::CORBA::Short nb); + void SetStretchFactor(::CORBA::Double factor); + void SetMethod( ::StdMeshers::VLExtrusionMethod how ); + void SetGroupName(const char* name); + + // Compute and return the shrink geometry + GEOM::GEOM_Object_ptr GetShrinkGeometry( SMESH::SMESH_Mesh_ptr finalMesh, GEOM::GEOM_Object_ptr theShapeObject ); + + // Build the viscous layer on the specified faces/edges available in the sourceMesh and consolidate the result in the finalMesh + CORBA::Boolean AddLayers( SMESH::SMESH_Mesh_ptr sourceMesh, SMESH::SMESH_Mesh_ptr finalMesh, GEOM::GEOM_Object_ptr theShapeObject ); + +}; + +#endif diff --git a/src/StdMeshers_I/StdMeshers_i.cxx b/src/StdMeshers_I/StdMeshers_i.cxx index 51fdef1a6..b22321f16 100644 --- a/src/StdMeshers_I/StdMeshers_i.cxx +++ b/src/StdMeshers_I/StdMeshers_i.cxx @@ -77,6 +77,7 @@ #include "StdMeshers_UseExisting_1D2D_i.hxx" #include "StdMeshers_ViscousLayers2D_i.hxx" #include "StdMeshers_ViscousLayers_i.hxx" +#include "StdMeshers_ViscousLayerBuilder_i.hxx" namespace SMESH { class ApplicableToAny @@ -206,7 +207,7 @@ STDMESHERS_I_EXPORT else if (strcmp(aHypName, "CartesianParameters3D") == 0) aCreator = new StdHypothesisCreator_i; else if (strcmp(aHypName, "BlockRenumber") == 0) - aCreator = new StdHypothesisCreator_i; + aCreator = new StdHypothesisCreator_i; // Algorithms else if (strcmp(aHypName, "Regular_1D") == 0) @@ -248,7 +249,9 @@ STDMESHERS_I_EXPORT else if (strcmp(aHypName, "PolygonPerFace_2D") == 0) aCreator = new StdHypothesisCreator_i; else if (strcmp(aHypName, "PolyhedronPerSolid_3D") == 0) - aCreator = new StdHypothesisCreator_i; + aCreator = new StdHypothesisCreator_i; + else if (strcmp(aHypName, "ViscousLayerBuilder") == 0) + aCreator = new StdHypothesisCreator_i; return aCreator; } diff --git a/test/test_vlapi_growthlayer.py b/test/test_vlapi_growthlayer.py new file mode 100644 index 000000000..a82afed7d --- /dev/null +++ b/test/test_vlapi_growthlayer.py @@ -0,0 +1,145 @@ +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2023 CEA, EDF, OPEN CASCADE +# +# 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, or (at your option) any later version. +# +# 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 +# + +import math +import salome +salome.salome_init_without_session() + +import GEOM +import SHAPERSTUDY +from salome.geom import geomBuilder +from salome.smesh import smeshBuilder +from salome.shaper import model + +def assertAlmostEqual(a,b,tol): + if ( abs(a-b) < tol ): + return True + else: + print( "not close vals", a, b ) + return False + + +geompy = geomBuilder.New() + +O = geompy.MakeVertex(0, 0, 0) +OX = geompy.MakeVectorDXDYDZ(1, 0, 0) +OY = geompy.MakeVectorDXDYDZ(0, 1, 0) +OZ = geompy.MakeVectorDXDYDZ(0, 0, 1) + +# create a disk +geompy.addToStudy( O, 'O' ) +geompy.addToStudy( OX, 'OX' ) +geompy.addToStudy( OY, 'OY' ) +geompy.addToStudy( OZ, 'OZ' ) +Box = geompy.MakeBox(0,0,0,10,10,10) + +smesh_builder = smeshBuilder.New() + +MesherBox = smesh_builder.Mesh(Box, "Box") +viscousBuilder = MesherBox.ViscousLayerBuilder() + +#############BOX AND TETRA ELEMENTS +#Set prismatic layer parameters +offset = 0.7 +numLayers = 4 +viscousBuilder.setBuilderParameters( offset, numLayers, 1.2, [13], False ) +ShrinkBox = viscousBuilder.GetShrinkGeometry() + +#Mesh the shrink box +MesherShinkBox = smesh_builder.Mesh(ShrinkBox, "ShrinkMesh") +ShrinkBoxMesh = MesherShinkBox.Tetrahedron(smeshBuilder.NETGEN_1D2D3D) + +#Compute +success = MesherShinkBox.Compute() +assert( success ) +assert( MesherShinkBox.NbVolumes() == 5 ) # if Fails! change the default value of volumes when meshing with Netgen! + +FinalMesh = viscousBuilder.AddLayers( MesherShinkBox ) +assert( FinalMesh.NbVolumes() == 5 + numLayers * 2 ) # here 2 stands for the number of face elements per face in the box +assert( FinalMesh.NbFaces() == 6 * 2 + 4 * numLayers ) # here is the number of face elements for the box + the new faces in the VL. (6 is the number of sides in the box) + +#Testing the configuration where face 13 is ignored and so the offset is applied to all other faces +viscousBuilder.setBuilderParameters( offset, numLayers, 1.2, [13], True ) +ShrinkBox2 = viscousBuilder.GetShrinkGeometry() +#Mesh the shrink box +MesherShinkBox2 = smesh_builder.Mesh(ShrinkBox2, "ShrinkMesh2") +ShrinkBoxMesh2 = MesherShinkBox2.Tetrahedron(smeshBuilder.NETGEN_1D2D3D) + +#Compute +success = MesherShinkBox2.Compute() +assert( success ) + +#Test the number of elements on the shrink mesh +assert( MesherShinkBox2.NbVolumes() == 5 ) # if Fails! change the default (default hypo) number of volumes when meshing with Netgen! + +FinalMesh2 = viscousBuilder.AddLayers( MesherShinkBox2 ) + +assert( FinalMesh2.NbVolumes() == 5 + numLayers * 2 * 5 ) # here 2 stands for the number of face elements per face in the box +assert( FinalMesh2.NbFaces() == 6 * 2 + 4 * numLayers ) # here is the number of face elements for the box + the new faces in the VL. (6 is the number of sides in the box) +#############END BOX AND TETRA ELEMENTS + +#############MESH SQUARE FACE +Face = geompy.MakeFaceHW(5, 5, 1) +Disk = geompy.MakeDiskR(5, 1) + +MesherSqr = smesh_builder.Mesh(Face, "Face") +viscousBuilder = MesherSqr.ViscousLayerBuilder() +#Set prismatic layer parameters +offset = 0.5 +numberOfLayers = 6 +viscousBuilder.setBuilderParameters( offset, numberOfLayers, 1.2 ) +ShrinkFace = viscousBuilder.GetShrinkGeometry() +#Mesh the shrink face +MesherShinkFace = smesh_builder.Mesh(ShrinkFace, "ShrinkFaceMesh") +algo = MesherShinkFace.Segment() +numOfSegments = 4 +algo.NumberOfSegments(numOfSegments) +ShrinkFaceMesh = MesherShinkFace.Triangle() +#Compute +success = MesherShinkFace.Compute() +assert( success ) +numFaceElementShrinkGeom = MesherShinkFace.NbFaces() +FinalFaceMesh = viscousBuilder.AddLayers( MesherShinkFace ) +# Check the number of additional elements +# numOfSegments * 4 * numberOfLayers +finalNumOfElements = FinalFaceMesh.NbFaces() +assert( numFaceElementShrinkGeom + 4 * numOfSegments * numberOfLayers == finalNumOfElements ) + +#############END MESH SQUARE FACE + +#############MESH CIRCULAR FACE +MesherCircle = smesh_builder.Mesh(Disk, "Disk") +viscousBuilder = MesherCircle.ViscousLayerBuilder() +viscousBuilder.setBuilderParameters( offset, numberOfLayers, 1.2 ) +ShrinkCircle = viscousBuilder.GetShrinkGeometry() +MesherShinkCircle = smesh_builder.Mesh(ShrinkCircle, "ShrinkCircleMesh") +algo = MesherShinkCircle.Segment() +numOfSegments = 12 +algo.NumberOfSegments(numOfSegments) +ShrinkCircleMesh = MesherShinkCircle.Triangle() + +#Compute +success = MesherShinkCircle.Compute() +numFaceElementShrinkGeom = MesherShinkCircle.NbFaces() +assert( success ) +FinalCircleMesh = viscousBuilder.AddLayers( MesherShinkCircle ) +finalNumOfElements = FinalCircleMesh.NbFaces() +assert( numFaceElementShrinkGeom + numOfSegments * numberOfLayers == finalNumOfElements ) +#############END MESH CIRCULAR FACE \ No newline at end of file diff --git a/test/test_vlapi_shrinkgeometry.py b/test/test_vlapi_shrinkgeometry.py new file mode 100644 index 000000000..3dccd0225 --- /dev/null +++ b/test/test_vlapi_shrinkgeometry.py @@ -0,0 +1,209 @@ +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2023 CEA, EDF, OPEN CASCADE +# +# 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, or (at your option) any later version. +# +# 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 +# + +import math +import salome +salome.salome_init_without_session() + +import GEOM +import SHAPERSTUDY +from salome.geom import geomBuilder +from salome.smesh import smeshBuilder +from salome.shaper import model + +def assertAlmostEqual(a,b,tol): + if ( abs(a-b) < tol ): + return True + else: + print( "not close vals", a, b ) + return False + + +geompy = geomBuilder.New() + +O = geompy.MakeVertex(0, 0, 0) +OX = geompy.MakeVectorDXDYDZ(1, 0, 0) +OY = geompy.MakeVectorDXDYDZ(0, 1, 0) +OZ = geompy.MakeVectorDXDYDZ(0, 0, 1) + +# create a disk +geompy.addToStudy( O, 'O' ) +geompy.addToStudy( OX, 'OX' ) +geompy.addToStudy( OY, 'OY' ) +geompy.addToStudy( OZ, 'OZ' ) +Box = geompy.MakeBox(0,0,0,10,10,10) + +smesh_builder = smeshBuilder.New() + +MesherBox = smesh_builder.Mesh(Box, "Box") +viscousBuilder = MesherBox.ViscousLayerBuilder() + +#Set prismatic layer parameters +offset = 0.1 + +####SHRINK THE BOX IN ALL DIRECTIONS +#No list of faces is passed and the isToIgnore flag true by default so the offset if applied to the entire geometry +viscousBuilder.setBuilderParameters( offset, 4, 1.2 ) +ShrinkBox = viscousBuilder.GetShrinkGeometry() + +BoxProperties = geompy.BasicProperties(Box) +ShrinkBoxProperties = geompy.BasicProperties(ShrinkBox) +assert( BoxProperties[2] > ShrinkBoxProperties[2] ) + +assert( assertAlmostEqual( BoxProperties[2], (10.0)**(3.0), 1e-12 ) ) +#The geometry is shrank in all directions +assert( assertAlmostEqual( ShrinkBoxProperties[2], (10.0-offset*2)**(3), 1e-12 ) ) +####END SHRINK THE BOX IN ALL DIRECTIONS + +####SHRINK THE BOX EXCEPT FOR ONE FACE +viscousBuilder = MesherBox.ViscousLayerBuilder() +selectableFaces = geompy.SubShapeAllSortedCentresIDs(Box, geompy.ShapeType["FACE"]) +# Set face 1 TO BE ignored +viscousBuilder.setBuilderParameters( offset, 4, 1.2, [ selectableFaces[ 0 ] ], True ) # Shrink in all faces except face id +ShrinkBox = viscousBuilder.GetShrinkGeometry() +ShrinkBoxProperties = geompy.BasicProperties(ShrinkBox) +selectableShrinkFaces = geompy.SubShapeAllSortedCentresIDs(ShrinkBox, geompy.ShapeType["FACE"]) +assert( assertAlmostEqual( ShrinkBoxProperties[2], (10.0-offset*2)**(2)*(10.0-offset), 1e-12 ) ) +####END SHRINK THE BOX EXCEPT FOR ONE FACE + +####SHRINK THE BOX IN DIRECTION OF ONLY ONE FACE +# Set face 1 TO NOT be ignored +viscousBuilder.setBuilderParameters( offset, 4, 1.2, [ selectableFaces[ 0 ] ], False ) # Shrink only the faceid +ShrinkBox = viscousBuilder.GetShrinkGeometry() +ShrinkBoxProperties = geompy.BasicProperties(ShrinkBox) +assert( assertAlmostEqual( ShrinkBoxProperties[2], (10.0)**(2)*(10.0-offset), 1e-12 ) ) +selectableShrinkFaces = geompy.SubShapeAllSortedCentresIDs(ShrinkBox, geompy.ShapeType["FACE"]) +####END SHRINK THE BOX IN DIRECTION OF ONLY ONE FACE + +####DO NOT SHRINK THE BOX +viscousBuilder.setBuilderParameters( offset, 4, 1.2, isElementToIgnore = False ) +ShrinkBox = viscousBuilder.GetShrinkGeometry() +BoxProperties = geompy.BasicProperties(Box) +ShrinkBoxProperties = geompy.BasicProperties(ShrinkBox) +assert( assertAlmostEqual( BoxProperties[2], ShrinkBoxProperties[2], 1e-12) ) +####END DO NOT SHRINK THE BOX + +####SHRINK THE ENTIRE SPHERE +#Test shrinking sphere +Radius = 10.0 +Sphere = geompy.MakeSphere(0,0,0,Radius) +MesherSphere = smesh_builder.Mesh(Sphere, "Sphere") +viscousBuilder = MesherSphere.ViscousLayerBuilder() +viscousBuilder.setBuilderParameters( offset, 4, 1.2 ) +ShrinkSphere = viscousBuilder.GetShrinkGeometry() +ShrinkSphereProperties = geompy.BasicProperties(ShrinkSphere) +assert( ShrinkSphereProperties[2] < 4.0/3.0*math.pi * Radius**3 ) +assert( assertAlmostEqual( ShrinkSphereProperties[2], 4.0/3.0*math.pi*(10.0-offset)**(3), 1e-12 ) ) +####END SHRINK THE ENTIRE SPHERE + +####SHRINK THE ENTIRE CYLINDER +#Test shrinking cylinder +Cylinder = geompy.MakeCylinderRH(10,30) +MesherCylinder = smesh_builder.Mesh(Cylinder, "Cylinder") +viscousBuilder = MesherCylinder.ViscousLayerBuilder() +viscousBuilder.setBuilderParameters( offset, 4, 1.2 ) +ShrinkCylinder = viscousBuilder.GetShrinkGeometry() +CylinderProp = geompy.BasicProperties(Cylinder) +ShirnkCylinderProp = geompy.BasicProperties(ShrinkCylinder) + +assert( CylinderProp[2] > ShirnkCylinderProp[2] ) +####END SHRINK THE ENTIRE CYLINDER + +####SHRINK THE ENTIRE TUBE +#Test shrinking tube +Circle_1 = geompy.MakeCircle(None, None, 20) +Circle_2 = geompy.MakeCircle(None, None, 10) +Face_1 = geompy.MakeFaceWires([Circle_1, Circle_2], 1) +Tube = geompy.MakePrismDXDYDZ(Face_1, 0, 0, 100) + +MesherTube = smesh_builder.Mesh(Tube, "Tube") +viscousBuilder = MesherTube.ViscousLayerBuilder() +viscousBuilder.setBuilderParameters( offset, 4, 1.2 ) +ShrinkTube = viscousBuilder.GetShrinkGeometry() +TubeProp = geompy.BasicProperties(Tube) +ShirnkTubeProp = geompy.BasicProperties(ShrinkTube) +assert( TubeProp[2] > ShirnkTubeProp[2] ) +####END SHRINK THE ENTIRE TUBE + +####SHRINK COMPOUND OBJECT TO GENERATE COMPOUND WITH COMMON FACE +X = geompy.MakeVectorDXDYDZ( 1,0,0 ) +O = geompy.MakeVertex( 100,50,50 ) +plane = geompy.MakePlane( O, X, 200 ) # plane YZ +lX = 200 +lYlZ = 100 +box = geompy.MakeBoxDXDYDZ(lX,lYlZ,lYlZ) +sBox = geompy.MakeHalfPartition( box, plane ) + +# Generate a uniquebody whit coincident faces +# 4 left, 34 middle, 50 right +ignoreFaces = [4,34,50] +geompy.addToStudy( sBox, "SisterBox" ) +MesherSBox = smesh_builder.Mesh( sBox, "SisterBoxMesh") +ViscousBuilder = MesherSBox.ViscousLayerBuilder() +thickness = 20 +numberOfLayers = 10 +stretchFactor = 1.5 +ViscousBuilder.setBuilderParameters( thickness, numberOfLayers, stretchFactor, ignoreFaces ) +ShrinkSBox = ViscousBuilder.GetShrinkGeometry() +SBoxProp = geompy.BasicProperties(sBox) +ShirnksBoxProp = geompy.BasicProperties(ShrinkSBox) +assert( assertAlmostEqual(ShirnksBoxProp[2], lX * (lYlZ - 2.0*thickness)**(2.0), 1e-12 ) ) +####END SHRINK COMPOUND OBJECT TO GENERATE COMPUND WITH COMMON FACE + + +####SHRINK COMPOUND OBJECT TO GENERATE TWO DISJOINT SOLIDS +ignoreFaces = [4,50] +ViscousBuilder.setBuilderParameters( thickness, numberOfLayers, stretchFactor, ignoreFaces ) +ShrinkSBox2 = ViscousBuilder.GetShrinkGeometry() +SBoxProp = geompy.BasicProperties(sBox) +ShirnksBoxProp2 = geompy.BasicProperties(ShrinkSBox2) +assert( assertAlmostEqual(ShirnksBoxProp2[2], (lX -2.0*thickness) * (lYlZ - 2.0*thickness)**(2.0), 1e-12 ) ) +####END SHRINK COMPOUND OBJECT TO GENERATE TWO DISJOINT SOLIDS + + +######SHRINK SQUARE +offset = 0.5 +numberOfLayers = 6 +Face = geompy.MakeFaceHW(5, 5, 1) +MesherSqr = smesh_builder.Mesh(Face, "Face") +viscousBuilder = MesherSqr.ViscousLayerBuilder() +viscousBuilder.setBuilderParameters( offset, numberOfLayers, 1.2 ) +ShrinkFace = viscousBuilder.GetShrinkGeometry() + +FaceProperties = geompy.BasicProperties(Face) +ShrinkFaceProperties = geompy.BasicProperties(ShrinkFace) +#Test smaller face +assert( ShrinkFaceProperties[1] < FaceProperties[1] ) +assertAlmostEqual( ShrinkFaceProperties[1], (5.0-offset*2.0)**(2.0), 1e-12 ) +######END SHRINK SQUARE + +######SHRINK CIRCLE +Disk = geompy.MakeDiskR(5, 1) + +#Test with circle +MesherCircle = smesh_builder.Mesh(Disk, "Disk") +viscousBuilder = MesherCircle.ViscousLayerBuilder() +viscousBuilder.setBuilderParameters( offset, numberOfLayers, 1.2 ) +ShrinkCircle = viscousBuilder.GetShrinkGeometry() +FaceProperties = geompy.BasicProperties(Disk) +ShrinkFaceProperties = geompy.BasicProperties(ShrinkCircle) + +assert( ShrinkFaceProperties[1] < FaceProperties[1] ) +######END SHRINK CIRCLE diff --git a/test/tests.set b/test/tests.set index 0248899d0..ff3dede4c 100644 --- a/test/tests.set +++ b/test/tests.set @@ -37,6 +37,7 @@ SET(BAD_TESTS ex29_refine.py ex_MakePolyLine.py test_smeshplugins.py + test_vlapi_growthlayer.py PAL_MESH_041_mesh.py PAL_MESH_043_3D.py SMESH_BelongToGeom.py @@ -77,6 +78,7 @@ SET(GOOD_TESTS create_penta_biquad.py extrusion_penta_biquad.py test_polyhedron_per_solid.py + test_vlapi_shrinkgeometry.py ex01_cube2build.py ex02_cube2primitive.py