From aa034dad38ed214bf71255119d299ec6826588f8 Mon Sep 17 00:00:00 2001 From: "yoann.audouin" Date: Tue, 2 Jan 2024 14:58:58 +0100 Subject: [PATCH] Modifications to properly handle parallel compute for 2D --- doc/examples/creating_parallel_2D_mesh.py | 15 +- doc/examples/creating_parallel_gmsh_mesh.py | 4 +- doc/examples/creating_parallel_mesh.py | 2 +- idl/SMESH_Mesh.idl | 3 + src/SMESH/SMESH_Gen.cxx | 24 +--- src/SMESH/SMESH_Mesh.hxx | 1 + src/SMESH/SMESH_ParallelMesh.cxx | 28 ++++ src/SMESH/SMESH_ParallelMesh.hxx | 6 + src/SMESH/SMESH_SequentialMesh.hxx | 1 + src/SMESH/SMESH_subMesh.cxx | 2 +- src/SMESH_I/SMESH_ParallelMesh_i.cxx | 19 +++ src/SMESH_I/SMESH_ParallelMesh_i.hxx | 3 + src/SMESH_SWIG/smeshBuilder.py | 146 +++++++++++++------- 13 files changed, 177 insertions(+), 77 deletions(-) diff --git a/doc/examples/creating_parallel_2D_mesh.py b/doc/examples/creating_parallel_2D_mesh.py index a351bfc77..2515543aa 100644 --- a/doc/examples/creating_parallel_2D_mesh.py +++ b/doc/examples/creating_parallel_2D_mesh.py @@ -45,8 +45,8 @@ boxes = [] # boxes.append(box) -#With 6 boxes works -#But simplify for 2 boxes to also Test possibility of rewritting the +#With 6 boxes works +#But simplify for 2 boxes to also Test possibility of rewritting the # input mesh from other parallel tests. In that case this test will break # because the input mesh will not match the exported/imported box geometry. for i in range(nbox): @@ -78,12 +78,19 @@ all_boxes = geompy.MakeGlueFaces(all_boxes, 1e-07) geompy.addToStudy(all_boxes, 'Glued_Faces_1') rubik_cube = geompy.MakeGlueEdges(all_boxes, 1e-07) -geompy.addToStudy(all_boxes, 'rubik_cube') +geompy.addToStudy(rubik_cube, 'rubik_cube') smesh = smeshBuilder.New() print("Creating Parallel Mesh") -par_mesh = smesh.ParallelMesh(rubik_cube, name="par_mesh", mesher2D="NETGEN_2D_Remote") +par_mesh = smesh.ParallelMesh(rubik_cube, name="par_mesh") + +print("Creating hypoehtesis for netgen") +NETGEN_2D_Parameters_1 = smesh.CreateHypothesisByAverageLength( 'NETGEN_Parameters_2D', + 'NETGENEngine', 34.641, 0 ) + +print("Adding hypothesis") +par_mesh.Add2DGlobalHypothesis(NETGEN_2D_Parameters_1) print("Setting parallelism method") par_mesh.SetParallelismMethod(smeshBuilder.MULTITHREAD) diff --git a/doc/examples/creating_parallel_gmsh_mesh.py b/doc/examples/creating_parallel_gmsh_mesh.py index a028f6525..51465214f 100644 --- a/doc/examples/creating_parallel_gmsh_mesh.py +++ b/doc/examples/creating_parallel_gmsh_mesh.py @@ -54,11 +54,11 @@ all_boxes = geompy.MakeGlueFaces(all_boxes, 1e-07) geompy.addToStudy(all_boxes, 'Glued_Faces_1') rubik_cube = geompy.MakeGlueEdges(all_boxes, 1e-07) -geompy.addToStudy(all_boxes, 'rubik_cube') +geompy.addToStudy(rubik_cube, 'rubik_cube') smesh = smeshBuilder.New() print("Creating Parallel Mesh") -par_mesh = smesh.ParallelMesh(rubik_cube, name="par_mesh", mesher3D="GMSH") +par_mesh = smesh.ParallelMesh(rubik_cube, name="par_mesh") print("Creating hypoehtesis for gmsh") GMSH_3D_Parameters_1 = smesh.CreateHypothesis( 'GMSH_Parameters', diff --git a/doc/examples/creating_parallel_mesh.py b/doc/examples/creating_parallel_mesh.py index 309674954..1a69fbd80 100644 --- a/doc/examples/creating_parallel_mesh.py +++ b/doc/examples/creating_parallel_mesh.py @@ -54,7 +54,7 @@ all_boxes = geompy.MakeGlueFaces(all_boxes, 1e-07) geompy.addToStudy(all_boxes, 'Glued_Faces_1') rubik_cube = geompy.MakeGlueEdges(all_boxes, 1e-07) -geompy.addToStudy(all_boxes, 'rubik_cube') +geompy.addToStudy(rubik_cube, 'rubik_cube') smesh = smeshBuilder.New() diff --git a/idl/SMESH_Mesh.idl b/idl/SMESH_Mesh.idl index 8ef9a454b..142252a4c 100644 --- a/idl/SMESH_Mesh.idl +++ b/idl/SMESH_Mesh.idl @@ -1109,6 +1109,9 @@ module SMESH long GetParallelismMethod(); void SetParallelismMethod(in long aMethod); + long GetParallelismDimension(); + void SetParallelismDimension(in long aDim); + // Parameters for MutliThreading long GetNbThreads(); void SetNbThreads(in long nbThreads); diff --git a/src/SMESH/SMESH_Gen.cxx b/src/SMESH/SMESH_Gen.cxx index 9f75ddfa5..08a5512b2 100644 --- a/src/SMESH/SMESH_Gen.cxx +++ b/src/SMESH/SMESH_Gen.cxx @@ -394,7 +394,7 @@ bool SMESH_Gen::parallelComputeSubMeshes( // do not mesh vertices of a pseudo shape const TopoDS_Shape& shape = smToCompute->GetSubShape(); const TopAbs_ShapeEnum shapeType = shape.ShapeType(); - // Not doing in parallel 1D and 2D meshes + // Not doing in parallel 1D meshes if ( !aMesh.HasShapeToMesh() && shapeType == TopAbs_VERTEX ) continue; @@ -402,22 +402,10 @@ bool SMESH_Gen::parallelComputeSubMeshes( // Waiting for all threads for the previous type to end aMesh.wait(); - std::string file_name; - switch(previousShapeType){ - case TopAbs_FACE: - file_name = "Mesh2D.med"; - break; - case TopAbs_EDGE: - file_name = "Mesh1D.med"; - break; - //case TopAbs_VERTEX: - // file_name = "Mesh0D.med"; - // break; - case TopAbs_SOLID: - default: - file_name = ""; - break; - } + std::string file_name=""; + if (previousShapeType == aParMesh.GetDumpElement()) + file_name = "Mesh"+std::to_string(aParMesh.GetParallelismDimension()-1)+"D.med"; + if(file_name != "") { fs::path mesh_file = fs::path(aParMesh.GetTmpFolder()) / fs::path(file_name); @@ -439,7 +427,7 @@ bool SMESH_Gen::parallelComputeSubMeshes( continue; } // Parallelism is only for 3D parts - if(shapeType!=TopAbs_SOLID){ + if(shapeType!=aMesh.GetParallelElement()){ compute_function(smToCompute, computeEvent, shapeSM, aShapeOnly, allowedSubShapes, aShapesId); diff --git a/src/SMESH/SMESH_Mesh.hxx b/src/SMESH/SMESH_Mesh.hxx index 4fa3a355d..adb4c03f6 100644 --- a/src/SMESH/SMESH_Mesh.hxx +++ b/src/SMESH/SMESH_Mesh.hxx @@ -397,6 +397,7 @@ class SMESH_EXPORT SMESH_Mesh virtual void wait(){}; virtual bool IsParallel(){throw SALOME_Exception("Calling SMESH_Mesh::IsParallel");return false;}; + virtual int GetParallelElement(){throw SALOME_Exception("Calling SMESH_Mesh::GetParallelElement");return 0;}; virtual bool ComputeSubMeshes( SMESH_Gen* gen, diff --git a/src/SMESH/SMESH_ParallelMesh.cxx b/src/SMESH/SMESH_ParallelMesh.cxx index 4b49486e0..b45e52797 100644 --- a/src/SMESH/SMESH_ParallelMesh.cxx +++ b/src/SMESH/SMESH_ParallelMesh.cxx @@ -151,6 +151,34 @@ void SMESH_ParallelMesh::SetNbThreads(long nbThreads) _NbThreads=nbThreads; }; +//============================================================================= +/*! + * \brief Get the element associated to the dimension of the parallelism + */ +//============================================================================= +int SMESH_ParallelMesh::GetParallelElement() +{ + if (_paraDim==2){ + return TopAbs_FACE; + }else{ + return TopAbs_SOLID; + } +}; + +//============================================================================= +/*! + * \brief Get the element associated to the dimension of the parallelism + */ +//============================================================================= +int SMESH_ParallelMesh::GetDumpElement() +{ + if (_paraDim==2){ + return TopAbs_EDGE; + }else{ + return TopAbs_FACE; + } +}; + bool SMESH_ParallelMesh::ComputeSubMeshes( SMESH_Gen* gen, SMESH_Mesh & aMesh, diff --git a/src/SMESH/SMESH_ParallelMesh.hxx b/src/SMESH/SMESH_ParallelMesh.hxx index 4a418a49f..70cf77a35 100644 --- a/src/SMESH/SMESH_ParallelMesh.hxx +++ b/src/SMESH/SMESH_ParallelMesh.hxx @@ -81,11 +81,16 @@ class SMESH_EXPORT SMESH_ParallelMesh: public SMESH_Mesh // bool IsParallel() override {return true;}; + int GetParallelElement() override; + int GetDumpElement(); // Parallelims paramaters int GetParallelismMethod() {return _method;}; void SetParallelismMethod(int aMethod) {_method = aMethod;}; + int GetParallelismDimension() {return _paraDim;}; + void SetParallelismDimension(int aDim) {_paraDim = aDim;}; + // Mutlithreading parameters int GetNbThreads() {return _NbThreads;}; void SetNbThreads(long nbThreads); @@ -134,6 +139,7 @@ class SMESH_EXPORT SMESH_ParallelMesh: public SMESH_Mesh #endif boost::filesystem::path tmp_folder; int _method = ParallelismMethod::MultiThread; + int _paraDim = 3; int _NbThreads = std::thread::hardware_concurrency(); diff --git a/src/SMESH/SMESH_SequentialMesh.hxx b/src/SMESH/SMESH_SequentialMesh.hxx index 5abf93bd0..1322403df 100644 --- a/src/SMESH/SMESH_SequentialMesh.hxx +++ b/src/SMESH/SMESH_SequentialMesh.hxx @@ -48,6 +48,7 @@ class SMESH_EXPORT SMESH_SequentialMesh: public SMESH_Mesh void wait() override {}; bool IsParallel() override {return false;}; + int GetParallelElement() override {return 0;}; bool ComputeSubMeshes ( SMESH_Gen* gen, diff --git a/src/SMESH/SMESH_subMesh.cxx b/src/SMESH/SMESH_subMesh.cxx index a019213ee..9abcbc215 100644 --- a/src/SMESH/SMESH_subMesh.cxx +++ b/src/SMESH/SMESH_subMesh.cxx @@ -1517,7 +1517,7 @@ bool SMESH_subMesh::ComputeStateEngine(compute_event event) // check submeshes needed // When computing in parallel mode we do not have a additional layer of submesh // The check should not be done in parallel as that check is not thread-safe - if (_father->HasShapeToMesh() && (!_father->IsParallel() || shape.ShapeType() != TopAbs_SOLID )) { + if (_father->HasShapeToMesh() && (!_father->IsParallel() || shape.ShapeType() != _father->GetParallelElement() )) { bool subComputed = false, subFailed = false; if (!algo->OnlyUnaryInput()) { // --- commented for bos#22320 to compute all sub-shapes at once if possible; diff --git a/src/SMESH_I/SMESH_ParallelMesh_i.cxx b/src/SMESH_I/SMESH_ParallelMesh_i.cxx index 6e8bad1a6..3302fbaa0 100644 --- a/src/SMESH_I/SMESH_ParallelMesh_i.cxx +++ b/src/SMESH_I/SMESH_ParallelMesh_i.cxx @@ -81,6 +81,25 @@ void SMESH_ParallelMesh_i::SetParallelismMethod(CORBA::Long aMethod){ DownCast()->SetParallelismMethod(aMethod); } +//============================================================================= +/*! + * \brief Get the parallell dimension + */ +//============================================================================= + +CORBA::Long SMESH_ParallelMesh_i::GetParallelismDimension(){ + return DownCast()->GetParallelismDimension(); +} + +//============================================================================= +/*! + * \brief Set the parallell dimension + */ +//============================================================================= +void SMESH_ParallelMesh_i::SetParallelismDimension(CORBA::Long aDim){ + DownCast()->SetParallelismDimension(aDim); +} + //============================================================================= /*! * \brief Get the number of threads for a parallel computation diff --git a/src/SMESH_I/SMESH_ParallelMesh_i.hxx b/src/SMESH_I/SMESH_ParallelMesh_i.hxx index 51ae0c93f..a5c9a407a 100644 --- a/src/SMESH_I/SMESH_ParallelMesh_i.hxx +++ b/src/SMESH_I/SMESH_ParallelMesh_i.hxx @@ -53,6 +53,9 @@ class SMESH_I_EXPORT SMESH_ParallelMesh_i: CORBA::Long GetParallelismMethod(); void SetParallelismMethod(CORBA::Long aMethod); + CORBA::Long GetParallelismDimension(); + void SetParallelismDimension(CORBA::Long aDim); + CORBA::Long GetNbThreads(); void SetNbThreads(CORBA::Long nbThreads); diff --git a/src/SMESH_SWIG/smeshBuilder.py b/src/SMESH_SWIG/smeshBuilder.py index cfeda1a3a..68b62eadc 100644 --- a/src/SMESH_SWIG/smeshBuilder.py +++ b/src/SMESH_SWIG/smeshBuilder.py @@ -462,7 +462,7 @@ class smeshBuilder( SMESH._objref_SMESH_Gen, object ): obj,name = name,obj return Mesh(self, self.geompyD, obj, name) - def ParallelMesh(self, obj, name=0, split_geom=True, mesher2D=None, mesher3D="NETGEN"): + def ParallelMesh(self, obj, name=0, split_geom=True): """ Create a parallel mesh. @@ -476,7 +476,7 @@ class smeshBuilder( SMESH._objref_SMESH_Gen, object ): an instance of class :class:`ParallelMesh`. """ return ParallelMesh(self, self.geompyD, obj, - split_geom=split_geom, name=name, mesher2D=mesher2D, mesher3D=mesher3D ) + split_geom=split_geom, name=name) def RemoveMesh( self, mesh ): """ @@ -7582,8 +7582,8 @@ 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 to have same results - 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()) @@ -7667,23 +7667,15 @@ 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)) + 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)) - # Creating submesh for edges 1D/2D part - 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 all_faces, solids + return faces, solids MULTITHREAD, MULTINODE = range(2) @@ -7810,7 +7802,7 @@ class ParallelMesh(Mesh): """ Surcharge on Mesh for parallel computation of a mesh """ - def __init__(self, smeshpyD, geompyD, geom, split_geom=True, name=0, mesher2D=None, mesher3D="NETGEN"): + def __init__(self, smeshpyD, geompyD, geom, split_geom=True, name=0): """ Create a parallel mesh. @@ -7833,48 +7825,64 @@ class ParallelMesh(Mesh): import shaperBuilder # If we have a shaper object converting it into geom (temporary solution) if isinstance(geom, SHAPERSTUDY.SHAPERSTUDY_ORB._objref_SHAPER_Object): - geom_obj = _shaperstudy2geom(geompyD, geom) + self._geom_obj = _shaperstudy2geom(geompyD, geom) else: - geom_obj = geom + self._geom_obj = geom # Splitting geometry into one geom containing 1D and 2D elements and a # list of 3D elements - super(ParallelMesh, self).__init__(smeshpyD, geompyD, geom_obj, 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_obj) + self._faces, self._solids = _split_geom(geompyD, self._geom_obj) - if mesher3D == "NETGEN": - self._algo2d = self.Triangle(geom=geom_obj, algo="NETGEN_2D") - elif mesher3D == "GMSH": - self._algo2d = self.Triangle(geom=geom_obj, algo="GMSH_2D") - else: - raise ValueError("mesher3D should be either NETGEN or GMSH") + self._param = None + def _build_submeshes(self, mesher2D, mesher3D): + """ + Contruct the submeshes for a parallel use of smesh - if mesher2D is not None: - #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 - self._algo1d = self.Segment().AutomaticLength(0.1) + Parameters: + mesher2D: name of 2D mesher for 2D parallel compute (NETGEN) + mesher3D: name of 3D mesher for 3D parallel compute (NETGEN or + GMSH) + """ - for solid_id, solid in enumerate(self._solids): - name = "Solid_{}".format(solid_id) - algo2d = self.Triangle(geom=solid, algo="NETGEN_2D_Remote") - self._algo2d.append(algo2d) + # Building global 2D mesher + if mesher3D: + if mesher3D == "NETGEN": + algo2D = "NETGEN_2D" + elif mesher3D == "GMSH": + algo2D = "GMSH_2D" else: - self._algo3d = [] - for solid_id, solid in enumerate(self._solids): - name = "Solid_{}".format(solid_id) - 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) + raise ValueError("mesher3D should be either NETGEN or GMSH") - self._param = None + 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) + 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) def GetNbSolids(self): """ @@ -7882,6 +7890,12 @@ class ParallelMesh(Mesh): """ 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() @@ -7918,11 +7932,16 @@ class ParallelMesh(Mesh): """ 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_param(2, param2d, hyp) @@ -7930,6 +7949,31 @@ class ParallelMesh(Mesh): param3d = algo3d.Parameters() 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 + + """ + 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 -- 2.30.2