X-Git-Url: http://git.salome-platform.org/gitweb/?p=modules%2Fsmesh.git;a=blobdiff_plain;f=src%2FSMESH_SWIG%2FsmeshBuilder.py;h=2c7211828d54774216d8c8a6fbdb613a0d35b386;hp=8d0a81ac5693dab47b5ca5898b616a07c1cb8df8;hb=HEAD;hpb=d328a1d7b8ebcd3d3f02ea9996d56d50016be3a4 diff --git a/src/SMESH_SWIG/smeshBuilder.py b/src/SMESH_SWIG/smeshBuilder.py index 8d0a81ac5..ce1bfdc13 100644 --- a/src/SMESH_SWIG/smeshBuilder.py +++ b/src/SMESH_SWIG/smeshBuilder.py @@ -1,4 +1,4 @@ -# Copyright (C) 2007-2022 CEA/DEN, EDF R&D, OPEN CASCADE +# Copyright (C) 2007-2024 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 @@ -741,6 +741,19 @@ class smeshBuilder( SMESH._objref_SMESH_Gen, object ): if error.comment: print("*** CreateMeshesFromGMF() errors:\n", error.comment) return Mesh(self, self.geompyD, aSmeshMesh), error + def CreateMeshesFromMESHIO(self, theFileName): + """ + Create a Mesh object(s) importing data from from any file supported by meshio library. + + Returns: + a tuple ( list of class :class:`Mesh` instances, + :class:`SMESH.DriverMED_ReadStatus` ) + """ + + aSmeshMeshes, aStatus = SMESH._objref_SMESH_Gen.CreateMeshesFromMESHIO(self, theFileName) + aMeshes = [ Mesh(self, self.geompyD, m) for m in aSmeshMeshes ] + return aMeshes, aStatus + def Concatenate( self, meshes, uniteIdenticalGroups, mergeNodesAndElements = False, mergeTolerance = 1e-5, allGroups = False, name = "", meshToAppendTo = None): @@ -1203,6 +1216,8 @@ class smeshBuilder( SMESH._objref_SMESH_Gen, object ): functor = aFilterMgr.CreateAspectRatio3D() elif theCriterion == FT_Warping: functor = aFilterMgr.CreateWarping() + elif theCriterion == FT_Warping3D: + functor = aFilterMgr.CreateWarping3D() elif theCriterion == FT_MinimumAngle: functor = aFilterMgr.CreateMinimumAngle() elif theCriterion == FT_Taper: @@ -1233,6 +1248,8 @@ class smeshBuilder( SMESH._objref_SMESH_Gen, object ): functor = aFilterMgr.CreateNodeConnectivityNumber() elif theCriterion == FT_BallDiameter: functor = aFilterMgr.CreateBallDiameter() + elif theCriterion == FT_ScaledJacobian: + functor = aFilterMgr.CreateScaledJacobian() else: print("Error: given parameter is not numerical functor type.") aFilterMgr.UnRegister() @@ -1627,7 +1644,9 @@ class Mesh(metaclass = MeshMeta): geo_name = "%s_%s to mesh"%(self.geom.GetShapeType(), id(self.geom)%100) geompyD.addToStudy( self.geom, geo_name ) if parallel and isinstance(self, ParallelMesh): - self.SetMesh( self.smeshpyD.CreateParallelMesh(self.geom) ) + mymesh = self.smeshpyD.CreateParallelMesh(self.geom) + mymesh2 = mymesh._narrow(SMESH._objref_SMESH_Mesh) + self.SetMesh( mymesh ) else: self.SetMesh( self.smeshpyD.CreateMesh(self.geom) ) @@ -1995,6 +2014,13 @@ class Mesh(metaclass = MeshMeta): return ok + def CheckCompute(self): + """ + Check if the mesh was properly compute + """ + if not self.mesh.IsComputedOK(): + raise Exception("Could not compute {}".format(self.GetName())) + def GetComputeErrors(self, shape=0 ): """ Return a list of error messages (:class:`SMESH.ComputeError`) of the last :meth:`Compute` @@ -2583,6 +2609,47 @@ class Mesh(metaclass = MeshMeta): meshPart = self.mesh self.mesh.ExportGMF(meshPart, f, True) + def ExportMESHIO( + self, + fileName, + selectedFilter, + meshPart + ): + """ + Exports a part of mesh to a file with meshio library + through an intermediate MED file. + + Parametrs described below are the same as for ExportMED() method. + However, we know that _pyMesh::Process(const Handle(_pyCommand)& theCommand) method + change a position of meshPart argument when dump to a Python script for whatever reason + this way: + - to 5th place for ExportMED command + - to last place for the rest commands + + So, for this method meshPart moved to the end of the args. + Look at src/SMESH_I/SMESH_2smeshpy.cxx for related source code. + + The same note is for the name of the method that was dumped as ExportPartToMESHIO(), + but then processing just removes PartTo from the middle of the name as it was done + for all export methods. + + Parameters: + fileName: is the file name + selectedFilter: filter string selected by user in a file dialog + meshPart: a part of mesh (:class:`sub-mesh, group or filter `) + to export instead of the mesh + """ + + if isinstance(meshPart, Mesh): + meshPart = meshPart.GetMesh() + + self.mesh.ExportPartToMESHIO( + meshPart, + fileName, + selectedFilter + ) + + def ExportToMED(self, *args, **kwargs): """ Deprecated, used only for compatibility! Please, use :meth:`ExportMED` method instead. @@ -5463,6 +5530,32 @@ class Mesh(metaclass = MeshMeta): if mesh: mesh = self.smeshpyD.Mesh(mesh) return mesh, group + def MakeBoundaryOfEachElement(self, groupName="", meshName="", toCopyAll=False, groups=[] ): + """ + Create boundary elements around the whole mesh or groups of elements + + Parameters: + groupName: a name of group to store all boundary elements in, + "" means not to create the group + meshName: a name of a new mesh, which is a copy of the initial + mesh + created boundary elements; "" means not to create the new mesh + toCopyAll: if True, the whole initial mesh will be copied into + the new mesh else only boundary elements will be copied into the new mesh + groups: list of :class:`sub-meshes, groups or filters ` of elements to make boundary around + + Returns: + tuple( long, mesh, group ) + - long - number of added boundary elements + - mesh - the :class:`Mesh` where elements were added to + - group - the :class:`group ` of boundary elements or None + """ + dimension=SMESH.BND_2DFROM3D + toCreateAllElements = True # create all boundary elements in the mesh + nb, mesh, group = self.editor.MakeBoundaryElements( dimension,groupName,meshName, + toCopyAll,toCreateAllElements,groups) + if mesh: mesh = self.smeshpyD.Mesh(mesh) + return nb, mesh, group + def MakeBoundaryElements(self, dimension=SMESH.BND_2DFROM3D, groupName="", meshName="", toCopyAll=False, groups=[]): """ @@ -5486,9 +5579,9 @@ class Mesh(metaclass = MeshMeta): - mesh - the :class:`Mesh` where elements were added to - group - the :class:`group ` of boundary elements or None """ - + toCreateAllElements = False # create only elements in the boundary of the solid nb, mesh, group = self.editor.MakeBoundaryElements(dimension,groupName,meshName, - toCopyAll,groups) + toCopyAll,toCreateAllElements,groups) if mesh: mesh = self.smeshpyD.Mesh(mesh) return nb, mesh, group @@ -7430,6 +7523,19 @@ class Mesh(metaclass = MeshMeta): return self.FunctorValue(SMESH.FT_Warping, elemId) + def GetWarping3D(self, elemId): + """ + Get warping angle of faces element of 3D elements. + + Parameters: + elemId: mesh element ID + + Returns: + element's warping angle value + """ + + return self.FunctorValue(SMESH.FT_Warping3D, elemId) + def GetMinimumAngle(self, elemId): """ Get minimum angle of 2D element. @@ -7469,6 +7575,19 @@ class Mesh(metaclass = MeshMeta): return self.FunctorValue(SMESH.FT_Skew, elemId) + def GetScaledJacobian(self, elemId): + """ + Get the scaled jacobian of 3D element id + + Parameters: + elemId: mesh element ID + + Returns: + the scaled jacobian + """ + + return self.FunctorValue(SMESH.FT_ScaledJacobian, elemId) + def GetMinMax(self, funType, meshPart=None): """ Return minimal and maximal value of a given functor. @@ -7503,14 +7622,44 @@ class Mesh(metaclass = MeshMeta): pass # end of Mesh class +def _copy_gmsh_param(dim, local_param, global_param): + if dim==3: + local_param.SetMaxSize(global_param.GetMaxSize()) + local_param.SetMinSize(global_param.GetMinSize()) + local_param.Set3DAlgo(global_param.Get3DAlgo()) + local_param.SetRecombineAll(global_param.GetRecombineAll()) + local_param.SetSubdivAlgo(global_param.GetSubdivAlgo()) + local_param.SetRemeshAlgo(global_param.GetRemeshAlgo()) + local_param.SetRemeshPara(global_param.GetRemeshPara()) + local_param.SetSmouthSteps(global_param.GetSmouthSteps()) + local_param.SetSizeFactor(global_param.GetSizeFactor()) + local_param.SetUseIncomplElem(global_param.GetUseIncomplElem()) + local_param.SetMeshCurvatureSize(global_param.GetMeshCurvatureSize()) + local_param.SetSecondOrder(global_param.GetSecondOrder()) + local_param.SetIs2d(global_param.GetIs2d()) + elif dim==2: + local_param.SetMaxSize(global_param.GetMaxSize()) + local_param.SetMinSize(global_param.GetMinSize()) + local_param.Set2DAlgo(global_param.Get2DAlgo()) + local_param.SetRecomb2DAlgo(global_param.GetRecomb2DAlgo()) + local_param.SetRecombineAll(global_param.GetRecombineAll()) + local_param.SetSubdivAlgo(global_param.GetSubdivAlgo()) + local_param.SetRemeshAlgo(global_param.GetRemeshAlgo()) + local_param.SetRemeshPara(global_param.GetRemeshPara()) + local_param.SetSmouthSteps(global_param.GetSmouthSteps()) + local_param.SetSizeFactor(global_param.GetSizeFactor()) + local_param.SetUseIncomplElem(global_param.GetUseIncomplElem()) + local_param.SetMeshCurvatureSize(global_param.GetMeshCurvatureSize()) + local_param.SetSecondOrder(global_param.GetSecondOrder()) + local_param.SetIs2d(global_param.GetIs2d()) def _copy_netgen_param(dim, local_param, global_param): """ Create 1D/2D/3D netgen parameters from a NETGEN 1D2D3D parameter """ if dim==1: - #TODO: Try to identify why we need to substract 1 - local_param.NumberOfSegments(int(global_param.GetNbSegPerEdge())-1) + #TODO: More global conversion ? or let user define it + local_param.NumberOfSegments(int(global_param.GetMaxSize())) elif dim==2: local_param.SetMaxSize(global_param.GetMaxSize()) local_param.SetMinSize(global_param.GetMinSize()) @@ -7518,6 +7667,7 @@ def _copy_netgen_param(dim, local_param, global_param): local_param.SetFineness(global_param.GetFineness()) local_param.SetNbSegPerEdge(global_param.GetNbSegPerEdge()) local_param.SetNbSegPerRadius(global_param.GetNbSegPerRadius()) + #TODO: Why the 0.9 to have same results local_param.SetGrowthRate(global_param.GetGrowthRate()*0.9) local_param.SetChordalError(global_param.GetChordalError()) local_param.SetChordalErrorEnabled(global_param.GetChordalErrorEnabled()) @@ -7539,6 +7689,31 @@ def _copy_netgen_param(dim, local_param, global_param): local_param.SetGrowthRate(global_param.GetGrowthRate()) local_param.SetNbThreads(global_param.GetNbThreads()) + +def _shaperstudy2geom(geompyD, shaper_obj): + """ + Convertion of shaper object to geom object + + Parameters: + geompyD: geomBuilder instance + shaper_obj: Shaper study object + + Returns: + geom object + + """ + import tempfile + #Writing shaperstudy object into a brep file + fid, tmp_file = tempfile.mkstemp(suffix='.brep') + with open(fid, 'wb') as f: + f.write(shaper_obj.GetShapeStream()) + # Reimporting brep file into geom + real_geom = geompyD.ImportBREP(tmp_file) + os.remove(tmp_file) + + return real_geom + + def _split_geom(geompyD, geom): """ Splitting geometry into n solids and a 2D/1D compound @@ -7547,7 +7722,11 @@ def _split_geom(geompyD, geom): geompyD: geomBuilder instance geom: geometrical object for meshing + Returns: + compound containing all the 1D,2D elements + list of solids """ + # Splitting geometry into 3D elements and all the 2D/1D into one compound object_solids = geompyD.ExtractShapes(geom, geompyD.ShapeType["SOLID"], True) @@ -7564,25 +7743,18 @@ def _split_geom(geompyD, geom): faces = [] iface = 0 - for isolid, solid in enumerate(solids): - solid_faces = geompyD.ExtractShapes(solid, geompyD.ShapeType["FACE"], - True) - for face in solid_faces: - faces.append(face) - iface += 1 - geompyD.addToStudyInFather(solid, face, - 'Face_{}'.format(iface)) - - # Creating submesh for edges 1D/2D part + solid_faces = geompyD.ExtractShapes(geom, geompyD.ShapeType["FACE"], + True) + for face in solid_faces: + faces.append(face) + iface += 1 + geompyD.addToStudyInFather(geom, face, + 'Face_{}'.format(iface)) - all_faces = geompyD.MakeCompound(faces) - geompyD.addToStudy(all_faces, 'Compound_1') - all_faces = geompyD.MakeGlueEdges(all_faces, 1e-07) - all_faces = geompyD.MakeGlueFaces(all_faces, 1e-07) - geompyD.addToStudy(all_faces, 'global2D') + return faces, solids - return all_faces, solids +MULTITHREAD, MULTINODE = range(2) class ParallelismSettings: """ Defines the parameters for the parallelism of ParallelMesh @@ -7599,21 +7771,109 @@ class ParallelismSettings: self._mesh = mesh + +class MTParallelismSettings(ParallelismSettings): + """ + Defines the parameters for the parallelism of ParallelMesh using MultiThreading + """ + def __init__(self, mesh): + ParallelismSettings.__init__(self, mesh) + + # Multithreading methods def SetNbThreads(self, nbThreads): - """ - Set the number of threads for multithreading - """ + """ Set the number of threads for multithread """ if nbThreads < 1: raise ValueError("Number of threads must be stricly greater than 1") self._mesh.mesh.SetNbThreads(nbThreads) def GetNbThreads(self): - """ - Get Number of threads - """ + """ Get Number of threads """ return self._mesh.mesh.GetNbThreads() + def __str__(self): + """ str conversion """ + string = "\nParameter for MultiThreading parallelism:\n" + string += "NbThreads: {}\n".format(self.GetNbThreads()) + + return string + + +class MNParallelismSettings(ParallelismSettings): + """ + Defines the parameters for the parallelism of ParallelMesh using MultiNodal + """ + def __init__(self, mesh): + ParallelismSettings.__init__(self, mesh) + + def GetResource(self): + """ Get the resource on which to run """ + return self._mesh.mesh.GetResource() + + def SetResource(self, resource): + """ Set the resource on which to run """ + self._mesh.mesh.SetResource(resource) + + def SetNbProc(self, nbProc): + """ Set the number of Processor for multinode """ + if nbProc < 1: + raise ValueError("Number of Proc must be stricly greater than 1") + self._mesh.mesh.SetNbProc(nbProc) + + def GetNbProc(self): + """ Get Number of Processor """ + return self._mesh.mesh.GetNbProc() + + def SetNbProcPerNode(self, nbProcPerNode): + """ Set the number of Processor Per Node for multinode """ + if nbProcPerNode < 1: + raise ValueError("Number of Processor Per Node must be stricly greater than 1") + + self._mesh.mesh.SetNbProcPerNode(nbProcPerNode) + + def GetNbProcPerNode(self): + """ Get Number of Processor Per Node """ + return self._mesh.mesh.GetNbProcPerNode() + + def SetNbNode(self, nbNode): + """ Set the number of Node for multinode """ + if nbNode < 1: + raise ValueError("Number of Node must be stricly greater than 1") + self._mesh.mesh.SetNbNode(nbNode) + + def GetNbNode(self): + """ Get Number of Node """ + return self._mesh.mesh.GetNbNode() + + def SetWcKey(self, wcKey): + """ Set the number of Node for multinode """ + self._mesh.mesh.SetWcKey(wcKey) + + def GetWcKey(self): + """ Get Number of Node """ + return self._mesh.mesh.GetWcKey() + + def SetWalltime(self, walltime): + """ Set the number of Node for multinode """ + self._mesh.mesh.SetWalltime(walltime) + + def GetWalltime(self): + """ Get Number of Node """ + return self._mesh.mesh.GetWalltime() + + def __str__(self): + """ str conversion """ + string = "\nParameter for MultiNode parallelism:\n" + string += "Reource: {}\n".format(self.GetResource()) + string += "NbProc: {}\n".format(self.GetNbProc()) + string += "NbProcPerNode: {}\n".format(self.GetNbProcPerNode()) + string += "NbNode: {}\n".format(self.GetNbNode()) + string += "WcKey: {}\n".format(self.GetWcKey()) + string += "Walltime: {}\n".format(self.GetWalltime()) + + return string + + class ParallelMesh(Mesh): """ Surcharge on Mesh for parallel computation of a mesh @@ -7637,33 +7897,114 @@ class ParallelMesh(Mesh): if not isinstance(geom, geomBuilder.GEOM._objref_GEOM_Object): raise ValueError("geom argument must be a geometry") + try: + import SHAPERSTUDY + shaper_object = SHAPERSTUDY.SHAPERSTUDY_ORB._objref_SHAPER_Object + has_shaper = True + except ImportError: + shaper_object = int + has_shaper = False + + # If we have a shaper object converting it into geom (temporary solution) + if isinstance(geom, shaper_object): + self._geom_obj = _shaperstudy2geom(geompyD, geom) + elif isinstance(geom, geomBuilder.GEOM._objref_GEOM_Object): + self._geom_obj = geom + else: + msg= "" + if not has_shaper: + msg = "\nShaper was not compiled" + raise Exception("Could not handle geom format {}.{} ".format(type(geom), msg)) + # Splitting geometry into one geom containing 1D and 2D elements and a # list of 3D elements - super(ParallelMesh, self).__init__(smeshpyD, geompyD, geom, name, parallel=True) + super(ParallelMesh, self).__init__(smeshpyD, geompyD, self._geom_obj, name, parallel=True) if split_geom: - self._all_faces, self._solids = _split_geom(geompyD, geom) + self._faces, self._solids = _split_geom(geompyD, self._geom_obj) - self.UseExistingSegments() - self.UseExistingFaces() + self._param = None - self._algo2d = self.Triangle(geom=self._all_faces, algo="NETGEN_2D") - self._algo3d = [] + def _build_submeshes(self, mesher2D, mesher3D): + """ + Contruct the submeshes for a parallel use of smesh + + Parameters: + mesher2D: name of 2D mesher for 2D parallel compute (NETGEN) + mesher3D: name of 3D mesher for 3D parallel compute (NETGEN or + GMSH) + """ + + # Building global 2D mesher + if mesher3D: + if mesher3D == "NETGEN": + algo2D = "NETGEN_2D" + elif mesher3D == "GMSH": + algo2D = "GMSH_2D" + else: + raise ValueError("mesher3D should be either NETGEN or GMSH") + + self._algo2d = self.Triangle(geom=self._geom_obj, algo=algo2D) + + # Parallel 2D + if mesher2D: + #Means that we want to mesh face of solids in parallel and not + #the volume + self._algo2d = [] + #For the moment use AutomaticLength based on finesse + # TODO: replace by input hypothesis + self._algo1d = self.Segment(geom=self._geom_obj) + + for face_id, face in enumerate(self._faces): + name = "face_{}".format(face_id) + algo2d = self.Triangle(geom=face, algo="NETGEN_2D_Remote") + self._algo2d.append(algo2d) + if mesher3D: + self._algo3d = [] for solid_id, solid in enumerate(self._solids): name = "Solid_{}".format(solid_id) - self.UseExistingSegments(geom=solid) - self.UseExistingFaces(geom=solid) - algo3d = self.Tetrahedron(geom=solid, algo="NETGEN_3D_Remote") - self._algo3d.append(algo3d) + if ( mesher3D == "NETGEN" ): + algo3d = self.Tetrahedron(geom=solid, algo="NETGEN_3D_Remote") + self._algo3d.append(algo3d) + elif ( mesher3D == "GMSH" ): + algo3d = self.Tetrahedron(geom=solid, algo="GMSH_3D_Remote") + self._algo3d.append(algo3d) - self._param = ParallelismSettings(self) + def GetNbSolids(self): + """ + Return the number of 3D solids + """ + return len(self._solids) + def GetNbFaces(self): + """ + Return the number of 2D faces + """ + return len(self._faces) + + def GetParallelismMethod(self): + """ Get the parallelims method """ + return self.mesh.GetParallelismMethod() + + def SetParallelismMethod(self, method): + """ Set the parallelims method """ + if method not in [MULTITHREAD , MULTINODE]: + raise ValueError("Parallelism method can only be 0:MultiThread or 1:MultiNode") + + self.mesh.SetParallelismMethod(method) + + if method == MULTITHREAD: + self._param = MTParallelismSettings(self) + else: + self._param = MNParallelismSettings(self) def GetParallelismSettings(self): """ Return class to set parameters for the parallelism """ + if self._param is None: + raise Exception("You need to set Parallelism method first (SetParallelismMethod)") return self._param def AddGlobalHypothesis(self, hyp): @@ -7676,20 +8017,52 @@ class ParallelMesh(Mesh): hyp: a hypothesis to assign """ - if not isinstance(hyp, NETGENPlugin._objref_NETGENPlugin_Hypothesis): - raise ValueError("param must come from NETGENPlugin") + if isinstance(hyp, NETGENPlugin._objref_NETGENPlugin_Hypothesis): + copy_param = _copy_netgen_param + mesher3D = "NETGEN" + elif isinstance(hyp, GMSHPlugin._objref_GMSHPlugin_Hypothesis): + copy_param = _copy_gmsh_param + mesher3D = "GMSH" + else: + raise ValueError("param must come from NETGENPlugin or GMSHPlugin") + + self.mesh.SetParallelismDimension(3) + self._build_submeshes(None, mesher3D) param2d = self._algo2d.Parameters() - _copy_netgen_param(2, param2d, hyp) + copy_param(2, param2d, hyp) for algo3d in self._algo3d: - param3d = algo3d.Parameters() - _copy_netgen_param(3, param3d, hyp) + copy_param(3, param3d, hyp) + + def Add2DGlobalHypothesis(self, hyp): + """ + Split hypothesis to apply it to all the submeshes: + - the 1D + - each of the 2D faces + Parameters: + hyp: a hypothesis to assign - pass # End of ParallelMesh + """ + if isinstance(hyp, NETGENPlugin._objref_NETGENPlugin_Hypothesis): + copy_param = _copy_netgen_param + mesher2D = "NETGEN" + else: + raise ValueError("param must come from NETGENPlugin") + + self.mesh.SetParallelismDimension(2) + self._build_submeshes(mesher2D, None) + param1d = self._algo1d + copy_param(1, param1d, hyp) + + for algo2d in self._algo2d: + param2d = algo2d.Parameters() + copy_param(2, param2d, hyp) + + pass # End of ParallelMesh class meshProxy(SMESH._objref_SMESH_Mesh): """ @@ -7733,10 +8106,20 @@ class meshProxy(SMESH._objref_SMESH_Mesh): if len( args ) == 1: args += True, return SMESH._objref_SMESH_Mesh.ExportDAT(self, *args) - pass + omniORB.registerObjref(SMESH._objref_SMESH_Mesh._NP_RepositoryId, meshProxy) +class parallelMeshProxy(SMESH._objref_SMESH_ParallelMesh): + def __init__(self,*args): + SMESH._objref_SMESH_ParallelMesh.__init__(self,*args) + def __deepcopy__(self, memo=None): + new = self.__class__(self) + return new +omniORB.registerObjref(SMESH._objref_SMESH_ParallelMesh._NP_RepositoryId, parallelMeshProxy) + + + class submeshProxy(SMESH._objref_SMESH_subMesh): """