From: Yoann Audouin Date: Thu, 9 Mar 2023 15:32:55 +0000 (+0100) Subject: Cleanup of parallel meshing + documentation X-Git-Tag: V9_11_0a1~18 X-Git-Url: http://git.salome-platform.org/gitweb/?a=commitdiff_plain;h=d328a1d7b8ebcd3d3f02ea9996d56d50016be3a4;p=modules%2Fsmesh.git Cleanup of parallel meshing + documentation --- diff --git a/doc/examples/creating_parallel_mesh.py b/doc/examples/creating_parallel_mesh.py new file mode 100644 index 000000000..e837fc9d7 --- /dev/null +++ b/doc/examples/creating_parallel_mesh.py @@ -0,0 +1,141 @@ +# contains function to compute a mesh in parallel +from platform import java_ver +import sys +from tkinter import W +import salome + +import time + + +salome.salome_init() +import salome_notebook +notebook = salome_notebook.NoteBook() + +### +### GEOM component +### + +import GEOM +from salome.geom import geomBuilder +from salome.smesh import smeshBuilder +import math +import SALOMEDS + +import numpy as np + +geompy = geomBuilder.New() + +smesh = smeshBuilder.New() + + +def build_seq_mesh(nbox, boxsize, offset): + # Create 3D faces + boxes = [] + # First creating all the boxes + for i in range(nbox): + for j in range(nbox): + for k in range(nbox): + + x_orig = i*(boxsize+offset) + y_orig = j*(boxsize+offset) + z_orig = k*(boxsize+offset) + + tmp_box = geompy.MakeBoxDXDYDZ(boxsize, boxsize, boxsize) + + if not i == j == k == 0: + box = geompy.MakeTranslation(tmp_box, x_orig, + y_orig, z_orig) + else: + box = tmp_box + + geompy.addToStudy(box, 'box_{}:{}:{}'.format(i, j, k)) + + boxes.append(box) + + # Create fuse of all boxes + all_boxes = geompy.MakeCompound(boxes) + geompy.addToStudy(all_boxes, 'Compound_1') + + # Removing duplicates faces and edges + all_boxes = geompy.MakeGlueFaces(all_boxes, 1e-07) + geompy.addToStudy(all_boxes, 'Glued_Faces_1') + + all_boxes = geompy.MakeGlueEdges(all_boxes, 1e-07) + geompy.addToStudy(all_boxes, 'rubik_cube') + + + # Building sequetial mesh + print("Creating mesh") + all_box_mesh = smesh.Mesh(all_boxes, "seq_mesh") + + print("Adding algo") + algo3d = all_box_mesh.Tetrahedron(algo=smeshBuilder.NETGEN_1D2D3D) + + netgen_parameters = algo3d.Parameters() + netgen_parameters.SetMaxSize(34.641) + netgen_parameters.SetMinSize(0.141421) + netgen_parameters.SetOptimize(1) + netgen_parameters.SetCheckOverlapping(0) + netgen_parameters.SetCheckChartBoundary(0) + netgen_parameters.SetFineness(5) + netgen_parameters.SetNbSegPerEdge(16*(boxsize//100)) + netgen_parameters.SetNbSegPerRadius(1.5) + netgen_parameters.SetGrowthRate(0.15) + netgen_parameters.SetChordalError(-1) + netgen_parameters.SetChordalErrorEnabled(0) + netgen_parameters.SetUseSurfaceCurvature(1) + netgen_parameters.SetQuadAllowed(0) + netgen_parameters.SetCheckOverlapping(False) + netgen_parameters.SetNbThreads(2) + + return all_boxes, all_box_mesh, netgen_parameters + +def run_test(nbox=2, boxsize=100): + """ Run sequential mesh and parallel version of it + + nbox: NUmber of boxes + boxsize: Size of each box + """ + geom, seq_mesh, netgen_parameters = build_seq_mesh(nbox, boxsize, 0) + + print("Creating Parallel Mesh") + par_mesh = smesh.ParallelMesh(geom, name="par_mesh") + par_mesh.AddGlobalHypothesis(netgen_parameters) + param = par_mesh.GetParallelismSettings() + param.SetNbThreads(6) + + assert param.GetNbThreads() == 6, param.GetNbThreads() + + print("Starting sequential compute") + start = time.monotonic() + is_done = seq_mesh.Compute() + assert is_done + stop = time.monotonic() + time_seq = stop-start + + print("Starting parallel compute") + start = time.monotonic() + is_done = par_mesh.Compute() + assert is_done + stop = time.monotonic() + time_par = stop-start + + print(" Tetrahedron: ", seq_mesh.NbTetras(), par_mesh.NbTetras()) + print(" Triangle: ", seq_mesh.NbTriangles(), par_mesh.NbTriangles()) + print(" edge: ", seq_mesh.NbEdges(), par_mesh.NbEdges()) + + assert par_mesh.NbTetras() > 0 + assert par_mesh.NbTriangles() > 0 + assert par_mesh.NbEdges() > 0 + + print("Time elapsed (seq, par): ", time_seq, time_par) + +def main(): + if sys.platform == "win32": + print("Test disabled on Windows") + return + nbox = 2 + boxsize = 100 + run_test(nbox, boxsize) + +main() diff --git a/doc/examples/tests.set b/doc/examples/tests.set index df39d9dd9..e051ca01c 100644 --- a/doc/examples/tests.set +++ b/doc/examples/tests.set @@ -129,6 +129,7 @@ SET(BAD_TESTS viewing_meshes_ex01.py radial_prism_3d_algo.py create_dual_mesh.py + creating_parallel_mesh.py ) IF(NOT WIN32) LIST(APPEND BAD_TESTS diff --git a/doc/gui/images/diagram_parallel_mesh.png b/doc/gui/images/diagram_parallel_mesh.png new file mode 100644 index 000000000..466b29b97 Binary files /dev/null and b/doc/gui/images/diagram_parallel_mesh.png differ diff --git a/doc/gui/input/about_meshes.rst b/doc/gui/input/about_meshes.rst index 619bd799f..6350eea4b 100644 --- a/doc/gui/input/about_meshes.rst +++ b/doc/gui/input/about_meshes.rst @@ -71,6 +71,8 @@ Quadratic mesh can be obtained in three ways: * Using :ref:`convert_to_from_quadratic_mesh_page` operation. * Using an appropriate option of some meshing algorithms, which generate elements of several dimensions starting from mesh segments. +A work in progress allow you to compute your mesh in parralle :ref:`Parallel Computing` + **Table of Contents** @@ -86,3 +88,4 @@ Quadratic mesh can be obtained in three ways: copy_mesh.rst create_dual_mesh.rst connectivity.rst + parallel_compute.rst diff --git a/doc/gui/input/parallel_compute.rst b/doc/gui/input/parallel_compute.rst new file mode 100644 index 000000000..fecbf2fea --- /dev/null +++ b/doc/gui/input/parallel_compute.rst @@ -0,0 +1,70 @@ +.. _parallel_compute_page: + +****************** +Parallel Computing +****************** + + +.. warning:: + This functionality is a work in progress. + + It is only available for NETGEN. + + It is only available in TUI. + + +The goal here is to speed up computation by running sub-meshes in parallel +(multi-threading). + +******* +Concept +******* + +.. image:: ../images/diagram_parallel_mesh.png + +In order to parallelise the computation of the mesh we split the geometry into: + + * A 1D+2D compound + * A list of 3D solids + +Then create a sub-mesh for each of those geometry. +And associate Hypothesis to the mesh using a hypothesis on the whole geometry + +We will first compute sequentially the 1D+2D compound with NETGEN_1D2D. + +Then we will compute all the solids in parallel. Having done the 1D+2D first +ensure that all the solids can be computed without any concurrency. + + +****** +How to +****** + +You follow the same principle as the creation of a sequential Mesh. + + +#. First you create the mesh: + .. code-block:: python + + par_mesh = smesh.ParallelMesh(geom, name="par_mesh") + +#. Define the Global Hypothesis that will be split into an hypothesis for the + 1D+2D compound and one for each of the 3D solids: + .. code-block:: python + + NETGEN_3D_Parameters_1 = smesh.CreateHypothesisByAverageLength( 'NETGEN_Parameters', + 'NETGENEngine', 34.641, 0 ) + par_mesh.AddGlobalHypothesis(netgen_parameters) + +#. Set the parameters for the parallelisation: + .. code-block:: python + + param = par_mesh.GetParallelismSettings() + param.SetNbThreads(6) + +#. Compute the mesh: + .. code-block:: python + + mesh.Compute() + +**See Also** a sample script of :ref:`tui_create_parallel_mesh`. diff --git a/doc/gui/input/tui_creating_meshes.rst b/doc/gui/input/tui_creating_meshes.rst index 253fbf260..67e14d358 100644 --- a/doc/gui/input/tui_creating_meshes.rst +++ b/doc/gui/input/tui_creating_meshes.rst @@ -117,3 +117,12 @@ Creating Dual Mesh :download:`Download this script <../../examples/create_dual_mesh.py>` +.. _tui_create_parallel_mesh: + +Creating Parallel Mesh +====================== + +.. literalinclude:: ../../examples/creating_parallel_mesh.py + :language: python + +:download:`Download this script <../../examples/creating_parallel_mesh.py>` diff --git a/idl/SMESH_Gen.idl b/idl/SMESH_Gen.idl index a5b48aaa6..a36e30d3e 100644 --- a/idl/SMESH_Gen.idl +++ b/idl/SMESH_Gen.idl @@ -242,6 +242,16 @@ module SMESH SMESH_Mesh CreateMesh( in GEOM::GEOM_Object theObject ) raises ( SALOME::SALOME_Exception ); + /*! + * Create a Mesh object, given a geometry shape. + * Mesh is created empty (no points, no elements). + * Shape is explored via GEOM_Client to create local copies. + * of TopoDS_Shapes and bind CORBA references of shape & subshapes + * with TopoDS_Shapes + * The mesh is a parallel one + */ + SMESH_Mesh CreateParallelMesh( in GEOM::GEOM_Object theObject ) + raises ( SALOME::SALOME_Exception ); /*! * Create an empty mesh object */ diff --git a/idl/SMESH_Mesh.idl b/idl/SMESH_Mesh.idl index 7b86361d8..88152274e 100644 --- a/idl/SMESH_Mesh.idl +++ b/idl/SMESH_Mesh.idl @@ -904,6 +904,11 @@ module SMESH */ void SetNbThreads(in long nbThreads); /*! + /*! + * \brief Get Number of Threads + */ + long GetNbThreads(); + /*! /*! * Get mesh description @@ -1108,6 +1113,9 @@ module SMESH long GetId(); }; + interface SMESH_SequentialMesh:SMESH_Mesh{}; + interface SMESH_ParallelMesh:SMESH_Mesh{}; + }; #endif diff --git a/src/SMESH/CMakeLists.txt b/src/SMESH/CMakeLists.txt index 88d42d875..ec3761798 100644 --- a/src/SMESH/CMakeLists.txt +++ b/src/SMESH/CMakeLists.txt @@ -77,6 +77,8 @@ SET(_link_LIBRARIES SET(SMESHimpl_HEADERS SMESH_Gen.hxx SMESH_Mesh.hxx + SMESH_SequentialMesh.hxx + SMESH_ParallelMesh.hxx SMESH_subMesh.hxx SMESH_subMeshEventListener.hxx SMESH_Hypothesis.hxx @@ -102,6 +104,8 @@ SET(SMESHimpl_SOURCES memoire.h SMESH_Gen.cxx SMESH_Mesh.cxx + SMESH_SequentialMesh.cxx + SMESH_ParallelMesh.cxx SMESH_subMesh.cxx SMESH_Hypothesis.cxx SMESH_Algo.cxx diff --git a/src/SMESH/SMESH_Gen.cxx b/src/SMESH/SMESH_Gen.cxx index bd8098302..4a020890e 100644 --- a/src/SMESH/SMESH_Gen.cxx +++ b/src/SMESH/SMESH_Gen.cxx @@ -27,9 +27,6 @@ // //#define CHRONODEF // -#ifndef WIN32 -#include -#endif #include "SMESH_Gen.hxx" #include "SMESH_DriverMesh.hxx" @@ -39,6 +36,8 @@ #include "SMESHDS_Document.hxx" #include "SMESH_HypoFilter.hxx" #include "SMESH_Mesh.hxx" +#include "SMESH_SequentialMesh.hxx" +#include "SMESH_ParallelMesh.hxx" #include "SMESH_MesherHelper.hxx" #include "SMESH_subMesh.hxx" @@ -58,6 +57,10 @@ #include +#ifndef WIN32 +#include +#endif + using namespace std; #ifndef WIN32 #include @@ -154,7 +157,8 @@ SMESH_Mesh* SMESH_Gen::CreateMesh(bool theIsEmbeddedMode) Unexpect aCatch(SalomeException); // create a new SMESH_mesh object - SMESH_Mesh *aMesh = new SMESH_Mesh(_localId++, + SMESH_Mesh *aMesh = new SMESH_SequentialMesh( + _localId++, this, theIsEmbeddedMode, _studyContext->myDocument); @@ -163,6 +167,27 @@ SMESH_Mesh* SMESH_Gen::CreateMesh(bool theIsEmbeddedMode) return aMesh; } +//============================================================================= +/*! + * Creates a parallel mesh in a study. + * if (theIsEmbeddedMode) { mesh modification commands are not logged } + */ +//============================================================================= + +SMESH_Mesh* SMESH_Gen::CreateParallelMesh(bool theIsEmbeddedMode) +{ + Unexpect aCatch(SalomeException); + + // create a new SMESH_mesh object + SMESH_Mesh *aMesh = new SMESH_ParallelMesh( + _localId++, + this, + theIsEmbeddedMode, + _studyContext->myDocument); + _studyContext->mapMesh[_localId-1] = aMesh; + + return aMesh; +} //============================================================================= /*! @@ -200,7 +225,7 @@ bool SMESH_Gen::sequentialComputeSubMeshes( continue; // check for preview dimension limitations - if ( aShapesId && GetShapeDim( shapeType ) > (int)aDim ) + if ( aShapesId && SMESH_Gen::GetShapeDim( shapeType ) > (int)aDim ) { // clear compute state not to show previous compute errors // if preview invoked less dimension less than previous @@ -264,6 +289,7 @@ const std::function (int)aDim ) + if ( aShapesId && SMESH_Gen::GetShapeDim( shapeType ) > (int)aDim ) { // clear compute state not to show previous compute errors // if preview invoked less dimension less than previous smToCompute->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE ); continue; } - boost::asio::post(*(aMesh._pool), std::bind(compute_function, smToCompute, computeEvent, + boost::asio::post(*(aMesh.GetPool()), std::bind(compute_function, smToCompute, computeEvent, shapeSM, aShapeOnly, allowedSubShapes, aShapesId)); } @@ -364,7 +384,6 @@ bool SMESH_Gen::parallelComputeSubMeshes( aMesh.wait(); aMesh.GetMeshDS()->Modified(); - aMesh.DeleteTmpFolder(); return ret; #endif @@ -416,22 +435,14 @@ bool SMESH_Gen::Compute(SMESH_Mesh & aMesh, // =============================================== // Mesh all the sub-shapes starting from vertices // =============================================== - if (aMesh.IsParallel()) - ret = parallelComputeSubMeshes( - aMesh, aShape, aDim, - aShapesId, allowedSubShapes, - computeEvent, - includeSelf, - complexShapeFirst, - aShapeOnly); - else - ret = sequentialComputeSubMeshes( - aMesh, aShape, aDim, - aShapesId, allowedSubShapes, - computeEvent, - includeSelf, - complexShapeFirst, - aShapeOnly); + ret = aMesh.ComputeSubMeshes( + this, + aMesh, aShape, aDim, + aShapesId, allowedSubShapes, + computeEvent, + includeSelf, + complexShapeFirst, + aShapeOnly); return ret; } diff --git a/src/SMESH/SMESH_Gen.hxx b/src/SMESH/SMESH_Gen.hxx index 94058184e..40e92ca37 100644 --- a/src/SMESH/SMESH_Gen.hxx +++ b/src/SMESH/SMESH_Gen.hxx @@ -70,6 +70,7 @@ public: ~SMESH_Gen(); SMESH_Mesh* CreateMesh(bool theIsEmbeddedMode); + SMESH_Mesh* CreateParallelMesh(bool theIsEmbeddedMode); enum ComputeFlags { @@ -167,9 +168,7 @@ public: int GetANewId(); -private: - - +public: bool parallelComputeSubMeshes( SMESH_Mesh & aMesh, const TopoDS_Shape & aShape, @@ -191,6 +190,11 @@ private: const bool includeSelf, const bool complexShapeFirst, const bool aShapeOnly); + +private: + + + int _localId; // unique Id of created objects, within SMESH_Gen entity StudyContextStruct* _studyContext; diff --git a/src/SMESH/SMESH_Mesh.cxx b/src/SMESH/SMESH_Mesh.cxx index 82ce58633..ad3d733f0 100644 --- a/src/SMESH/SMESH_Mesh.cxx +++ b/src/SMESH/SMESH_Mesh.cxx @@ -232,8 +232,6 @@ SMESH_Mesh::~SMESH_Mesh() int result=pthread_create(&thread, NULL, deleteMeshDS, (void*)_meshDS); #endif } - if(_pool) - DeletePoolThreads(); } //================================================================================ @@ -2564,30 +2562,3 @@ void SMESH_Mesh::getAncestorsSubMeshes (const TopoDS_Shape& theSubSha // sort submeshes according to stored mesh order SortByMeshOrder( theSubMeshes ); } - - -//============================================================================= -/*! - * \brief Build folder for parallel computation - */ -//============================================================================= -void SMESH_Mesh::CreateTmpFolder() -{ -#ifndef WIN32 - // Temporary folder that will be used by parallel computation - tmp_folder = fs::temp_directory_path()/fs::unique_path(fs::path("SMESH_%%%%-%%%%")); - fs::create_directories(tmp_folder); -#endif -} -// -//============================================================================= -/*! - * \brief Delete temporary folder used for parallel computation - */ -//============================================================================= -void SMESH_Mesh::DeleteTmpFolder() -{ -#ifndef WIN32 - fs::remove_all(tmp_folder); -#endif -} diff --git a/src/SMESH/SMESH_Mesh.hxx b/src/SMESH/SMESH_Mesh.hxx index 9c9606499..c0cbb37b6 100644 --- a/src/SMESH/SMESH_Mesh.hxx +++ b/src/SMESH/SMESH_Mesh.hxx @@ -33,6 +33,7 @@ #include "SMESH_ComputeError.hxx" #include "SMESH_Controls.hxx" #include "SMESH_Hypothesis.hxx" +#include "SMESH_subMesh.hxx" #include "SMDS_Iterator.hxx" #include "Utils_SALOME_Exception.hxx" @@ -72,6 +73,7 @@ class TopoDS_Solid; class DriverMED_W_SMESHDS_Mesh; +typedef std::set TSetOfInt; typedef std::list TListOfInt; typedef std::list TListOfListOfInt; @@ -390,45 +392,32 @@ class SMESH_EXPORT SMESH_Mesh // Parallel computation functions -#ifdef WIN32 - void Lock() {}; - void Unlock() {}; - - int GetNbThreads(){return _NbThreads;}; - void SetNbThreads(long nbThreads){std::cout << "Warning Parallel Meshing is disabled on Windows it will behave as a slower normal compute" << std::endl;_NbThreads=nbThreads;}; - - void InitPoolThreads(){}; - void DeletePoolThreads(){}; - void wait(){} + virtual void Lock(){}; + virtual void Unlock(){}; - bool IsParallel(){return _NbThreads > 0;} -#else - void Lock() {_my_lock.lock();}; - void Unlock() {_my_lock.unlock();}; + virtual int GetNbThreads(){return 0;}; + virtual void SetNbThreads(long nbThreads){(void) nbThreads;}; - int GetNbThreads(){return _NbThreads;}; - void SetNbThreads(long nbThreads){_NbThreads=nbThreads;}; + virtual void InitPoolThreads(){std::cout << "Should not pass here: InitPoolThread" << std::endl;}; + virtual void DeletePoolThreads(){std::cout << "Should not pass here: DeletePoolThread" << std::endl;}; + virtual void wait(){std::cout << "Should not pass here: wait" << std::endl;}; - void InitPoolThreads(){_pool = new boost::asio::thread_pool(_NbThreads);}; - void DeletePoolThreads(){delete _pool;}; + virtual bool IsParallel(){std::cout << "Should not pass here: IsParallel" << std::endl;return false;}; - void wait(){_pool->join(); DeletePoolThreads(); InitPoolThreads(); } - - bool IsParallel(){return _NbThreads > 0;} -#endif - - void CreateTmpFolder(); - void DeleteTmpFolder(); - - // Temporary folder used during parallel Computation -#ifndef WIN32 - boost::filesystem::path tmp_folder; - boost::asio::thread_pool * _pool = nullptr; //thread pool for computation -#else - std::string tmp_folder; - bool _pool = false; -#endif + virtual boost::filesystem::path GetTmpFolder() {return "";}; + virtual boost::asio::thread_pool* GetPool() {return NULL;}; + virtual bool ComputeSubMeshes( + SMESH_Gen* gen, + SMESH_Mesh & aMesh, + const TopoDS_Shape & aShape, + const ::MeshDimension aDim, + TSetOfInt* aShapesId /*=0*/, + TopTools_IndexedMapOfShape* allowedSubShapes, + SMESH_subMesh::compute_event &computeEvent, + const bool includeSelf, + const bool complexShapeFirst, + const bool aShapeOnly){(void) gen;(void) aMesh;(void) aShape;(void) aDim;(void) aShapesId;(void) allowedSubShapes;(void) computeEvent;(void) includeSelf;(void) complexShapeFirst;(void) aShapeOnly;std::cout << "Should not pass here: computesubmesh" << std::endl;return false;}; private: @@ -480,7 +469,7 @@ protected: #ifndef WIN32 boost::mutex _my_lock; #endif - int _NbThreads=0; + int _NbThreads=-1; protected: SMESH_Mesh(); diff --git a/src/SMESH/SMESH_ParallelMesh.cxx b/src/SMESH/SMESH_ParallelMesh.cxx new file mode 100644 index 000000000..4b9f83ce5 --- /dev/null +++ b/src/SMESH/SMESH_ParallelMesh.cxx @@ -0,0 +1,120 @@ +// Copyright (C) 2007-2022 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// 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 : SMESH_ParallelMesh.cxx +// Author : Yoann AUDOUIN, EDF +// Module : SMESH +// +#include "SMESH_ParallelMesh.hxx" + +#include "SMESH_Gen.hxx" + +#ifdef WIN32 + #include +#endif + +#ifndef WIN32 +#include +namespace fs=boost::filesystem; +#endif + +#ifndef WIN32 +#include +#endif + +#include + +#ifdef _DEBUG_ +static int MYDEBUG = 1; +#else +static int MYDEBUG = 0; +#endif + +SMESH_ParallelMesh::SMESH_ParallelMesh(int theLocalId, + SMESH_Gen* theGen, + bool theIsEmbeddedMode, + SMESHDS_Document* theDocument) :SMESH_Mesh(theLocalId, + theGen, + theIsEmbeddedMode, + theDocument) +{ + MESSAGE("SMESH_ParallelMesh::SMESH_ParallelMesh(int localId)"); + _NbThreads = std::thread::hardware_concurrency(); + CreateTmpFolder(); +}; + +SMESH_ParallelMesh::~SMESH_ParallelMesh() +{ + DeletePoolThreads(); + if(!MYDEBUG) + DeleteTmpFolder(); +}; + + + +//============================================================================= +/*! + * \brief Build folder for parallel computation + */ +//============================================================================= +void SMESH_ParallelMesh::CreateTmpFolder() +{ +#ifndef WIN32 + // Temporary folder that will be used by parallel computation + tmp_folder = fs::temp_directory_path()/fs::unique_path(fs::path("SMESH_%%%%-%%%%")); + fs::create_directories(tmp_folder); +#endif +} +// +//============================================================================= +/*! + * \brief Delete temporary folder used for parallel computation + */ +//============================================================================= +void SMESH_ParallelMesh::DeleteTmpFolder() +{ +#ifndef WIN32 + fs::remove_all(tmp_folder); +#endif +} + +bool SMESH_ParallelMesh::ComputeSubMeshes( + SMESH_Gen* gen, + SMESH_Mesh & aMesh, + const TopoDS_Shape & aShape, + const ::MeshDimension aDim, + TSetOfInt* aShapesId /*=0*/, + TopTools_IndexedMapOfShape* allowedSubShapes, + SMESH_subMesh::compute_event &computeEvent, + const bool includeSelf, + const bool complexShapeFirst, + const bool aShapeOnly) +{ + InitPoolThreads(); + return gen->parallelComputeSubMeshes( + aMesh, aShape, aDim, + aShapesId, allowedSubShapes, + computeEvent, + includeSelf, + complexShapeFirst, + aShapeOnly); +} diff --git a/src/SMESH/SMESH_ParallelMesh.hxx b/src/SMESH/SMESH_ParallelMesh.hxx new file mode 100644 index 000000000..09a520681 --- /dev/null +++ b/src/SMESH/SMESH_ParallelMesh.hxx @@ -0,0 +1,83 @@ +// Copyright (C) 2007-2022 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// 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 : SMESH_ParallelMesh.hxx +// Author : Yoann AUDOUIN, EDF +// Module : SMESH +// +#ifndef _SMESH_PARALLELMESH_HXX_ +#define _SMESH_PARALLELMESH_HXX_ + +#include "SMESH_Mesh.hxx" + +#include "SMESH_Gen.hxx" +#include "SMESH_subMesh.hxx" + +class SMESH_EXPORT SMESH_ParallelMesh: public SMESH_Mesh +{ + public: + SMESH_ParallelMesh(int theLocalId, + SMESH_Gen* theGen, + bool theIsEmbeddedMode, + SMESHDS_Document* theDocument); + + virtual ~SMESH_ParallelMesh(); + + void Lock() override {_my_lock.lock();}; + void Unlock() override {_my_lock.unlock();}; + + int GetNbThreads() override{return _NbThreads;}; + void SetNbThreads(long nbThreads) override{_NbThreads=nbThreads;}; + + void InitPoolThreads() override {_pool = new boost::asio::thread_pool(_NbThreads);}; + void DeletePoolThreads() override {delete _pool;}; + + void wait() override {_pool->join(); DeletePoolThreads(); InitPoolThreads(); }; + + bool IsParallel() override {return _NbThreads > 0;}; + + void CreateTmpFolder(); + void DeleteTmpFolder(); + + boost::filesystem::path GetTmpFolder() override {return tmp_folder;}; + boost::asio::thread_pool* GetPool() override {return _pool;}; + + bool ComputeSubMeshes( + SMESH_Gen* gen, + SMESH_Mesh & aMesh, + const TopoDS_Shape & aShape, + const ::MeshDimension aDim, + TSetOfInt* aShapesId /*=0*/, + TopTools_IndexedMapOfShape* allowedSubShapes, + SMESH_subMesh::compute_event &computeEvent, + const bool includeSelf, + const bool complexShapeFirst, + const bool aShapeOnly) override; + + protected: + SMESH_ParallelMesh():SMESH_Mesh() {}; + SMESH_ParallelMesh(const SMESH_ParallelMesh& aMesh):SMESH_Mesh(aMesh) {}; + private: + boost::filesystem::path tmp_folder; + boost::asio::thread_pool * _pool = nullptr; //thread pool for computation +}; +#endif diff --git a/src/SMESH/SMESH_SequentialMesh.cxx b/src/SMESH/SMESH_SequentialMesh.cxx new file mode 100644 index 000000000..35023ae50 --- /dev/null +++ b/src/SMESH/SMESH_SequentialMesh.cxx @@ -0,0 +1,69 @@ +// Copyright (C) 2007-2022 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// 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 : SMESH_SequentialMesh.cxx +// Author : Yoann AUDOUIN, EDF +// Module : SMESH +// +#include "SMESH_SequentialMesh.hxx" + +#include +#include +#include + +#include + +SMESH_SequentialMesh::SMESH_SequentialMesh(int theLocalId, + SMESH_Gen* theGen, + bool theIsEmbeddedMode, + SMESHDS_Document* theDocument) :SMESH_Mesh(theLocalId, + theGen, + theIsEmbeddedMode, + theDocument) +{ + MESSAGE("SMESH_SequentialMesh::SMESH_SequentialMesh(int localId)"); +}; + +SMESH_SequentialMesh::~SMESH_SequentialMesh() +{ +}; + +bool SMESH_SequentialMesh::ComputeSubMeshes( + SMESH_Gen* gen, + SMESH_Mesh & aMesh, + const TopoDS_Shape & aShape, + const ::MeshDimension aDim, + TSetOfInt* aShapesId /*=0*/, + TopTools_IndexedMapOfShape* allowedSubShapes, + SMESH_subMesh::compute_event &computeEvent, + const bool includeSelf, + const bool complexShapeFirst, + const bool aShapeOnly) +{ + return gen->sequentialComputeSubMeshes( + aMesh, aShape, aDim, + aShapesId, allowedSubShapes, + computeEvent, + includeSelf, + complexShapeFirst, + aShapeOnly); +}; diff --git a/src/SMESH/SMESH_SequentialMesh.hxx b/src/SMESH/SMESH_SequentialMesh.hxx new file mode 100644 index 000000000..90cf904ce --- /dev/null +++ b/src/SMESH/SMESH_SequentialMesh.hxx @@ -0,0 +1,73 @@ +// Copyright (C) 2007-2022 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// 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 : SMESH_SequentialMesh.hxx +// Author : Yoann AUDOUIN, EDF +// Module : SMESH +// +#ifndef _SMESH_SEQUENTIALMESH_HXX_ +#define _SMESH_SEQUENTIALMESH_HXX_ + +#include "SMESH_Mesh.hxx" + +#include "SMESH_Gen.hxx" +#include "SMESH_subMesh.hxx" + +class SMESH_EXPORT SMESH_SequentialMesh: public SMESH_Mesh +{ + public: + SMESH_SequentialMesh(int theLocalId, + SMESH_Gen* theGen, + bool theIsEmbeddedMode, + SMESHDS_Document* theDocument); + + virtual ~SMESH_SequentialMesh(); + + void Lock() override {}; + void Unlock() override {}; + + int GetNbThreads() override {return 0;}; + void SetNbThreads(long nbThreads) {(void) nbThreads;}; + + void InitPoolThreads() override {}; + void DeletePoolThreads() override {}; + void wait() override {}; + + bool IsParallel() override {return false;}; + + bool ComputeSubMeshes ( + SMESH_Gen* gen, + SMESH_Mesh & aMesh, + const TopoDS_Shape & aShape, + const ::MeshDimension aDim, + TSetOfInt* aShapesId /*=0*/, + TopTools_IndexedMapOfShape* allowedSubShapes, + SMESH_subMesh::compute_event &computeEvent, + const bool includeSelf, + const bool complexShapeFirst, + const bool aShapeOnly) override; + + protected: + SMESH_SequentialMesh():SMESH_Mesh() {}; + SMESH_SequentialMesh(const SMESH_SequentialMesh& aMesh):SMESH_Mesh(aMesh) {}; +}; +#endif diff --git a/src/SMESH/SMESH_subMesh.cxx b/src/SMESH/SMESH_subMesh.cxx index 76dfb83e1..bc3aa8792 100644 --- a/src/SMESH/SMESH_subMesh.cxx +++ b/src/SMESH/SMESH_subMesh.cxx @@ -1515,7 +1515,8 @@ bool SMESH_subMesh::ComputeStateEngine(compute_event event) TopoDS_Shape shape = _subShape; algo->setSubMeshesToCompute(this); // check submeshes needed - // In parallel there would be no submesh to check + // 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()) { bool subComputed = false, subFailed = false; if (!algo->OnlyUnaryInput()) { diff --git a/src/SMESH_I/CMakeLists.txt b/src/SMESH_I/CMakeLists.txt index 218cb915e..f525f022c 100644 --- a/src/SMESH_I/CMakeLists.txt +++ b/src/SMESH_I/CMakeLists.txt @@ -67,7 +67,7 @@ SET(_link_LIBRARIES ${KERNEL_Registry} ${KERNEL_SalomeHDFPersist} ${KERNEL_SalomeLifeCycleCORBA} - ${KERNEL_TOOLSDS} + ${KERNEL_TOOLSDS} ${KERNEL_SalomeGenericObj} ${KERNEL_SalomeIDLKERNEL} ${KERNEL_SALOMELocalTrace} @@ -115,6 +115,8 @@ SET(SMESHEngine_HEADERS SMESH.hxx MG_ADAPT_i.hxx SMESH_Homard_i.hxx + SMESH_SequentialMesh_i.hxx + SMESH_ParallelMesh_i.hxx ) # --- sources --- diff --git a/src/SMESH_I/SMESH_Gen_i.cxx b/src/SMESH_I/SMESH_Gen_i.cxx index 32265c514..0bf885f35 100644 --- a/src/SMESH_I/SMESH_Gen_i.cxx +++ b/src/SMESH_I/SMESH_Gen_i.cxx @@ -107,6 +107,8 @@ #include "SMESH_PythonDump.hxx" #include "SMESH_ControlsDef.hxx" #include +#include +#include // to pass CORBA exception through SMESH_TRY #define SMY_OWN_CATCH catch( SALOME::SALOME_Exception& se ) { throw se; } @@ -560,7 +562,7 @@ SMESH::SMESH_Hypothesis_ptr SMESH_Gen_i::createHypothesis(const char* theHypName */ //============================================================================= -SMESH::SMESH_Mesh_ptr SMESH_Gen_i::createMesh() +SMESH::SMESH_Mesh_ptr SMESH_Gen_i::createMesh(bool parallel /*=false*/) { Unexpect aCatch(SALOME_SalomeException); MESSAGE( "SMESH_Gen_i::createMesh" ); @@ -571,7 +573,11 @@ SMESH::SMESH_Mesh_ptr SMESH_Gen_i::createMesh() SMESH_Mesh_i* meshServant = new SMESH_Mesh_i( GetPOA(), this ); // create a new mesh object MESSAGE("myIsEmbeddedMode " << myIsEmbeddedMode); - meshServant->SetImpl( myGen.CreateMesh( myIsEmbeddedMode )); + if(parallel) { + meshServant->SetImpl( dynamic_cast(myGen.CreateParallelMesh( myIsEmbeddedMode ))); + }else{ + meshServant->SetImpl( dynamic_cast(myGen.CreateMesh( myIsEmbeddedMode ))); + } // activate the CORBA servant of Mesh SMESH::SMESH_Mesh_var mesh = SMESH::SMESH_Mesh::_narrow( meshServant->_this() ); @@ -1198,7 +1204,7 @@ char* SMESH_Gen_i::GetOption(const char* name) SMESH::SMESH_Mesh_ptr SMESH_Gen_i::CreateMesh( GEOM::GEOM_Object_ptr theShapeObject ) { Unexpect aCatch(SALOME_SalomeException); - MESSAGE( "SMESH_Gen_i::CreateMesh" ); + MESSAGE( "SMESH_Gen_i::CreateMesh(GEOM_Object_ptr)" ); // create mesh SMESH::SMESH_Mesh_var mesh = this->createMesh(); // set shape @@ -1221,6 +1227,40 @@ SMESH::SMESH_Mesh_ptr SMESH_Gen_i::CreateMesh( GEOM::GEOM_Object_ptr theShapeObj return mesh._retn(); } +//============================================================================= +/*! + * SMESH_Gen_i::CreateParallelMesh + * + * Create empty parallel mesh on a shape and publish it in the study + */ +//============================================================================= + +SMESH::SMESH_Mesh_ptr SMESH_Gen_i::CreateParallelMesh( GEOM::GEOM_Object_ptr theShapeObject ) +{ + Unexpect aCatch(SALOME_SalomeException); + MESSAGE( "SMESH_Gen_i::CreateParallelMesh" ); + // create mesh + SMESH::SMESH_Mesh_var mesh = this->createMesh(true); + // set shape + SMESH_Mesh_i* meshServant = SMESH::DownCast( mesh ); + ASSERT( meshServant ); + meshServant->SetShape( theShapeObject ); + + // publish mesh in the study + if ( CanPublishInStudy( mesh ) ) { + SALOMEDS::StudyBuilder_var aStudyBuilder = getStudyServant()->NewBuilder(); + aStudyBuilder->NewCommand(); // There is a transaction + SALOMEDS::SObject_wrap aSO = PublishMesh( mesh.in() ); + aStudyBuilder->CommitCommand(); + if ( !aSO->_is_nil() ) { + // Update Python script + TPythonDump(this) << aSO << " = " << this << ".CreateMesh(" << theShapeObject << ")"; + } + } + + return mesh._retn(); +} + //============================================================================= /*! * SMESH_Gen_i::CreateEmptyMesh @@ -1232,7 +1272,7 @@ SMESH::SMESH_Mesh_ptr SMESH_Gen_i::CreateMesh( GEOM::GEOM_Object_ptr theShapeObj SMESH::SMESH_Mesh_ptr SMESH_Gen_i::CreateEmptyMesh() { Unexpect aCatch(SALOME_SalomeException); - MESSAGE( "SMESH_Gen_i::CreateMesh" ); + MESSAGE( "SMESH_Gen_i::CreateEmptyMesh" ); // create mesh SMESH::SMESH_Mesh_var mesh = this->createMesh(); diff --git a/src/SMESH_I/SMESH_Gen_i.hxx b/src/SMESH_I/SMESH_Gen_i.hxx index 822e71f2e..f793d4f1b 100644 --- a/src/SMESH_I/SMESH_Gen_i.hxx +++ b/src/SMESH_I/SMESH_Gen_i.hxx @@ -231,6 +231,9 @@ public: // Create empty mesh on a shape SMESH::SMESH_Mesh_ptr CreateMesh( GEOM::GEOM_Object_ptr theShapeObject ); + // Create empty parallel mesh on a shape + SMESH::SMESH_Mesh_ptr CreateParallelMesh( GEOM::GEOM_Object_ptr theShapeObject ); + // Create empty mesh SMESH::SMESH_Mesh_ptr CreateEmptyMesh(); @@ -631,7 +634,7 @@ private: SMESH::SMESH_Hypothesis_ptr createHypothesis( const char* theHypName, const char* theLibName); // Create empty mesh on shape - SMESH::SMESH_Mesh_ptr createMesh(); + SMESH::SMESH_Mesh_ptr createMesh(bool parallel=false); // Check mesh icon bool isGeomModifIcon( SMESH::SMESH_Mesh_ptr mesh ); diff --git a/src/SMESH_I/SMESH_Mesh_i.cxx b/src/SMESH_I/SMESH_Mesh_i.cxx index 7d4bcb418..d101f14dc 100644 --- a/src/SMESH_I/SMESH_Mesh_i.cxx +++ b/src/SMESH_I/SMESH_Mesh_i.cxx @@ -7037,6 +7037,15 @@ void SMESH_Mesh_i::SetNbThreads(CORBA::Long nbThreads){ _impl->SetNbThreads(nbThreads); } +//============================================================================= +/*! + * \brief Get the number of threads for a parallel computation + */ +//============================================================================= +CORBA::Long SMESH_Mesh_i::GetNbThreads(){ + return _impl->GetNbThreads(); +} + //============================================================================= /*! diff --git a/src/SMESH_I/SMESH_Mesh_i.hxx b/src/SMESH_I/SMESH_Mesh_i.hxx index 45928272d..d9a82e3c6 100644 --- a/src/SMESH_I/SMESH_Mesh_i.hxx +++ b/src/SMESH_I/SMESH_Mesh_i.hxx @@ -673,7 +673,11 @@ private: SMESH::submesh_array_array& theSubMeshOrder, const bool theIsDump); + /*! + * Parallelims informations + */ void SetNbThreads(CORBA::Long nbThreads); + CORBA::Long GetNbThreads(); /*! * \brief Finds concurrent sub-meshes diff --git a/src/SMESH_SWIG/smeshBuilder.py b/src/SMESH_SWIG/smeshBuilder.py index 972a4507c..8d0a81ac5 100644 --- a/src/SMESH_SWIG/smeshBuilder.py +++ b/src/SMESH_SWIG/smeshBuilder.py @@ -462,20 +462,21 @@ class smeshBuilder( SMESH._objref_SMESH_Gen, object ): obj,name = name,obj return Mesh(self, self.geompyD, obj, name) - def ParallelMesh(self, obj, param, nbThreads, name=0): + def ParallelMesh(self, obj, name=0, split_geom=True): """ Create a parallel mesh. Parameters: obj: geometrical object for meshing name: the name for the new mesh. - param: full mesh parameters - nbThreads: Number of threads for parallelisation. + split_geom: If True split the geometry and create the assoicated + sub meshes Returns: an instance of class :class:`ParallelMesh`. """ - return ParallelMesh(self, self.geompyD, obj, param, nbThreads, name) + return ParallelMesh(self, self.geompyD, obj, + split_geom=split_geom, name=name) def RemoveMesh( self, mesh ): """ @@ -1592,7 +1593,7 @@ class Mesh(metaclass = MeshMeta): mesh = 0 editor = 0 - def __init__(self, smeshpyD, geompyD, obj=0, name=0): + def __init__(self, smeshpyD, geompyD, obj=0, name=0, parallel=False): """ Constructor @@ -1625,7 +1626,10 @@ class Mesh(metaclass = MeshMeta): else: geo_name = "%s_%s to mesh"%(self.geom.GetShapeType(), id(self.geom)%100) geompyD.addToStudy( self.geom, geo_name ) - self.SetMesh( self.smeshpyD.CreateMesh(self.geom) ) + if parallel and isinstance(self, ParallelMesh): + self.SetMesh( self.smeshpyD.CreateParallelMesh(self.geom) ) + else: + self.SetMesh( self.smeshpyD.CreateMesh(self.geom) ) elif isinstance(obj, SMESH._objref_SMESH_Mesh): self.SetMesh(obj) @@ -7501,6 +7505,9 @@ class Mesh(metaclass = MeshMeta): 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) @@ -7532,19 +7539,95 @@ def _copy_netgen_param(dim, local_param, global_param): local_param.SetGrowthRate(global_param.GetGrowthRate()) local_param.SetNbThreads(global_param.GetNbThreads()) +def _split_geom(geompyD, geom): + """ + Splitting geometry into n solids and a 2D/1D compound + + Parameters: + geompyD: geomBuilder instance + geom: geometrical object for meshing + + """ + # Splitting geometry into 3D elements and all the 2D/1D into one compound + object_solids = geompyD.ExtractShapes(geom, geompyD.ShapeType["SOLID"], + True) + + solids = [] + isolid = 0 + for solid in object_solids: + isolid += 1 + geompyD.addToStudyInFather( geom, solid, 'Solid_{}'.format(isolid) ) + solids.append(solid) + # If geom is a solid ExtractShapes will return nothin in that case geom is the solids + if isolid == 0: + solids = [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 + + 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 + +class ParallelismSettings: + """ + Defines the parameters for the parallelism of ParallelMesh + """ + def __init__(self, mesh): + """ + Construsctor + + Parameters: + mesh: Instance of ParallelMesh + """ + if not(isinstance(mesh, ParallelMesh)): + raise ValueError("mesh should be a ParallelMesh") + + self._mesh = mesh + + def SetNbThreads(self, nbThreads): + """ + Set the number of threads for multithreading + """ + 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 + """ + return self._mesh.mesh.GetNbThreads() + class ParallelMesh(Mesh): """ Surcharge on Mesh for parallel computation of a mesh """ - - def __init__(self, smeshpyD, geompyD, geom, param, nbThreads, name=0): + def __init__(self, smeshpyD, geompyD, geom, split_geom=True, name=0): """ Create a parallel mesh. Parameters: + smeshpyD: instance of smeshBuilder + geompyD: instance of geomBuilder geom: geometrical object for meshing - param: full mesh parameters - nbThreads: Number of threads for parallelisation. + split_geom: If true will divide geometry on solids and 1D/2D + coumpound and create the associated submeshes name: the name for the new mesh. Returns: @@ -7554,63 +7637,56 @@ class ParallelMesh(Mesh): if not isinstance(geom, geomBuilder.GEOM._objref_GEOM_Object): raise ValueError("geom argument must be a geometry") - if not isinstance(param, NETGENPlugin._objref_NETGENPlugin_Hypothesis): - raise ValueError("param must come from NETGENPlugin") + # 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) - if nbThreads < 1: - raise ValueError("Number of threads must be stricly greater than 1") + if split_geom: + self._all_faces, self._solids = _split_geom(geompyD, geom) - # Splitting geometry into 3D elements and all the 2D/1D into one compound - object_solids = geompyD.ExtractShapes(geom, geompyD.ShapeType["SOLID"], - True) + self.UseExistingSegments() + self.UseExistingFaces() - solids = [] - isolid = 0 - for solid in object_solids: - isolid += 1 - geompyD.addToStudyInFather( geom, solid, 'Solid_{}'.format(isolid) ) - solids.append(solid) + self._algo2d = self.Triangle(geom=self._all_faces, algo="NETGEN_2D") + self._algo3d = [] - 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)) + 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) - # Creating submesh for edges 1D/2D part + self._param = ParallelismSettings(self) - 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') - super(ParallelMesh, self).__init__(smeshpyD, geompyD, geom, name) + def GetParallelismSettings(self): + """ + Return class to set parameters for the parallelism + """ + return self._param - self.mesh.SetNbThreads(nbThreads) + def AddGlobalHypothesis(self, hyp): + """ + Split hypothesis to apply it to all the submeshes: + - the 1D+2D + - each of the 3D solids - self.UseExistingSegments() - self.UseExistingFaces() + Parameters: + hyp: a hypothesis to assign - algo2d = self.Triangle(geom=all_faces, algo="NETGEN_2D") - param2d = algo2d.Parameters() + """ + if not isinstance(hyp, NETGENPlugin._objref_NETGENPlugin_Hypothesis): + raise ValueError("param must come from NETGENPlugin") - _copy_netgen_param(2, param2d, param) + param2d = self._algo2d.Parameters() + _copy_netgen_param(2, param2d, hyp) - for solid_id, solid in enumerate(solids): - name = "Solid_{}".format(solid_id) - self.UseExistingSegments(geom=solid) - self.UseExistingFaces(geom=solid) - algo3d = self.Tetrahedron(geom=solid, algo="NETGEN_3D_Remote") + for algo3d in self._algo3d: param3d = algo3d.Parameters() + _copy_netgen_param(3, param3d, hyp) - _copy_netgen_param(3, param3d, param) pass # End of ParallelMesh diff --git a/src/StdMeshers/StdMeshers_Prism_3D.cxx b/src/StdMeshers/StdMeshers_Prism_3D.cxx index 4caad58b4..005be9abb 100644 --- a/src/StdMeshers/StdMeshers_Prism_3D.cxx +++ b/src/StdMeshers/StdMeshers_Prism_3D.cxx @@ -95,7 +95,7 @@ namespace /*! * \brief Auxiliary mesh */ - struct TmpMesh: public SMESH_Mesh + struct TmpMesh: public SMESH_SequentialMesh { TmpMesh() { _isShapeToMesh = (_id = 0); @@ -5075,7 +5075,7 @@ void StdMeshers_PrismAsBlock::THorizontalEdgeAdaptor::dumpNodes(int nbNodes) con { if (!SALOME::VerbosityActivated()) return; - + // Not bedugged code. Last node is sometimes incorrect const TSideFace* side = mySide; double u = 0; diff --git a/src/StdMeshers/StdMeshers_Prism_3D.hxx b/src/StdMeshers/StdMeshers_Prism_3D.hxx index d2bea83f4..9a7398e91 100644 --- a/src/StdMeshers/StdMeshers_Prism_3D.hxx +++ b/src/StdMeshers/StdMeshers_Prism_3D.hxx @@ -33,6 +33,7 @@ #include "SMESH_Block.hxx" #include "SMESH_Comment.hxx" #include "SMESH_Mesh.hxx" +#include "SMESH_SequentialMesh.hxx" #include "SMESH_MesherHelper.hxx" #include "SMESH_TypeDefs.hxx" #include "SMESH_subMesh.hxx" @@ -117,7 +118,7 @@ namespace Prism_3D // =============================================================== /*! - * \brief Tool analyzing and giving access to a prism geometry + * \brief Tool analyzing and giving access to a prism geometry * treating it like a block, i.e. the four side faces are * emulated by division/uniting of missing/excess faces. * It also manage associations between block sub-shapes and a mesh. @@ -200,7 +201,7 @@ class STDMESHERS_EXPORT StdMeshers_PrismAsBlock: public SMESH_Block */ bool GetLayersTransformation(std::vector & trsf, const Prism_3D::TPrismTopo& prism) const; - + /*! * \brief Return pointer to mesh * \retval SMESH_Mesh - mesh @@ -389,7 +390,7 @@ private: SMESH_ComputeErrorPtr myError; // container of 4 side faces - TSideFace* mySide; + TSideFace* mySide; // node columns for each base edge std::vector< TParam2ColumnMap > myParam2ColumnMaps; // to find a column for a node by edge SMESHDS Index diff --git a/src/StdMeshers/StdMeshers_Projection_2D.cxx b/src/StdMeshers/StdMeshers_Projection_2D.cxx index 589112b47..f3e01a338 100644 --- a/src/StdMeshers/StdMeshers_Projection_2D.cxx +++ b/src/StdMeshers/StdMeshers_Projection_2D.cxx @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -324,7 +325,7 @@ namespace { break; } case TopAbs_EDGE: { - + // Get submeshes of sub-vertices const map< int, SMESH_subMesh * >& subSM = sm->DependsOn(); if ( subSM.size() != 2 ) @@ -711,7 +712,7 @@ namespace { while ( elemIt->more() ) // loop on all mesh faces on srcFace { const SMDS_MeshElement* elem = elemIt->next(); - const int nbN = elem->NbCornerNodes(); + const int nbN = elem->NbCornerNodes(); tgtNodes.resize( nbN ); helper->SetElementsOnShape( false ); for ( int i = 0; i < nbN; ++i ) // loop on nodes of the source element @@ -1096,7 +1097,7 @@ namespace { */ //================================================================================ - struct QuadMesh : public SMESH_Mesh + struct QuadMesh : public SMESH_SequentialMesh { ObjectPool< TriaCoordSys > _traiLCSPool; SMESH_ElementSearcher* _elemSearcher; @@ -1428,7 +1429,7 @@ namespace { // const SMDS_MeshElement* elem = elemIt->next(); // TFaceConn& tgtNodes = newFacesVec[ iFaceSrc++ ]; - // const int nbN = elem->NbCornerNodes(); + // const int nbN = elem->NbCornerNodes(); // tgtNodes.resize( nbN ); // for ( int i = 0; i < nbN; ++i ) // loop on nodes of the source element // { @@ -1442,7 +1443,7 @@ namespace { // { // tgtNodeOrXY.first = srcN_tgtN->second; // tgt node exists // } - // else + // else // { // // find XY of src node within the quadrilateral srcFace // if ( !block.ComputeParameters( SMESH_TNodeXYZ( srcNode ), diff --git a/src/StdMeshers/StdMeshers_QuadFromMedialAxis_1D2D.cxx b/src/StdMeshers/StdMeshers_QuadFromMedialAxis_1D2D.cxx index 5cdf93f2b..a14c51377 100644 --- a/src/StdMeshers/StdMeshers_QuadFromMedialAxis_1D2D.cxx +++ b/src/StdMeshers/StdMeshers_QuadFromMedialAxis_1D2D.cxx @@ -30,6 +30,7 @@ #include "SMESH_Gen.hxx" #include "SMESH_MAT2d.hxx" #include "SMESH_Mesh.hxx" +#include "SMESH_SequentialMesh.hxx" #include "SMESH_MeshEditor.hxx" #include "SMESH_MesherHelper.hxx" #include "SMESH_ProxyMesh.hxx" @@ -144,7 +145,7 @@ public: return true; } }; - + //================================================================================ /*! * \brief Constructor sets algo features @@ -203,7 +204,7 @@ bool StdMeshers_QuadFromMedialAxis_1D2D::CheckHypothesis(SMESH_Mesh& aMe namespace { typedef map< const SMDS_MeshNode*, list< const SMDS_MeshNode* > > TMergeMap; - + //================================================================================ /*! * \brief Sinuous face @@ -236,7 +237,7 @@ namespace /*! * \brief Temporary mesh */ - struct TmpMesh : public SMESH_Mesh + struct TmpMesh : public SMESH_SequentialMesh { TmpMesh() { @@ -506,7 +507,7 @@ namespace theSinuEdges[1].clear(); theShortEdges[0].clear(); theShortEdges[1].clear(); - + vector & allEdges = theSinuFace._edges; const size_t nbEdges = allEdges.size(); if ( nbEdges < 4 && theSinuFace._nbWires == 1 ) @@ -841,7 +842,7 @@ namespace // Find 1D algo to mesh branchEdge - + // look for a most local 1D hyp assigned to the FACE int mostSimpleShape = -1, maxShape = TopAbs_EDGE; TopoDS_Edge edge; @@ -1450,7 +1451,7 @@ namespace nIn = nodeParams.rbegin()->second; else nIn = u2n->second; - + // find position of distant nodes in uvsOut and uvsIn size_t iDistOut, iDistIn; for ( iDistOut = 0; iDistOut < uvsOut.size(); ++iDistOut ) @@ -2151,6 +2152,7 @@ bool StdMeshers_QuadFromMedialAxis_1D2D::computeQuads( SMESH_MesherHelper& theHe bool StdMeshers_QuadFromMedialAxis_1D2D::Compute(SMESH_Mesh& theMesh, const TopoDS_Shape& theShape) { + std::cout << "helper_quad " << theMesh.IsParallel() << std::endl; SMESH_MesherHelper helper( theMesh ); helper.SetSubShape( theShape ); diff --git a/src/StdMeshers/StdMeshers_RadialQuadrangle_1D2D.cxx b/src/StdMeshers/StdMeshers_RadialQuadrangle_1D2D.cxx index ff5906d53..a97f274a2 100644 --- a/src/StdMeshers/StdMeshers_RadialQuadrangle_1D2D.cxx +++ b/src/StdMeshers/StdMeshers_RadialQuadrangle_1D2D.cxx @@ -70,7 +70,7 @@ using namespace std; //======================================================================= //function : StdMeshers_RadialQuadrangle_1D2D -//purpose : +//purpose : //======================================================================= StdMeshers_RadialQuadrangle_1D2D::StdMeshers_RadialQuadrangle_1D2D(int hypId, @@ -103,7 +103,7 @@ StdMeshers_RadialQuadrangle_1D2D::~StdMeshers_RadialQuadrangle_1D2D() //======================================================================= //function : CheckHypothesis -//purpose : +//purpose : //======================================================================= bool StdMeshers_RadialQuadrangle_1D2D::CheckHypothesis @@ -111,7 +111,7 @@ bool StdMeshers_RadialQuadrangle_1D2D::CheckHypothesis const TopoDS_Shape& aShape, SMESH_Hypothesis::Hypothesis_Status& aStatus) { - // check aShape + // check aShape myNbLayerHypo = 0; myDistributionHypo = 0; @@ -271,7 +271,7 @@ namespace sideEdges.splice( sideEdges.end(), edges, edges.begin() ); StdMeshers_FaceSidePtr side; - if ( aMesh ) + if ( aMesh ) side = StdMeshers_FaceSide::New( face, sideEdges, aMesh, /*isFwd=*/true, /*skipMedium=*/ true, helper ); sides.push_back( side ); @@ -355,7 +355,7 @@ namespace } } - int iCirc = deviation2sideInd.rbegin()->second; + int iCirc = deviation2sideInd.rbegin()->second; aCircSide = sides[ iCirc ]; aLinSide1 = sides[( iCirc + 1 ) % sides.size() ]; if ( sides.size() > 2 ) @@ -958,7 +958,7 @@ bool StdMeshers_RadialQuadrangle_1D2D::Compute(SMESH_Mesh& aMesh, centerUV = nodes.back().UV(); } // ------------------------------------------------------------------------------------------ - else // nbSides == 3 + else // nbSides == 3 { // one curve must be a part of ellipse and 2 other curves must be segments of line @@ -1082,7 +1082,7 @@ int StdMeshers_RadialQuadrangle_1D2D::computeLayerPositions(StdMeshers_FaceSideP if ( !TNodeDistributor::GetDistributor(*mesh)->Compute( positions, linSide->Edge(0), *curve, f, l, *mesh, hyp1D )) { - if ( myDistributionHypo ) { // bad hyp assigned + if ( myDistributionHypo ) { // bad hyp assigned return error( TNodeDistributor::GetDistributor(*mesh)->GetComputeError() ); } else { @@ -1090,7 +1090,7 @@ int StdMeshers_RadialQuadrangle_1D2D::computeLayerPositions(StdMeshers_FaceSideP } } } - + if ( positions.empty() ) // try to use nb of layers { if ( !nbLayers ) @@ -1132,7 +1132,7 @@ int StdMeshers_RadialQuadrangle_1D2D::computeLayerPositions(StdMeshers_FaceSideP //======================================================================= //function : Evaluate -//purpose : +//purpose : //======================================================================= bool StdMeshers_RadialQuadrangle_1D2D::Evaluate(SMESH_Mesh& aMesh, @@ -1193,7 +1193,7 @@ bool StdMeshers_RadialQuadrangle_1D2D::Evaluate(SMESH_Mesh& aMesh, return false; } // ------------------------------------------------------------------------------------------ - else // nbSides == 3 + else // nbSides == 3 { if ( !computeLayerPositions(( linSide1->Length() > linSide2->Length() ) ? linSide1 : linSide2, layerPositions )) @@ -1217,7 +1217,7 @@ bool StdMeshers_RadialQuadrangle_1D2D::Evaluate(SMESH_Mesh& aMesh, if ( SMDSEntity_Quad_Edge < (int) nbElems.size() ) nbCircSegments += ( nbElems[ SMDSEntity_Edge ] + nbElems[ SMDSEntity_Quad_Edge ]); } - + smIdType nbQuads = nbCircSegments * ( layerPositions.size() - 1 ); smIdType nbTria = nbCircSegments; smIdType nbNodes = ( nbCircSegments - 1 ) * ( layerPositions.size() - 2 ); diff --git a/test/SMESH_ParallelCompute.py b/test/SMESH_ParallelCompute.py deleted file mode 100644 index fccec1300..000000000 --- a/test/SMESH_ParallelCompute.py +++ /dev/null @@ -1,133 +0,0 @@ -# contains function to compute a mesh in parallel -from platform import java_ver -import sys -from tkinter import W -import salome - -import time - - -salome.salome_init() -import salome_notebook -notebook = salome_notebook.NoteBook() - -### -### GEOM component -### - -import GEOM -from salome.geom import geomBuilder -from salome.smesh import smeshBuilder -import math -import SALOMEDS - -import numpy as np - -geompy = geomBuilder.New() - -smesh = smeshBuilder.New() - - -def build_seq_mesh(nbox, boxsize, offset): - # Create 3D faces - boxes = [] - # First creating all the boxes - for i in range(nbox): - for j in range(nbox): - for k in range(nbox): - - x_orig = i*(boxsize+offset) - y_orig = j*(boxsize+offset) - z_orig = k*(boxsize+offset) - - tmp_box = geompy.MakeBoxDXDYDZ(boxsize, boxsize, boxsize) - - if not i == j == k == 0: - box = geompy.MakeTranslation(tmp_box, x_orig, - y_orig, z_orig) - else: - box = tmp_box - - geompy.addToStudy(box, 'box_{}:{}:{}'.format(i, j, k)) - - boxes.append(box) - - # Create fuse of all boxes - all_boxes = geompy.MakeCompound(boxes) - geompy.addToStudy(all_boxes, 'Compound_1') - - # Removing duplicates faces and edges - all_boxes = geompy.MakeGlueFaces(all_boxes, 1e-07) - geompy.addToStudy(all_boxes, 'Glued_Faces_1') - - all_boxes = geompy.MakeGlueEdges(all_boxes, 1e-07) - geompy.addToStudy(all_boxes, 'rubik_cube') - - - # Building sequetial mesh - print("Creating mesh") - all_box_mesh = smesh.Mesh(all_boxes, "seq_mesh") - - print("Adding algo") - algo3d = all_box_mesh.Tetrahedron(algo=smeshBuilder.NETGEN_1D2D3D) - - netgen_parameters = algo3d.Parameters() - netgen_parameters.SetMaxSize(34.641) - netgen_parameters.SetMinSize(0.141421) - netgen_parameters.SetOptimize(1) - netgen_parameters.SetCheckOverlapping(0) - netgen_parameters.SetCheckChartBoundary(0) - netgen_parameters.SetFineness(5) - netgen_parameters.SetNbSegPerEdge(16*(boxsize//100)) - netgen_parameters.SetNbSegPerRadius(1.5) - netgen_parameters.SetGrowthRate(0.15) - netgen_parameters.SetChordalError(-1) - netgen_parameters.SetChordalErrorEnabled(0) - netgen_parameters.SetUseSurfaceCurvature(1) - netgen_parameters.SetQuadAllowed(0) - netgen_parameters.SetCheckOverlapping(False) - netgen_parameters.SetNbThreads(2) - - return all_boxes, all_box_mesh, netgen_parameters - -def run_test(nbox=2, boxsize=100): - """ Run sequential mesh and parallel version of it - - nbox: NUmber of boxes - boxsize: Size of each box - """ - geom, seq_mesh, netgen_parameters = build_seq_mesh(nbox, boxsize, 0) - - par_mesh = smesh.ParallelMesh(geom, netgen_parameters, 6, name="par_mesh") - - start = time.monotonic() - is_done = seq_mesh.Compute() - assert is_done - stop = time.monotonic() - time_seq = stop-start - - start = time.monotonic() - is_done = par_mesh.Compute() - assert is_done - stop = time.monotonic() - time_par = stop-start - - print(" Tetrahedron: ", seq_mesh.NbTetras(), par_mesh.NbTetras()) - print(" Triangle: ", seq_mesh.NbTriangles(), par_mesh.NbTriangles()) - print(" edge: ", seq_mesh.NbEdges(), par_mesh.NbEdges()) - - assert par_mesh.NbTetras() > 0 - assert par_mesh.NbTriangles() > 0 - assert par_mesh.NbEdges() > 0 - - print("Time elapsed (seq, par): ", time_seq, time_par) - -def main(): - if sys.platform == "win32": - print("Test disabled on Windows") - return - nbox = 2 - boxsize = 100 - run_test(nbox, boxsize) - -main() diff --git a/test/tests.set b/test/tests.set index 0035933fe..b14e7c200 100644 --- a/test/tests.set +++ b/test/tests.set @@ -65,7 +65,6 @@ SET(BAD_TESTS SMESH_create_dual_mesh_adapt.py SMESH_create_dual_mesh_tpipe.py netgen_runner.py - SMESH_ParallelCompute.py ) IF(NOT WIN32) LIST(APPEND BAD_TESTS