Adding parallel meshing (NETGEN only, Linux Only)

See example test/SMESH_ParallelCompute.py for an example
This commit is contained in:
Yoann Audouin 2022-10-21 15:03:25 +02:00
parent fa95110a3b
commit 7292711034
28 changed files with 1577 additions and 385 deletions

View File

@ -52,7 +52,7 @@ The mesh can include the following entities:
* **Node** - a mesh entity defining a position in 3D space with coordinates (x, y, z). * **Node** - a mesh entity defining a position in 3D space with coordinates (x, y, z).
* **Edge** (or segment) - 1D mesh element linking two nodes. * **Edge** (or segment) - 1D mesh element linking two nodes.
* **Face** - 2D mesh element representing a part of surface bound by links between face nodes. A face can be a triangle, quadrangle or polygon. * **Face** - 2D mesh element representing a part of surface bound by links between face nodes. A face can be a triangle, quadrangle or polygon.
* **Volume** - 3D mesh element representing a part of 3D space bound by volume facets. Nodes of a volume describing each facet are defined by the :ref:`connectivity convention <connectivity_page>`. A volume can be a tetrahedron, hexahedron, pentahedron, pyramid, hexagonal prism or polyhedron. * **Volume** - 3D mesh element representing a part of 3D space bound by volume facets. Nodes of a volume describing each facet are defined by the :ref:`connectivity convention <connectivity_page>`. A volume can be a tetrahedron, hexahedron, pentahedron, pyramid, hexagonal or polyhedron.
* **0D** element - mesh element defined by one node. * **0D** element - mesh element defined by one node.
* **Ball** element - discrete mesh element defined by a node and a diameter. * **Ball** element - discrete mesh element defined by a node and a diameter.

View File

@ -899,6 +899,11 @@ module SMESH
*/ */
boolean SetMeshOrder(in submesh_array_array theSubMeshArray); boolean SetMeshOrder(in submesh_array_array theSubMeshArray);
/*!
* \brief Set Number of Threads
*/
void SetNbThreads(in long nbThreads);
/*!
/*! /*!
* Get mesh description * Get mesh description

View File

@ -90,6 +90,9 @@ SET(SMESHimpl_HEADERS
SMESH_SMESH.hxx SMESH_SMESH.hxx
MG_ADAPT.hxx MG_ADAPT.hxx
SMESH_Homard.hxx SMESH_Homard.hxx
SMESH_DriverMesh.hxx
SMESH_DriverShape.hxx
SMESH_MeshLocker.hxx
) )
# --- sources --- # --- sources ---
@ -110,6 +113,9 @@ SET(SMESHimpl_SOURCES
SMESH_MesherHelper.cxx SMESH_MesherHelper.cxx
MG_ADAPT.cxx MG_ADAPT.cxx
SMESH_Homard.cxx SMESH_Homard.cxx
SMESH_DriverMesh.cxx
SMESH_DriverShape.cxx
SMESH_MeshLocker.cxx
) )
# --- rules --- # --- rules ---

View File

@ -277,6 +277,9 @@ public:
// 6 - if algo !NeedDiscreteBoundary() but requires presence of // 6 - if algo !NeedDiscreteBoundary() but requires presence of
// hypotheses of dimension <dim> to generate all-dimensional mesh. // hypotheses of dimension <dim> to generate all-dimensional mesh.
// This info is used not to issue warnings on hiding of lower global algos. // This info is used not to issue warnings on hiding of lower global algos.
//
virtual void setSubMeshesToCompute(SMESH_subMesh * aSubMesh) {SubMeshesToCompute().assign( 1, aSubMesh );}
public: public:
// ================================================================== // ==================================================================

View File

@ -0,0 +1,105 @@
// 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_DriverMesh.cxx
// Author : Yoann AUDOUIN, EDF
// Module : SMESH
//
#include "utilities.h"
#include "SMESH_DriverMesh.hxx"
#include "SMESH_Mesh.hxx"
#include "SMESH_Gen.hxx"
#include <MEDFileMesh.hxx>
#include <MEDCouplingUMesh.hxx>
using namespace MEDCoupling;
/**
* @brief Compares the mesh from two mesh files (MED)
*
* @param mesh_file1 First file
* @param mesh_file2 Second file
* @param mesh_name Name of the mesh in the files
*
* @return true if the mesh within the files are identical
*/
bool SMESH_DriverMesh::diffMEDFile(const std::string mesh_file1, const std::string mesh_file2, const std::string mesh_name){
MEDFileUMesh* medmesh1 = MEDFileUMesh::New(mesh_file1, mesh_name);
MEDFileUMesh* medmesh2 = MEDFileUMesh::New(mesh_file2, mesh_name);
MEDCouplingUMesh *m0_1=medmesh1->getMeshAtLevel(0,false);
MEDCouplingUMesh *m0_2=medmesh2->getMeshAtLevel(0,false);
return m0_1->isEqual(m0_2, 1e-12);
}
std::string getMeshName(std::string mesh_file){
// TODO: Memory leak but desctructor private check with AG
MEDFileUMesh * myMedMesh = MEDFileUMesh::New(mesh_file);
return myMedMesh->getLevel0Mesh()->getName();
}
/**
* @brief Import a mesh from a mesh file (MED) into a SMESH_Mesh object
*
* @param mesh_file the file
* @param aMesh the object
* @param mesh_name the name of the mesh in the file
*
* @return error code
*/
int SMESH_DriverMesh::importMesh(const std::string mesh_file, SMESH_Mesh& aMesh){
// TODO: change that as it depends on the language
std::string mesh_name = getMeshName(mesh_file);
MESSAGE("Importing mesh from " << mesh_file << " mesh " << mesh_name);
int ret = aMesh.MEDToMesh(mesh_file.c_str(), mesh_name.c_str());
return ret;
}
/**
* @brief Export the content of a SMESH_Mesh into a mesh file (MED)
*
* @param mesh_file the file
* @param aMesh the object
* @param mesh_name name of the mesh in the file
*
* @return error code
*/
int SMESH_DriverMesh::exportMesh(const std::string mesh_file, SMESH_Mesh& aMesh, const std::string mesh_name){
MESSAGE("Exporting mesh to " << mesh_file);
aMesh.ExportMED(mesh_file.c_str(), // theFile
mesh_name.c_str(), // theMeshName
false, // theAutoGroups
-1, // theVersion
nullptr, // theMeshPart
true, // theAutoDimension
true, // theAddODOnVertices
1e-8, // theZTolerance
true // theSaveNumbers
);
return true;
}

View File

@ -0,0 +1,47 @@
// Copyright (C) 2007-2021 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_DriverMesh.hxx
// Author : Yoann AUDOUIN, EDF
// Module : SMESH
//
#ifndef _SMESH_DRIVERMESH_HXX_
#define _SMESH_DRIVERMESH_HXX_
#include <string>
#include <cassert>
#include "SMESH_SMESH.hxx"
class SMESH_Mesh;
class SMESH_EXPORT SMESH_DriverMesh{
public:
static bool diffMEDFile(const std::string mesh_file1,
const std::string mesh_file2,
const std::string mesh_name);
static int importMesh(const std::string mesh_file,
SMESH_Mesh& aMesh);
static int exportMesh(const std::string mesh_file,
SMESH_Mesh& aMesh,
const std::string meshName);
};
#endif

View File

@ -0,0 +1,188 @@
// 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_DriverShape.cxx
// Author : Yoann AUDOUIN, EDF
// Module : SMESH
//
#include <utilities.h>
#include <Utils_SALOME_Exception.hxx>
#include "SMESH_DriverShape.hxx"
// step include
#include <STEPControl_Reader.hxx>
#include <STEPControl_Writer.hxx>
#include <Interface_Static.hxx>
// Brep include
#include <BRepTools.hxx>
#include <BRep_Builder.hxx>
//Occ include
#include <TopoDS.hxx>
#ifndef WIN32
#include <boost/filesystem.hpp>
#include <boost/algorithm/string.hpp>
namespace fs = boost::filesystem;
#endif
/**
* @brief Import the content of a shape file (STEP) into a TopDS_Shape object
*
* @param shape_file the shape file
* @param aShape the object
*
* @return error code
*/
int importSTEPShape(const std::string shape_file, TopoDS_Shape& aShape){
MESSAGE("Importing STEP shape from " << shape_file);
STEPControl_Reader reader;
// Forcing Unit in meter
Interface_Static::SetCVal("xstep.cascade.unit","M");
Interface_Static::SetIVal("read.step.ideas", 1);
Interface_Static::SetIVal("read.step.nonmanifold", 1);
IFSelect_ReturnStatus aStat = reader.ReadFile(shape_file.c_str());
if(aStat != IFSelect_RetDone){
throw SALOME_Exception("Reading error for " + shape_file);
}
int NbTrans = reader.TransferRoots();
// There should be only one shape within the file
assert(NbTrans==1);
aShape = reader.OneShape();
return false;
}
/**
* @brief Export the content of a TopoDS_Shape into a shape file (STEP)
*
* @param shape_file the shape file
* @param aShape the object
*
* @return error code
*/
int exportSTEPShape(const std::string shape_file, const TopoDS_Shape& aShape){
MESSAGE("Exporting STEP shape to " << shape_file);
STEPControl_Writer aWriter;
// Forcing Unit in meter
Interface_Static::SetCVal("xstep.cascade.unit","M");
Interface_Static::SetCVal("write.step.unit","M");
Interface_Static::SetIVal("write.step.nonmanifold", 1);
IFSelect_ReturnStatus aStat = aWriter.Transfer(aShape,STEPControl_AsIs);
if(aStat != IFSelect_RetDone){
throw SALOME_Exception("Reading error for " + shape_file);
}
aStat = aWriter.Write(shape_file.c_str());
if(aStat != IFSelect_RetDone){
throw SALOME_Exception("Writing error for " + shape_file);
}
return aStat;
}
/**
* @brief Import the content of a shape file (BREP) into a TopDS_Shape object
*
* @param shape_file the shape file
* @param aShape the object
*
* @return error code
*/
int importBREPShape(const std::string shape_file, TopoDS_Shape& aShape){
MESSAGE("Importing BREP shape from " << shape_file);
BRep_Builder builder;
BRepTools::Read(aShape, shape_file.c_str(), builder);
return false;
}
/**
* @brief Export the content of a TopoDS_Shape into a shape file (BREP)
*
* @param shape_file the shape file
* @param aShape the object
*
* @return error code
*/
int exportBREPShape(const std::string shape_file, const TopoDS_Shape& aShape){
MESSAGE("Exporting BREP shape to " << shape_file);
BRepTools::Write(aShape, shape_file.c_str());
return false;
}
/**
* @brief Import the content of a shape file into a TopDS_Shape object
*
* @param shape_file the shape file
* @param aShape the object
*
* @return error code
*/
int SMESH_DriverShape::importShape(const std::string shape_file, TopoDS_Shape& aShape){
#ifndef WIN32
std::string type = fs::path(shape_file).extension().string();
boost::algorithm::to_lower(type);
if (type == ".brep"){
return importBREPShape(shape_file, aShape);
} else if (type == ".step"){
return importSTEPShape(shape_file, aShape);
} else {
throw SALOME_Exception("Unknow format for importShape: " + type);
}
#else
return 0;
#endif
}
/**
* @brief Import the content of a shape file into a TopDS_Shape object
*
* @param shape_file the shape file
* @param aShape the object
*
* @return error code
*/
int SMESH_DriverShape::exportShape(const std::string shape_file, const TopoDS_Shape& aShape){
#ifndef WIN32
std::string type = fs::path(shape_file).extension().string();
boost::algorithm::to_lower(type);
if (type == ".brep"){
return exportBREPShape(shape_file, aShape);
} else if (type == ".step"){
return exportSTEPShape(shape_file, aShape);
} else {
throw SALOME_Exception("Unknow format for exportShape: " + type);
}
#else
return 0;
#endif
}

View File

@ -0,0 +1,40 @@
// Copyright (C) 2007-2021 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_DriverShape.hxx
// Author : Yoann AUDOUIN, EDF
// Module : SMESH
//
#ifndef _SMESH_DRIVERSHAPE_HXX_
#define _SMESH_DRIVERSHAPE_HXX_
#include <string>
#include <cassert>
#include "SMESH_SMESH.hxx"
class TopoDS_Shape;
class SMESH_EXPORT SMESH_DriverShape{
public:
static int importShape(const std::string shape_file, TopoDS_Shape& aShape);
static int exportShape(const std::string shape_file, const TopoDS_Shape& aShape);
};
#endif

View File

@ -25,11 +25,14 @@
// Author : Paul RASCLE, EDF // Author : Paul RASCLE, EDF
// Module : SMESH // Module : SMESH
// //
//#define CHRONODEF //#define CHRONODEF
//
#ifndef WIN32
#include <boost/asio.hpp>
#endif
#include "SMESH_Gen.hxx" #include "SMESH_Gen.hxx"
#include "SMESH_DriverMesh.hxx"
#include "SMDS_Mesh.hxx" #include "SMDS_Mesh.hxx"
#include "SMDS_MeshElement.hxx" #include "SMDS_MeshElement.hxx"
#include "SMDS_MeshNode.hxx" #include "SMDS_MeshNode.hxx"
@ -47,6 +50,7 @@
#include <TopoDS_Iterator.hxx> #include <TopoDS_Iterator.hxx>
#include "memoire.h" #include "memoire.h"
#include <functional>
#ifdef WIN32 #ifdef WIN32
#include <windows.h> #include <windows.h>
@ -55,6 +59,10 @@
#include <Basics_Utils.hxx> #include <Basics_Utils.hxx>
using namespace std; using namespace std;
#ifndef WIN32
#include <boost/filesystem.hpp>
namespace fs = boost::filesystem;
#endif
// Environment variable separator // Environment variable separator
#ifdef WIN32 #ifdef WIN32
@ -155,6 +163,213 @@ SMESH_Mesh* SMESH_Gen::CreateMesh(bool theIsEmbeddedMode)
return aMesh; return aMesh;
} }
//=============================================================================
/*!
* Algo to run the computation of all the submeshes of a mesh in sequentila
*/
//=============================================================================
bool SMESH_Gen::sequentialComputeSubMeshes(
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)
{
MESSAGE("Compute submeshes sequentialy");
bool ret = true;
SMESH_subMeshIteratorPtr smIt;
SMESH_subMesh *shapeSM = aMesh.GetSubMesh(aShape);
smIt = shapeSM->getDependsOnIterator(includeSelf, !complexShapeFirst);
while ( smIt->more() )
{
SMESH_subMesh* smToCompute = smIt->next();
// do not mesh vertices of a pseudo shape
const TopoDS_Shape& shape = smToCompute->GetSubShape();
const TopAbs_ShapeEnum shapeType = shape.ShapeType();
if ( !aMesh.HasShapeToMesh() && shapeType == TopAbs_VERTEX )
continue;
// check for preview dimension limitations
if ( aShapesId && 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;
}
if (smToCompute->GetComputeState() == SMESH_subMesh::READY_TO_COMPUTE)
{
if (_compute_canceled)
return false;
smToCompute->SetAllowedSubShapes( fillAllowed( shapeSM, aShapeOnly, allowedSubShapes ));
setCurrentSubMesh( smToCompute );
smToCompute->ComputeStateEngine( computeEvent );
setCurrentSubMesh( nullptr );
smToCompute->SetAllowedSubShapes( nullptr );
}
// we check all the sub-meshes here and detect if any of them failed to compute
if (smToCompute->GetComputeState() == SMESH_subMesh::FAILED_TO_COMPUTE &&
( shapeType != TopAbs_EDGE || !SMESH_Algo::isDegenerated( TopoDS::Edge( shape ))))
ret = false;
else if ( aShapesId )
aShapesId->insert( smToCompute->GetId() );
}
//aMesh.GetMeshDS()->Modified();
return ret;
};
//=============================================================================
/*
* compute of a submesh
* This function is passed to the thread pool
*/
//=============================================================================
const std::function<void(SMESH_subMesh*,
SMESH_subMesh::compute_event,
SMESH_subMesh*,
bool,
TopTools_IndexedMapOfShape *,
TSetOfInt*)>
compute_function([] (SMESH_subMesh* sm,
SMESH_subMesh::compute_event event,
SMESH_subMesh *shapeSM,
bool aShapeOnly,
TopTools_IndexedMapOfShape *allowedSubShapes,
TSetOfInt* aShapesId) -> void
{
if (sm->GetComputeState() == SMESH_subMesh::READY_TO_COMPUTE)
{
sm->SetAllowedSubShapes( fillAllowed( shapeSM, aShapeOnly, allowedSubShapes ));
//setCurrentSubMesh( sm );
sm->ComputeStateEngine(event);
//setCurrentSubMesh( nullptr );
sm->SetAllowedSubShapes( nullptr );
}
if ( aShapesId )
aShapesId->insert( sm->GetId() );
});
//=============================================================================
/*!
* Algo to run the computation of all the submeshes of a mesh in parallel
*/
//=============================================================================
bool SMESH_Gen::parallelComputeSubMeshes(
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)
{
#ifdef WIN32
throw SALOME_Exception("ParallelMesh is not working on Windows");
#else
bool ret = true;
SMESH_subMeshIteratorPtr smIt;
SMESH_subMesh *shapeSM = aMesh.GetSubMesh(aShape);
// Pool of thread for computation
// TODO: move when parallelMesh created
aMesh.InitPoolThreads();
aMesh.CreateTmpFolder();
TopAbs_ShapeEnum previousShapeType = TopAbs_VERTEX;
int nbThreads = aMesh.GetNbThreads();
MESSAGE("Compute submeshes with threads: " << nbThreads);
smIt = shapeSM->getDependsOnIterator(includeSelf, !complexShapeFirst);
while ( smIt->more() )
{
SMESH_subMesh* smToCompute = smIt->next();
// 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
if ( !aMesh.HasShapeToMesh() && shapeType == TopAbs_VERTEX )
continue;
if(shapeType==TopAbs_FACE||shapeType==TopAbs_EDGE)
aMesh.SetNbThreads(0);
else
aMesh.SetNbThreads(nbThreads);
if (shapeType != previousShapeType) {
// 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;
}
if(file_name != "")
{
fs::path mesh_file = fs::path(aMesh.tmp_folder) / fs::path(file_name);
SMESH_DriverMesh::exportMesh(mesh_file.string(), aMesh, "MESH");
}
//Resetting threaded pool info
previousShapeType = shapeType;
}
// check for preview dimension limitations
if ( aShapesId && 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,
shapeSM, aShapeOnly, allowedSubShapes,
aShapesId));
}
// Waiting for the thread for Solids to finish
aMesh.wait();
aMesh.GetMeshDS()->Modified();
aMesh.DeleteTmpFolder();
return ret;
#endif
};
//============================================================================= //=============================================================================
/* /*
* Compute a mesh * Compute a mesh
@ -201,57 +416,33 @@ bool SMESH_Gen::Compute(SMESH_Mesh & aMesh,
// =============================================== // ===============================================
// Mesh all the sub-shapes starting from vertices // 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);
smIt = shapeSM->getDependsOnIterator(includeSelf, !complexShapeFirst);
while ( smIt->more() )
{
SMESH_subMesh* smToCompute = smIt->next();
// do not mesh vertices of a pseudo shape
const TopoDS_Shape& shape = smToCompute->GetSubShape();
const TopAbs_ShapeEnum shapeType = shape.ShapeType();
if ( !aMesh.HasShapeToMesh() && shapeType == TopAbs_VERTEX )
continue;
// check for preview dimension limitations
if ( aShapesId && 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;
}
if (smToCompute->GetComputeState() == SMESH_subMesh::READY_TO_COMPUTE)
{
if (_compute_canceled)
return false;
smToCompute->SetAllowedSubShapes( fillAllowed( shapeSM, aShapeOnly, allowedSubShapes ));
setCurrentSubMesh( smToCompute );
smToCompute->ComputeStateEngine( computeEvent );
setCurrentSubMesh( nullptr );
smToCompute->SetAllowedSubShapes( nullptr );
}
// we check all the sub-meshes here and detect if any of them failed to compute
if (smToCompute->GetComputeState() == SMESH_subMesh::FAILED_TO_COMPUTE &&
( shapeType != TopAbs_EDGE || !SMESH_Algo::isDegenerated( TopoDS::Edge( shape ))))
ret = false;
else if ( aShapesId )
aShapesId->insert( smToCompute->GetId() );
}
//aMesh.GetMeshDS()->Modified();
return ret; return ret;
} }
else else
{ {
// ================================================================ // ================================================================
// Apply algos that do NOT require discreteized boundaries // Apply algos that do NOT require discretized boundaries
// ("all-dimensional") and do NOT support sub-meshes, starting from // ("all-dimensional") and do NOT support sub-meshes, starting from
// the most complex shapes and collect sub-meshes with algos that // the most complex shapes and collect sub-meshes with algos that
// DO support sub-meshes // DO support sub-meshes
// ================================================================ // ================================================================
list< SMESH_subMesh* > smWithAlgoSupportingSubmeshes[4]; // for each dim list< SMESH_subMesh* > smWithAlgoSupportingSubmeshes[4]; // for each dim
// map to sort sm with same dim algos according to dim of // map to sort sm with same dim algos according to dim of
@ -448,6 +639,7 @@ bool SMESH_Gen::Compute(SMESH_Mesh & aMesh,
sm->SetAllowedSubShapes( fillAllowed( shapeSM, aShapeOnly, allowedSubShapes )); sm->SetAllowedSubShapes( fillAllowed( shapeSM, aShapeOnly, allowedSubShapes ));
setCurrentSubMesh( sm ); setCurrentSubMesh( sm );
sm->ComputeStateEngine( computeEvent ); sm->ComputeStateEngine( computeEvent );
setCurrentSubMesh( NULL ); setCurrentSubMesh( NULL );
sm->SetAllowedSubShapes( nullptr ); sm->SetAllowedSubShapes( nullptr );
if ( aShapesId ) if ( aShapesId )
@ -460,6 +652,7 @@ bool SMESH_Gen::Compute(SMESH_Mesh & aMesh,
// mesh the rest sub-shapes starting from vertices // mesh the rest sub-shapes starting from vertices
// ----------------------------------------------- // -----------------------------------------------
ret = Compute( aMesh, aShape, aFlags | UPWARD, aDim, aShapesId, allowedSubShapes ); ret = Compute( aMesh, aShape, aFlags | UPWARD, aDim, aShapesId, allowedSubShapes );
} }
MEMOSTAT; MEMOSTAT;

View File

@ -34,6 +34,7 @@
#include "SMESH_Algo.hxx" #include "SMESH_Algo.hxx"
#include "SMESH_ComputeError.hxx" #include "SMESH_ComputeError.hxx"
#include "SMESH_subMesh.hxx"
#include <map> #include <map>
#include <list> #include <list>
@ -41,6 +42,7 @@
#include <vector> #include <vector>
#include <string> #include <string>
#include <TopoDS_Shape.hxx> #include <TopoDS_Shape.hxx>
#include <TopTools_IndexedMapOfShape.hxx> #include <TopTools_IndexedMapOfShape.hxx>
@ -48,7 +50,7 @@ class SMESHDS_Document;
class SMESH_Algo; class SMESH_Algo;
class SMESH_Mesh; class SMESH_Mesh;
class TopoDS_Shape; class TopoDS_Shape;
class SMESH_subMesh;
typedef SMESH_Hypothesis::Hypothesis_Status TAlgoStateErrorName; typedef SMESH_Hypothesis::Hypothesis_Status TAlgoStateErrorName;
@ -167,6 +169,28 @@ public:
private: private:
bool parallelComputeSubMeshes(
SMESH_Mesh & aMesh,
const TopoDS_Shape & aShape,
const ::MeshDimension aDim,
TSetOfInt* aShapesId,
TopTools_IndexedMapOfShape* allowedSubShapes,
SMESH_subMesh::compute_event &computeEvent,
const bool includeSelf,
const bool complexShapeFirst,
const bool aShapeOnly);
bool sequentialComputeSubMeshes(
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);
int _localId; // unique Id of created objects, within SMESH_Gen entity int _localId; // unique Id of created objects, within SMESH_Gen entity
StudyContextStruct* _studyContext; StudyContextStruct* _studyContext;

View File

@ -80,6 +80,11 @@
#include <pthread.h> #include <pthread.h>
#endif #endif
#ifndef WIN32
#include <boost/filesystem.hpp>
namespace fs=boost::filesystem;
#endif
// maximum stored group name length in MED file // maximum stored group name length in MED file
#define MAX_MED_GROUP_NAME_LENGTH 80 #define MAX_MED_GROUP_NAME_LENGTH 80
@ -162,13 +167,11 @@ namespace
#ifndef WIN32 #ifndef WIN32
void deleteMeshDS(SMESHDS_Mesh* meshDS) void deleteMeshDS(SMESHDS_Mesh* meshDS)
{ {
//cout << "deleteMeshDS( " << meshDS << endl;
delete meshDS; delete meshDS;
} }
#else #else
static void* deleteMeshDS(void* meshDS) static void* deleteMeshDS(void* meshDS)
{ {
//cout << "deleteMeshDS( " << meshDS << endl;
SMESHDS_Mesh* m = (SMESHDS_Mesh*)meshDS; SMESHDS_Mesh* m = (SMESHDS_Mesh*)meshDS;
if(m) { if(m) {
delete m; delete m;
@ -229,6 +232,8 @@ SMESH_Mesh::~SMESH_Mesh()
int result=pthread_create(&thread, NULL, deleteMeshDS, (void*)_meshDS); int result=pthread_create(&thread, NULL, deleteMeshDS, (void*)_meshDS);
#endif #endif
} }
if(_pool)
DeletePoolThreads();
} }
//================================================================================ //================================================================================
@ -1756,7 +1761,6 @@ double SMESH_Mesh::GetComputeProgress() const
rate = algo->GetProgressByTic(); rate = algo->GetProgressByTic();
computedCost += algoDoneCost + rate * algoNotDoneCost; computedCost += algoDoneCost + rate * algoNotDoneCost;
} }
// cout << "rate: "<<rate << " algoNotDoneCost: " << algoNotDoneCost << endl;
} }
// get cost of already treated sub-meshes // get cost of already treated sub-meshes
@ -1777,9 +1781,6 @@ double SMESH_Mesh::GetComputeProgress() const
} }
} }
} }
// cout << "Total: " << totalCost
// << " computed: " << computedCost << " progress: " << computedCost / totalCost
// << " nbElems: " << GetMeshDS()->GetMeshInfo().NbElements() << endl;
return computedCost / totalCost; return computedCost / totalCost;
} }
@ -2563,3 +2564,30 @@ void SMESH_Mesh::getAncestorsSubMeshes (const TopoDS_Shape& theSubSha
// sort submeshes according to stored mesh order // sort submeshes according to stored mesh order
SortByMeshOrder( theSubMeshes ); 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
}

View File

@ -48,6 +48,12 @@
#include <vector> #include <vector>
#include <ostream> #include <ostream>
#ifndef WIN32
#include <boost/filesystem.hpp>
#include <boost/asio/thread_pool.hpp>
#endif
#include <boost/thread.hpp>
#ifdef WIN32 #ifdef WIN32
#pragma warning(disable:4251) // Warning DLL Interface ... #pragma warning(disable:4251) // Warning DLL Interface ...
#pragma warning(disable:4290) // Warning Exception ... #pragma warning(disable:4290) // Warning Exception ...
@ -382,6 +388,48 @@ class SMESH_EXPORT SMESH_Mesh
std::ostream& Dump(std::ostream & save); std::ostream& Dump(std::ostream & save);
// 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(){}
bool IsParallel(){return _NbThreads > 0;}
#else
void Lock() {_my_lock.lock();};
void Unlock() {_my_lock.unlock();};
int GetNbThreads(){return _NbThreads;};
void SetNbThreads(long nbThreads){_NbThreads=nbThreads;};
void InitPoolThreads(){_pool = new boost::asio::thread_pool(_NbThreads);};
void DeletePoolThreads(){delete _pool;};
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
private: private:
void exportMEDCommmon(DriverMED_W_SMESHDS_Mesh& myWriter, void exportMEDCommmon(DriverMED_W_SMESHDS_Mesh& myWriter,
@ -428,9 +476,14 @@ protected:
// 2) to forget not loaded mesh data at hyp modification // 2) to forget not loaded mesh data at hyp modification
TCallUp* _callUp; TCallUp* _callUp;
// Mutex for multhitreading write in SMESH_Mesh
#ifndef WIN32
boost::mutex _my_lock;
#endif
int _NbThreads=0;
protected: protected:
SMESH_Mesh(); SMESH_Mesh();
SMESH_Mesh(const SMESH_Mesh&) {}; SMESH_Mesh(const SMESH_Mesh&) {};
}; };
#endif #endif

View File

@ -0,0 +1,46 @@
// 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_MeshLocker.cxx
// Author : Yoann AUDOUIN, EDF
// Module : SMESH
//
#include "SMESH_MeshLocker.hxx"
#include "SMESH_Mesh.hxx"
/*
* When instanced will run the command Lock from a SMESH_Mesh
*/
SMESH_MeshLocker::SMESH_MeshLocker(SMESH_Mesh * aMesh) : _myMesh(aMesh)
{
_myMesh->Lock();
}
/*
* When freed will run the command Unlock from the SMESH_Mesh associated
*/
SMESH_MeshLocker::~SMESH_MeshLocker()
{
_myMesh->Unlock();
}

View File

@ -0,0 +1,45 @@
// 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_Mesh.hxx
// Author : Paul RASCLE, EDF
// Module : SMESH
//
#ifndef _SMESH_MESHLOCKER_HXX_
#define _SMESH_MESHLOCKER_HXX_
#include "SMESH_SMESH.hxx"
class SMESH_Mesh;
class SMESH_EXPORT SMESH_MeshLocker{
public:
SMESH_MeshLocker(SMESH_Mesh * aMesh);
~SMESH_MeshLocker();
protected:
SMESH_MeshLocker();
private:
SMESH_Mesh * _myMesh=nullptr;
};
#endif

View File

@ -3323,9 +3323,7 @@ double SMESH_MesherHelper::GetAngle( const TopoDS_Edge & theE1,
vecRef = du ^ dv; vecRef = du ^ dv;
if ( ++nbLoops > 10 ) if ( ++nbLoops > 10 )
{ {
if (SALOME::VerbosityActivated()) MESSAGE("SMESH_MesherHelper::GetAngle(): Captured in a singularity");
cout << "SMESH_MesherHelper::GetAngle(): Captured in a sigularity" << endl;
return angle; return angle;
} }
} }
@ -5584,7 +5582,6 @@ void SMESH_MesherHelper::WriteShape(const TopoDS_Shape& s)
{ {
const char* name = "/tmp/shape.brep"; const char* name = "/tmp/shape.brep";
BRepTools::Write( s, name ); BRepTools::Write( s, name );
if (SALOME::VerbosityActivated()) MESSAGE(name);
std::cout << name << std::endl;
} }

View File

@ -37,6 +37,7 @@
#include "SMESH_Mesh.hxx" #include "SMESH_Mesh.hxx"
#include "SMESH_MesherHelper.hxx" #include "SMESH_MesherHelper.hxx"
#include "SMESH_subMeshEventListener.hxx" #include "SMESH_subMeshEventListener.hxx"
#include "SMESH_MeshLocker.hxx"
#include "utilities.h" #include "utilities.h"
#include "Basics_Utils.hxx" #include "Basics_Utils.hxx"
@ -62,7 +63,7 @@ using namespace std;
#ifdef _DEBUG_ #ifdef _DEBUG_
// enable printing algo + shape id + hypo used while meshing // enable printing algo + shape id + hypo used while meshing
//#define PRINT_WHO_COMPUTE_WHAT #define PRINT_WHO_COMPUTE_WHAT
#endif #endif
//============================================================================= //=============================================================================
@ -256,7 +257,7 @@ bool SMESH_subMesh::IsMeshComputed() const
TopExp_Explorer exp( _subShape, (TopAbs_ShapeEnum) type ); TopExp_Explorer exp( _subShape, (TopAbs_ShapeEnum) type );
for ( ; exp.More(); exp.Next() ) for ( ; exp.More(); exp.Next() )
{ {
if ( SMESHDS_SubMesh * smDS = meshDS->MeshElements( exp.Current() )) if ( SMESHDS_SubMesh * smDS = meshDS->MeshElements( exp.Current() ) )
{ {
bool computed = (dim > 0) ? smDS->NbElements() : smDS->NbNodes(); bool computed = (dim > 0) ? smDS->NbElements() : smDS->NbNodes();
if ( computed ) if ( computed )
@ -1392,6 +1393,7 @@ bool SMESH_subMesh::ComputeStateEngine(compute_event event)
else if (( event == COMPUTE || event == COMPUTE_SUBMESH ) else if (( event == COMPUTE || event == COMPUTE_SUBMESH )
&& !_alwaysComputed ) && !_alwaysComputed )
{ {
SMESH_MeshLocker myLocker(_father);
const TopoDS_Vertex & V = TopoDS::Vertex( _subShape ); const TopoDS_Vertex & V = TopoDS::Vertex( _subShape );
gp_Pnt P = BRep_Tool::Pnt(V); gp_Pnt P = BRep_Tool::Pnt(V);
if ( SMDS_MeshNode * n = _father->GetMeshDS()->AddNode(P.X(), P.Y(), P.Z()) ) { if ( SMDS_MeshNode * n = _father->GetMeshDS()->AddNode(P.X(), P.Y(), P.Z()) ) {
@ -1511,9 +1513,10 @@ bool SMESH_subMesh::ComputeStateEngine(compute_event event)
break; break;
} }
TopoDS_Shape shape = _subShape; TopoDS_Shape shape = _subShape;
algo->SubMeshesToCompute().assign( 1, this ); algo->setSubMeshesToCompute(this);
// check submeshes needed // check submeshes needed
if (_father->HasShapeToMesh() ) { // In parallel there would be no submesh to check
if (_father->HasShapeToMesh() && !_father->IsParallel()) {
bool subComputed = false, subFailed = false; bool subComputed = false, subFailed = false;
if (!algo->OnlyUnaryInput()) { if (!algo->OnlyUnaryInput()) {
// --- commented for bos#22320 to compute all sub-shapes at once if possible; // --- commented for bos#22320 to compute all sub-shapes at once if possible;
@ -1575,7 +1578,7 @@ bool SMESH_subMesh::ComputeStateEngine(compute_event event)
_computeError = SMESH_ComputeError::Worst( _computeError, algo->GetComputeError() ); _computeError = SMESH_ComputeError::Worst( _computeError, algo->GetComputeError() );
} }
catch ( ::SMESH_ComputeError& comperr ) { catch ( ::SMESH_ComputeError& comperr ) {
cout << " SMESH_ComputeError caught" << endl; MESSAGE(" SMESH_ComputeError caught");
if ( !_computeError ) _computeError = SMESH_ComputeError::New(); if ( !_computeError ) _computeError = SMESH_ComputeError::New();
*_computeError = comperr; *_computeError = comperr;
} }
@ -1642,6 +1645,7 @@ bool SMESH_subMesh::ComputeStateEngine(compute_event event)
#ifdef PRINT_WHO_COMPUTE_WHAT #ifdef PRINT_WHO_COMPUTE_WHAT
for (subS.ReInit(); subS.More(); subS.Next()) for (subS.ReInit(); subS.More(); subS.Next())
{ {
SMESH_MeshLocker myLocker(_father);
const std::list <const SMESHDS_Hypothesis *> & hyps = const std::list <const SMESHDS_Hypothesis *> & hyps =
_algo->GetUsedHypothesis( *_father, _subShape ); _algo->GetUsedHypothesis( *_father, _subShape );
SMESH_Comment hypStr; SMESH_Comment hypStr;

View File

@ -79,6 +79,7 @@ class SMESH_EXPORT SMESH_subMesh
SMESH_subMesh *GetFirstToCompute(); SMESH_subMesh *GetFirstToCompute();
SMESH_Algo* GetAlgo() const; SMESH_Algo* GetAlgo() const;
SMESH_Algo* CopyAlgo() const;
const std::map < int, SMESH_subMesh * >& DependsOn(); const std::map < int, SMESH_subMesh * >& DependsOn();
bool DependsOn( const SMESH_subMesh* other ) const; bool DependsOn( const SMESH_subMesh* other ) const;

View File

@ -2216,7 +2216,8 @@ bool _pyMesh::NeedMeshAccess( const Handle(_pyCommand)& theCommand )
"GetElemNode","IsMediumNode","IsMediumNodeOfAnyElem","ElemNbEdges","ElemNbFaces", "GetElemNode","IsMediumNode","IsMediumNodeOfAnyElem","ElemNbEdges","ElemNbFaces",
"GetElemFaceNodes", "GetFaceNormal", "FindElementByNodes", "GetElemFaceNodes", "GetFaceNormal", "FindElementByNodes",
"IsPoly","IsQuadratic","BaryCenter","GetHypothesisList", "SetAutoColor", "GetAutoColor", "IsPoly","IsQuadratic","BaryCenter","GetHypothesisList", "SetAutoColor", "GetAutoColor",
"Clear", "ConvertToStandalone", "GetMeshOrder", "SetMeshOrder" "Clear", "ConvertToStandalone", "GetMeshOrder", "SetMeshOrder",
"SetNbThreads"
,"" }; // <- mark of end ,"" }; // <- mark of end
sameMethods.Insert( names ); sameMethods.Insert( names );
} }

View File

@ -6643,8 +6643,6 @@ CORBA::Boolean SMESH_Gen_i::IsApplicable ( const char* theAlgoType,
SMESH_CATCH( SMESH::doNothing ); SMESH_CATCH( SMESH::doNothing );
if (SALOME::VerbosityActivated()) MESSAGE("SMESH_Gen_i::IsApplicable(): exception in " << ( theAlgoType ? theAlgoType : ""));
cout << "SMESH_Gen_i::IsApplicable(): exception in " << ( theAlgoType ? theAlgoType : "") << endl;
return true; return true;
} }

View File

@ -7028,6 +7028,16 @@ TListOfListOfInt SMESH_Mesh_i::findConcurrentSubMeshes()
return res; return res;
} }
//=============================================================================
/*!
* \brief Set the number of threads for a parallel computation
*/
//=============================================================================
void SMESH_Mesh_i::SetNbThreads(CORBA::Long nbThreads){
_impl->SetNbThreads(nbThreads);
}
//============================================================================= //=============================================================================
/*! /*!
* \brief Convert submesh ids into submesh interfaces * \brief Convert submesh ids into submesh interfaces

View File

@ -673,6 +673,8 @@ private:
SMESH::submesh_array_array& theSubMeshOrder, SMESH::submesh_array_array& theSubMeshOrder,
const bool theIsDump); const bool theIsDump);
void SetNbThreads(CORBA::Long nbThreads);
/*! /*!
* \brief Finds concurrent sub-meshes * \brief Finds concurrent sub-meshes
*/ */

View File

@ -462,6 +462,21 @@ class smeshBuilder( SMESH._objref_SMESH_Gen, object ):
obj,name = name,obj obj,name = name,obj
return Mesh(self, self.geompyD, obj, name) return Mesh(self, self.geompyD, obj, name)
def ParallelMesh(self, obj, param, nbThreads, name=0):
"""
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.
Returns:
an instance of class :class:`ParallelMesh`.
"""
return ParallelMesh(self, self.geompyD, obj, param, nbThreads, name)
def RemoveMesh( self, mesh ): def RemoveMesh( self, mesh ):
""" """
Delete a mesh Delete a mesh
@ -1863,7 +1878,6 @@ class Mesh(metaclass = MeshMeta):
geom = self.geom geom = self.geom
return self.smeshpyD.Evaluate(self.mesh, geom) return self.smeshpyD.Evaluate(self.mesh, geom)
def Compute(self, geom=0, discardModifs=False, refresh=False): def Compute(self, geom=0, discardModifs=False, refresh=False):
""" """
Compute the mesh and return the status of the computation Compute the mesh and return the status of the computation
@ -7487,6 +7501,121 @@ class Mesh(metaclass = MeshMeta):
pass # end of Mesh class pass # end of Mesh class
def _copy_netgen_param(dim, local_param, global_param):
if dim==1:
#TODO: Try to identify why we need to substract 1
local_param.NumberOfSegments(int(global_param.GetNbSegPerEdge())-1)
elif dim==2:
local_param.SetMaxSize(global_param.GetMaxSize())
local_param.SetMinSize(global_param.GetMinSize())
local_param.SetOptimize(global_param.GetOptimize())
local_param.SetFineness(global_param.GetFineness())
local_param.SetNbSegPerEdge(global_param.GetNbSegPerEdge())
local_param.SetNbSegPerRadius(global_param.GetNbSegPerRadius())
local_param.SetGrowthRate(global_param.GetGrowthRate()*0.9)
local_param.SetChordalError(global_param.GetChordalError())
local_param.SetChordalErrorEnabled(global_param.GetChordalErrorEnabled())
local_param.SetUseSurfaceCurvature(global_param.GetUseSurfaceCurvature())
local_param.SetUseDelauney(global_param.GetUseDelauney())
local_param.SetQuadAllowed(global_param.GetQuadAllowed())
local_param.SetWorstElemMeasure(global_param.GetWorstElemMeasure())
local_param.SetCheckChartBoundary(global_param.GetCheckChartBoundary())
local_param.SetNbThreads(global_param.GetNbThreads())
else:
local_param.SetMaxSize(global_param.GetMaxSize())
local_param.SetMinSize(global_param.GetMinSize())
local_param.SetOptimize(global_param.GetOptimize())
local_param.SetCheckOverlapping(global_param.GetCheckOverlapping())
local_param.SetCheckChartBoundary(global_param.GetCheckChartBoundary())
local_param.SetFineness(global_param.GetFineness())
local_param.SetNbSegPerEdge(global_param.GetNbSegPerEdge())
local_param.SetNbSegPerRadius(global_param.GetNbSegPerRadius())
local_param.SetGrowthRate(global_param.GetGrowthRate())
local_param.SetNbThreads(global_param.GetNbThreads())
class ParallelMesh(Mesh):
"""
Surcharge on Mesh for parallel computation of a mesh
"""
def __init__(self, smeshpyD, geompyD, geom, param, nbThreads, name=0):
"""
Create a parallel mesh.
Parameters:
geom: geometrical object for meshing
param: full mesh parameters
nbThreads: Number of threads for parallelisation.
name: the name for the new mesh.
Returns:
an instance of class :class:`ParallelMesh`.
"""
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")
if nbThreads < 1:
raise ValueError("Number of threads must be stricly greater than 1")
# 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)
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')
super(ParallelMesh, self).__init__(smeshpyD, geompyD, geom, name)
self.mesh.SetNbThreads(nbThreads)
self.UseExistingSegments()
self.UseExistingFaces()
algo2d = self.Triangle(geom=all_faces, algo="NETGEN_2D")
param2d = algo2d.Parameters()
_copy_netgen_param(2, param2d, param)
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")
param3d = algo3d.Parameters()
_copy_netgen_param(3, param3d, param)
pass # End of ParallelMesh
class meshProxy(SMESH._objref_SMESH_Mesh): class meshProxy(SMESH._objref_SMESH_Mesh):
""" """
Private class used to compensate change of CORBA API of SMESH_Mesh for backward compatibility Private class used to compensate change of CORBA API of SMESH_Mesh for backward compatibility

View File

@ -0,0 +1,133 @@
# 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()

132
test/netgen_runner.py Normal file
View File

@ -0,0 +1,132 @@
#!/usr/bin/env python
import sys
import salome
salome.salome_init()
from os import path
import tempfile
import subprocess
import GEOM, SMESH, SALOMEDS
from salome.geom import geomBuilder
from salome.smesh import smeshBuilder
import math
smesh = smeshBuilder.New()
geompy = geomBuilder.New()
import medcoupling as mc
def create_param_file(param_file):
""" Create a parameter file for runner """
param = """1
34.64
0.14
16
0.15
1.5
0
0
1
5
1
1
-1
3
3
0.2
2
1
0
0
2
2
0
0
0
0"""
with open(param_file, "w") as ffile:
ffile.write(param)
def test_netgen3d():
""" Test netgen3d mesher """
# Building geometry
box = geompy.MakeBoxDXDYDZ(200, 200, 200)
geompy.ExtractShapes(box, geompy.ShapeType["FACE"], True)
groupe_1 = geompy.CreateGroup(box, geompy.ShapeType["FACE"])
geompy.UnionIDs(groupe_1, [3, 13, 23, 27, 31, 33])
[_, _, _, _, _, _, groupe_1] = geompy.GetExistingSubObjects(box, False)
# Creating 2D mesh
netgen_2d_parameters_1 = smesh.CreateHypothesisByAverageLength(
'NETGEN_Parameters_2D', 'NETGENEngine', 34.641, 0)
mesh_2d = smesh.Mesh(groupe_1, 'Maillage_1')
mesh_2d.AddHypothesis(groupe_1, netgen_2d_parameters_1)
mesh_2d.Triangle(algo=smeshBuilder.NETGEN_1D2D)
is_done = mesh_2d.Compute()
assert is_done
smesh.SetName(mesh_2d, 'Maillage_1')
with tempfile.TemporaryDirectory() as tmp_dir:
mesh_file = path.join(tmp_dir, "mesh.med")
shape_file = path.join(tmp_dir, "shape.step")
param_file = path.join(tmp_dir, "param.txt")
output_mesh = path.join(tmp_dir, "mesh3D.med")
print("Running in folder: ", tmp_dir)
create_param_file(param_file)
mesh_2d.ExportMED(mesh_file, 0, 41, 1, mesh_2d, 1, [], '', -1, 1)
geompy.ExportSTEP(box, shape_file, GEOM.LU_METER)
runner = path.join("${NETGENPLUGIN_ROOT_DIR}",
"bin",
"salome",
"NETGENPlugin_Runner")
if sys.platform == 'win32':
runner += ".exe"
cmd = "{runner} NETGEN3D {mesh_file} {shape_file} "\
"{param_file} NONE NONE {output_mesh}"\
.format(runner=runner,
mesh_file=mesh_file,
shape_file=shape_file,
param_file=param_file,
output_mesh=output_mesh)
print(cmd)
subprocess.check_call(cmd, shell=True)
mesh_read = mc.ReadUMeshFromFile(output_mesh, "MESH", 0)
nb_tetras = mesh_read.getNumberOfCellsWithType(mc.NORM_TETRA4)
nb_points = mesh_read.getNumberOfNodes()
mesh_read = mc.ReadUMeshFromFile(output_mesh, "MESH", -1)
nb_triangles = mesh_read.getNumberOfCellsWithType(mc.NORM_TRI3)
mesh_read = mc.ReadUMeshFromFile(output_mesh, "MESH", -2)
nb_segments = mesh_read.getNumberOfCellsWithType(mc.NORM_SEG2)
print("Nb Tetras:", nb_tetras)
print("Nb Triangles:", nb_triangles)
print("Nb Segments:", nb_segments)
print("Nb Points:", nb_points)
assert nb_points > 0
assert nb_segments > 0
assert nb_triangles > 0
assert nb_tetras > 0
if __name__ == "__main__":
if sys.platform == "win32":
print("Disabled on windows")
sys.exit(0)
test_netgen3d()

View File

@ -64,6 +64,8 @@ SET(BAD_TESTS
SMESH_test4.py SMESH_test4.py
SMESH_create_dual_mesh_adapt.py SMESH_create_dual_mesh_adapt.py
SMESH_create_dual_mesh_tpipe.py SMESH_create_dual_mesh_tpipe.py
netgen_runner.py
SMESH_ParallelCompute.py
) )
IF(NOT WIN32) IF(NOT WIN32)
LIST(APPEND BAD_TESTS LIST(APPEND BAD_TESTS