From 84f6ecdcf3dcf7a1c5c9e3a756983213055da213 Mon Sep 17 00:00:00 2001 From: cconopoima Date: Tue, 7 Nov 2023 20:16:03 +0000 Subject: [PATCH] [bos #38046] [EDF] (2023-T3) Stand alone and Remote versions for GMSH meshers. Get created elements and nodes to write to binary file. Remote version. Fill premeshed faces considering element orientation. Dumping created volumetric elements considering new nodes and old nodes from faces. Intermermedial commit. Code clean up. Final tested version of GMSH_3D_Remote mesher. --- idl/GMSHPlugin_Algorithm.idl | 7 + src/GMSHPlugin/CMakeLists.txt | 18 +- src/GMSHPlugin/GMSHPluginBuilder.py | 22 +- src/GMSHPlugin/GMSHPlugin_GMSH_3D.cxx | 2 - src/GMSHPlugin/GMSHPlugin_GMSH_3D.hxx | 1 + src/GMSHPlugin/GMSHPlugin_GMSH_3D_Remote.cxx | 379 ++++++++++++++++++ src/GMSHPlugin/GMSHPlugin_GMSH_3D_Remote.hxx | 62 +++ .../GMSHPlugin_GMSH_3D_Remote_i.cxx | 78 ++++ .../GMSHPlugin_GMSH_3D_Remote_i.hxx | 58 +++ src/GMSHPlugin/GMSHPlugin_GMSH_3D_SA.cxx | 331 +++++++++++++++ src/GMSHPlugin/GMSHPlugin_GMSH_3D_SA.hxx | 57 +++ src/GMSHPlugin/GMSHPlugin_Mesher.cxx | 328 ++++++++++++++- src/GMSHPlugin/GMSHPlugin_Mesher.hxx | 18 +- src/GMSHPlugin/GMSHPlugin_Runner_main.cxx | 109 +++++ src/GMSHPlugin/GMSHPlugin_i.cxx | 3 + 15 files changed, 1449 insertions(+), 24 deletions(-) create mode 100644 src/GMSHPlugin/GMSHPlugin_GMSH_3D_Remote.cxx create mode 100644 src/GMSHPlugin/GMSHPlugin_GMSH_3D_Remote.hxx create mode 100644 src/GMSHPlugin/GMSHPlugin_GMSH_3D_Remote_i.cxx create mode 100644 src/GMSHPlugin/GMSHPlugin_GMSH_3D_Remote_i.hxx create mode 100644 src/GMSHPlugin/GMSHPlugin_GMSH_3D_SA.cxx create mode 100644 src/GMSHPlugin/GMSHPlugin_GMSH_3D_SA.hxx create mode 100644 src/GMSHPlugin/GMSHPlugin_Runner_main.cxx diff --git a/idl/GMSHPlugin_Algorithm.idl b/idl/GMSHPlugin_Algorithm.idl index cf0b5c3..be7e14e 100644 --- a/idl/GMSHPlugin_Algorithm.idl +++ b/idl/GMSHPlugin_Algorithm.idl @@ -46,6 +46,13 @@ module GMSHPlugin { }; + /*! + * GMSHPlugin_GMSH: interface of "Gmsh" algorithm + */ + interface GMSHPlugin_GMSH_3D_Remote : GMSHPlugin::GMSHPlugin_GMSH_3D + { + }; + /*! * GMSHPlugin_GMSH_2D: interface of "Gmsh_2D" algorithm */ diff --git a/src/GMSHPlugin/CMakeLists.txt b/src/GMSHPlugin/CMakeLists.txt index 92097da..4c6cabb 100644 --- a/src/GMSHPlugin/CMakeLists.txt +++ b/src/GMSHPlugin/CMakeLists.txt @@ -29,10 +29,12 @@ INCLUDE_DIRECTORIES( ${MEDCOUPLING_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS} ${OMNIORB_INCLUDE_DIR} - ${GMSH_INCLUDE_DIRS} ${PROJECT_BINARY_DIR}/idl ) +#Avoid compilation warnings from gmsh headers +INCLUDE_DIRECTORIES( SYSTEM ${GMSH_INCLUDE_DIRS} ) + # additional preprocessor / compiler flags ADD_DEFINITIONS( ${OMNIORB_DEFINITIONS} @@ -68,6 +70,9 @@ SET(GMSHEngine_HEADERS GMSHPlugin_GMSH_2D.hxx GMSHPlugin_GMSH_2D_i.hxx GMSHPlugin_GMSH_3D.hxx + GMSHPlugin_GMSH_3D_SA.hxx + GMSHPlugin_GMSH_3D_Remote.hxx + GMSHPlugin_GMSH_3D_Remote_i.hxx GMSHPlugin_GMSH_3D_i.cxx GMSHPlugin_GMSH.hxx GMSHPlugin_GMSH_i.hxx @@ -88,6 +93,9 @@ SET(GMSHEngine_SOURCES GMSHPlugin_GMSH_2D.cxx GMSHPlugin_GMSH_2D_i.cxx GMSHPlugin_GMSH_3D.cxx + GMSHPlugin_GMSH_3D_SA.cxx + GMSHPlugin_GMSH_3D_Remote.cxx + GMSHPlugin_GMSH_3D_Remote_i.cxx GMSHPlugin_GMSH_3D_i.cxx GMSHPlugin_GMSH.cxx GMSHPlugin_GMSH_i.cxx @@ -101,6 +109,10 @@ SET(GMSHEngine_SOURCES GMSHPlugin_i.cxx ) +SET(GmshRunner_SOURCES + GMSHPlugin_Runner_main.cxx +) + # --- scripts --- # scripts / static @@ -115,6 +127,10 @@ ADD_LIBRARY(GMSHEngine ${GMSHEngine_SOURCES}) TARGET_LINK_LIBRARIES(GMSHEngine ${_link_LIBRARIES} ) INSTALL(TARGETS GMSHEngine EXPORT ${PROJECT_NAME}TargetGroup DESTINATION ${SALOME_INSTALL_LIBS}) +ADD_EXECUTABLE(GMSHPlugin_Runner ${GmshRunner_SOURCES}) +TARGET_LINK_LIBRARIES(GMSHPlugin_Runner ${_link_LIBRARIES} GMSHEngine ) +INSTALL(TARGETS GMSHPlugin_Runner EXPORT ${PROJECT_NAME}TargetGroup DESTINATION ${SALOME_INSTALL_BINS}) + INSTALL(FILES ${GMSHEngine_HEADERS} DESTINATION ${SALOME_INSTALL_HEADERS}) SALOME_INSTALL_SCRIPTS("${_bin_SCRIPTS}" ${SALOME_INSTALL_PYTHON}/salome/GMSHPlugin) diff --git a/src/GMSHPlugin/GMSHPluginBuilder.py b/src/GMSHPlugin/GMSHPluginBuilder.py index 80dfbad..4717d38 100644 --- a/src/GMSHPlugin/GMSHPluginBuilder.py +++ b/src/GMSHPlugin/GMSHPluginBuilder.py @@ -37,6 +37,7 @@ except ImportError: GMSH = "GMSH" GMSH_3D = "GMSH_3D" GMSH_2D = "GMSH_2D" +GMSH_3D_Remote = "GMSH_3D_Remote" ## Base of all GMSH algorithms. # @@ -60,6 +61,8 @@ class GMSH_Algorithm(Mesh_Algorithm): hypType = "GMSH_Parameters" elif self.algoType == GMSH_3D: hypType = "GMSH_Parameters_3D" + elif self.algoType == GMSH_3D_Remote: + hypType = "GMSH_Parameters_3D" if self.params and self.params.GetName() != hypType: self.mesh.RemoveHypothesis( self.params, self.geom ) @@ -79,9 +82,18 @@ class GMSH_2D_Algorithm(GMSH_Algorithm): class GMSH_3D_Algorithm(GMSH_Algorithm): - meshMethod = "Tetrahedron" - algoType = GMSH_3D + meshMethod = "Tetrahedron" + algoType = GMSH_3D + + ## Private constructor. + def __init__(self, mesh, geom=0): + GMSH_Algorithm.__init__(self, mesh, geom) - ## Private constructor. - def __init__(self, mesh, geom=0): - GMSH_Algorithm.__init__(self, mesh, geom) + +class GMSH_3D_Remote_Algorithm(GMSH_Algorithm): + meshMethod = "Tetrahedron" + algoType = GMSH_3D_Remote + + ## Private constructor. + def __init__(self, mesh, geom=0): + GMSH_Algorithm.__init__(self, mesh, geom) \ No newline at end of file diff --git a/src/GMSHPlugin/GMSHPlugin_GMSH_3D.cxx b/src/GMSHPlugin/GMSHPlugin_GMSH_3D.cxx index b132c54..35a4406 100644 --- a/src/GMSHPlugin/GMSHPlugin_GMSH_3D.cxx +++ b/src/GMSHPlugin/GMSHPlugin_GMSH_3D.cxx @@ -20,7 +20,6 @@ // #include "GMSHPlugin_GMSH_3D.hxx" #include "GMSHPlugin_Hypothesis_2D.hxx" -#include "GMSHPlugin_Mesher.hxx" #include #include @@ -74,7 +73,6 @@ bool GMSHPlugin_GMSH_3D::CheckHypothesis SMESH_Hypothesis::Hypothesis_Status& aStatus) { MESSAGE("GMSHPlugin_GMSH::CheckHypothesis"); - _hypothesis = NULL; const list& hyps = GetUsedHypothesis(aMesh, aShape); diff --git a/src/GMSHPlugin/GMSHPlugin_GMSH_3D.hxx b/src/GMSHPlugin/GMSHPlugin_GMSH_3D.hxx index c659ae9..f41d546 100644 --- a/src/GMSHPlugin/GMSHPlugin_GMSH_3D.hxx +++ b/src/GMSHPlugin/GMSHPlugin_GMSH_3D.hxx @@ -22,6 +22,7 @@ #define _GMSHPlugin_GMSH_3D_HXX_ #include "GMSHPlugin_Defs.hxx" +#include "GMSHPlugin_Mesher.hxx" #include "SMESH_Algo.hxx" #include "SMESH_subMesh.hxx" diff --git a/src/GMSHPlugin/GMSHPlugin_GMSH_3D_Remote.cxx b/src/GMSHPlugin/GMSHPlugin_GMSH_3D_Remote.cxx new file mode 100644 index 0000000..7635a9d --- /dev/null +++ b/src/GMSHPlugin/GMSHPlugin_GMSH_3D_Remote.cxx @@ -0,0 +1,379 @@ +// Copyright (C) 2007-2023 CEA, EDF, 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 : GMSHPlugin_GMSH_3D_Remote.hxx +// Created : 09 Septembre 2023 +// Author : Cesar Conopoima (OCC) +// Project : SALOME +//============================================================================= +// +// + +#include "GMSHPlugin_GMSH_3D_Remote.hxx" +#include "GMSHPlugin_Hypothesis.hxx" +#include "Utils_SALOME_Exception.hxx" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#ifdef WIN32 +#include +namespace fs = std::filesystem; +#else +#include +namespace fs = boost::filesystem; +#endif + +//============================================================================= +/*! + * Constructor + */ +//============================================================================= + +GMSHPlugin_GMSH_3D_Remote::GMSHPlugin_GMSH_3D_Remote(int hypId, SMESH_Gen * gen) + : GMSHPlugin_GMSH_3D(hypId, gen) +{ + _name = "GMSH_3D_Remote"; +} + +//============================================================================= +/*! + * Destructor + */ +//============================================================================= + +GMSHPlugin_GMSH_3D_Remote::~GMSHPlugin_GMSH_3D_Remote() +{ +} + +/** + * @brief Fill the structure netgen_param with the information from the hypothesis + * + * @param param_file name of the file to saven the gmsh parameter + * @param hyp the hypothesis + */ +void GMSHPlugin_GMSH_3D_Remote::exportGmshParams( const std::string param_file, const SMESHDS_Hypothesis* hyp ) +{ + std::ofstream myfile(param_file); + if ( const GMSHPlugin_Hypothesis* gmshHypo = dynamic_cast(hyp) ) + { + myfile << 1 << std::endl; // Mark existence of correct hypothesis + myfile << (int) gmshHypo->Get2DAlgo() << std::endl; + myfile << (int) gmshHypo->Get3DAlgo() << std::endl; + myfile << (int) gmshHypo->GetRecomb2DAlgo() << std::endl; + myfile << (int) gmshHypo->GetRecombineAll() << std::endl; + myfile << (int) gmshHypo->GetSubdivAlgo() << std::endl; + myfile << (int) gmshHypo->GetRemeshAlgo() << std::endl; + myfile << (double) gmshHypo->GetSmouthSteps() << std::endl; + myfile << (double) gmshHypo->GetSizeFactor() << std::endl; +#if GMSH_MAJOR_VERSION >=4 && GMSH_MINOR_VERSION >=10 + myfile << (double) gmshHypo->GetMeshCurvatureSize() << std::endl; +#elif + myfile << -1.0 << std::endl; // dummy value writte for conformity +#endif + myfile << (double) gmshHypo->GetMaxSize() << std::endl; + myfile << (double) gmshHypo->GetMinSize() << std::endl; + myfile << (int) gmshHypo->GetSecondOrder() << std::endl; + myfile << (int) gmshHypo->GetUseIncomplElem() << std::endl; + if ( gmshHypo->GetCompoundOnEntries().size() > 0 ) + { + TCompound defCompounds = gmshHypo->GetCompoundOnEntries(); + for (TCompound::const_iterator it = defCompounds.begin(); it != defCompounds.end(); ++it ) + { + std::string token = (it !=--defCompounds.end()) ? "," : ""; + myfile << *it << token; + } + myfile << std::endl; + } + else + { + myfile << 0 << std::endl; // mark the absence of compounds + } + } + else + { + //Mark tha absence of parameters in the file + myfile << 0 << std::endl; + } + myfile.close(); +} + +// + +/** + * @brief write in a binary file the orientation for each surface element of the mesh + * + * @param aMesh The mesh + * @param aShape the shape associated to the mesh + * @param output_file name of the binary file + */ +void GMSHPlugin_GMSH_3D_Remote::exportElementOrientation(SMESH_Mesh& aMesh, + const TopoDS_Shape& aShape, + const std::string output_file) +{ + SMESH_MesherHelper helper(aMesh); + SMESH_ProxyMesh::Ptr proxyMesh( new SMESH_ProxyMesh( aMesh )); + std::map elemOrientation; + + for ( TopExp_Explorer exFa( aShape, TopAbs_FACE ); exFa.More(); exFa.Next()) + { + const TopoDS_Shape& aShapeFace = exFa.Current(); + int faceID = aMesh.GetMeshDS()->ShapeToIndex( aShapeFace ); + bool isRev = false; + if ( helper.NbAncestors(aShapeFace, aMesh, aShape.ShapeType()) > 1 ) + // IsReversedSubMesh() can work wrong on strongly curved faces, + // so we use it as less as possible + isRev = helper.IsReversedSubMesh( TopoDS::Face( aShapeFace )); + + const SMESHDS_SubMesh * aSubMeshDSFace = proxyMesh->GetSubMesh( aShapeFace ); + if ( !aSubMeshDSFace ) continue; + + SMDS_ElemIteratorPtr iteratorElem = aSubMeshDSFace->GetElements(); + + while ( iteratorElem->more() ) // loop on elements on a geom face + { + // check mesh face + const SMDS_MeshElement* elem = iteratorElem->next(); + if ( !elem ) + error( COMPERR_BAD_INPUT_MESH, "Null element encounters"); + if ( elem->NbCornerNodes() != 3 ) + error( COMPERR_BAD_INPUT_MESH, "Not triangle element encounters"); + elemOrientation[elem->GetID()] = isRev; + } // loop on elements on a face + } // loop on faces of a SOLID or SHELL + + { + std::ofstream df(output_file, ios::out|ios::binary); + int size = elemOrientation.size(); + df.write((char*)&size, sizeof(int)); + for(auto const& [id, orient]:elemOrientation) + { + df.write((char*)&id, sizeof(vtkIdType)); + df.write((char*)&orient, sizeof(bool)); + } + df.close(); + } +} + +/** + * @brief Compute mesh associate to shape + * + * @param aMesh The mesh + * @param aShape The shape + * @return true fi there are some error + */ +bool GMSHPlugin_GMSH_3D_Remote::Compute(SMESH_Mesh& aMesh, + const TopoDS_Shape& aShape) +{ + { + SMESH_MeshLocker myLocker(&aMesh); + SMESH_Hypothesis::Hypothesis_Status hypStatus; + GMSHPlugin_GMSH_3D::CheckHypothesis(aMesh, aShape, hypStatus); //in this call the _hypothesis is defined! + } + + SMESH_ParallelMesh& aParMesh = dynamic_cast(aMesh); + // Temporary folder for run +#ifdef WIN32 + // On windows mesh does not have GetTmpFolder + fs::path tmp_folder = aParMesh.GetTmpFolder() / fs::path("Volume-%%%%-%%%%"); +#else + fs::path tmp_folder = aParMesh.GetTmpFolder() / fs::unique_path(fs::path("Volume-%%%%-%%%%")); +#endif + fs::create_directories(tmp_folder); + // Using MESH2D generated after all triangles where created. + fs::path mesh_file= aParMesh.GetTmpFolder() / fs::path("Mesh2D.med"); + fs::path element_orientation_file=tmp_folder / fs::path("element_orientation.dat"); + fs::path new_element_file=tmp_folder / fs::path("new_elements.dat"); + //fs::path tmp_mesh_file=tmp_folder / fs::path("tmp_mesh.med"); + // Not used kept for debug + // fs::path output_mesh_file=tmp_folder / fs::path("output_mesh.med"); + fs::path shape_file=tmp_folder / fs::path("shape.brep"); + fs::path param_file=tmp_folder / fs::path("gmsh_param.txt"); + fs::path log_file=tmp_folder / fs::path("run.log"); + fs::path cmd_file=tmp_folder / fs::path("cmd.txt"); + + std::string mesh_name = "MESH"; + + { + SMESH_MeshLocker myLocker(&aMesh); + //Writing Shape + SMESH_DriverShape::exportShape(shape_file.string(), aShape); + + //Writing hypothesis to file + exportGmshParams(param_file.string(), _hypothesis); + + // Exporting element orientation + exportElementOrientation(aMesh, aShape, element_orientation_file.string()); + } + + // Calling run_mesher + // Path to mesher script + fs::path mesher_launcher = fs::path(std::getenv("SMESH_ROOT_DIR"))/ + fs::path("bin")/ + fs::path("salome")/ + fs::path("mesher_launcher.py"); + + std::string s_program="python3"; + std::list params; + params.push_back(mesher_launcher.string()); + params.push_back("GMSH3D"); + params.push_back(mesh_file.string()); + params.push_back(shape_file.string()); + params.push_back(param_file.string()); + params.push_back("--elem-orient-file=" + element_orientation_file.string()); + params.push_back("--new-element-file=" + new_element_file.string()); + // params.push_back("--output-mesh-file=" + output_mesh_file.string()); + + // Parallelism method parameters + int method = aParMesh.GetParallelismMethod(); + if(method == ParallelismMethod::MultiThread){ + params.push_back("--method=local"); + } else if (method == ParallelismMethod::MultiNode){ + params.push_back("--method=cluster"); + params.push_back("--resource="+aParMesh.GetResource()); + params.push_back("--wc-key="+aParMesh.GetWcKey()); + params.push_back("--nb-proc=1"); + params.push_back("--nb-proc-per-node="+std::to_string(aParMesh.GetNbProcPerNode())); + params.push_back("--nb-node="+std::to_string(aParMesh.GetNbNode())); + params.push_back("--walltime="+aParMesh.GetWalltime()); + } else { + throw SALOME_Exception("Unknown parallelism method "+method); + } + + std::string cmd = ""; + cmd += s_program; + for(auto arg: params){ + cmd += " " + arg; + } + MESSAGE("Running command: "); + MESSAGE(cmd); + // Writing command in cmd.log + { + std::ofstream flog(cmd_file.string()); + flog << cmd << endl; + } + + // Building arguments for QProcess + QString program = QString::fromStdString(s_program); + QStringList arguments; + for(auto arg : params){ + arguments << arg.c_str(); + } + + QString out_file = log_file.string().c_str(); + QProcess myProcess; + // myProcess.setProcessChannelMode(QProcess::MergedChannels); + myProcess.setProcessChannelMode(QProcess::ForwardedChannels); + myProcess.setStandardOutputFile(out_file); + + myProcess.start(program, arguments); + // Waiting for process to finish (argument -1 make it wait until the end of + // the process otherwise it just waits 30 seconds) + bool finished = myProcess.waitForFinished(-1); + int ret = myProcess.exitCode(); + if(ret != 0 || !finished){ + // Run crahed + std::string msg = "Issue with mesh_launcher: \n"; + msg += "See log for more details: " + log_file.string() + "\n"; + msg += cmd + "\n"; + throw SALOME_Exception(msg); + } + { + SMESH_MeshLocker myLocker(&aMesh); + // Binary file written from SA version of the mesher + std::ifstream df(new_element_file.string(), ios::binary); + + int numberOfNodes; + int totalNumberOfNodes; + int numberOfVolumes; + double meshNodes[3]; + int volNodes[4]; + int nodeID; + + SMESH_MesherHelper helper(aMesh); + // This function is mandatory for setElementsOnShape to work + helper.IsQuadraticSubMesh(aShape); + helper.SetElementsOnShape( true ); + + // Number of nodes in intial mesh + df.read((char*) &numberOfNodes, sizeof(int)); + // Number of nodes added by netgen + df.read((char*) &totalNumberOfNodes, sizeof(int)); + // Filling nodevec (correspondence netgen numbering mesh numbering) + std::vector< const SMDS_MeshNode* > nodeVec ( totalNumberOfNodes + 1 ); + SMESHDS_Mesh * meshDS = helper.GetMeshDS(); + for (int nodeIndex = 1 ; nodeIndex <= numberOfNodes; ++nodeIndex ) + { + //Id of the point + df.read((char*) &nodeID, sizeof(int)); + nodeVec.at(nodeIndex) = meshDS->FindNode(nodeID); + } + + // Add new points and update nodeVec + for (int nodeIndex = numberOfNodes +1; nodeIndex <= totalNumberOfNodes; ++nodeIndex ) + { + df.read((char *) &meshNodes, sizeof(double)*3); + nodeVec.at(nodeIndex) = helper.AddNode(meshNodes[0], meshNodes[1], meshNodes[2]); + } + + // Add tetrahedrons + df.read((char*) &numberOfVolumes, sizeof(int)); + for ( int elemIndex = 1; elemIndex <= numberOfVolumes; ++elemIndex ) + { + df.read((char*) &volNodes, sizeof(int)*4); + auto n0 = meshDS->FindNode(nodeVec[volNodes[0]]->GetID()); + auto n1 = meshDS->FindNode(nodeVec[volNodes[1]]->GetID()); + auto n2 = meshDS->FindNode(nodeVec[volNodes[2]]->GetID()); + auto n3 = meshDS->FindNode(nodeVec[volNodes[3]]->GetID()); + if ( n0 && n1 && n2 && n3 ) + helper.AddVolume( n0, n2, n1, n3 ); + } + } + + return true; +} + +/** + * @brief Assign submeshes to compute + * + * @param aSubMesh submesh to add + */ +void GMSHPlugin_GMSH_3D_Remote::setSubMeshesToCompute(SMESH_subMesh * aSubMesh) +{ + SMESH_MeshLocker myLocker(aSubMesh->GetFather()); + SMESH_Algo::setSubMeshesToCompute(aSubMesh); +} diff --git a/src/GMSHPlugin/GMSHPlugin_GMSH_3D_Remote.hxx b/src/GMSHPlugin/GMSHPlugin_GMSH_3D_Remote.hxx new file mode 100644 index 0000000..73265af --- /dev/null +++ b/src/GMSHPlugin/GMSHPlugin_GMSH_3D_Remote.hxx @@ -0,0 +1,62 @@ +// Copyright (C) 2007-2023 CEA, EDF, 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 : GMSHPlugin_GMSH_3D_Remote.hxx +// Created : 09 Septembre 2023 +// Author : Cesar Conopoima (OCC) +// Project : SALOME +//============================================================================= +// +#ifndef _GMSHPlugin_GMSH_3D_REMOTE_HXX_ +#define _GMSHPlugin_GMSH_3D_REMOTE_HXX_ + +#include "GMSHPlugin_GMSH_3D.hxx" + +#include +#include + +class SMDS_MeshNode; + +class GMSHPLUGIN_EXPORT GMSHPlugin_GMSH_3D_Remote: public GMSHPlugin_GMSH_3D +{ + public: + GMSHPlugin_GMSH_3D_Remote(int hypId, SMESH_Gen* gen); + virtual ~GMSHPlugin_GMSH_3D_Remote(); + + // Function whould not be used with remote Computing + bool CheckHypothesis (SMESH_Mesh& aMesh, + const TopoDS_Shape& aShape, + Hypothesis_Status& aStatus) override {(void)aMesh;(void)aShape;aStatus = HYP_OK;return true;}; + + bool Compute(SMESH_Mesh& aMesh, const TopoDS_Shape& aShape) override; + + void setSubMeshesToCompute(SMESH_subMesh * aSubMesh) override; + + + protected: + void exportElementOrientation(SMESH_Mesh& aMesh, const TopoDS_Shape& aShape, const std::string output_file); + void exportGmshParams( const std::string param_file, const SMESHDS_Hypothesis* hyp ); + typedef std::set TCompound; +}; + +#endif diff --git a/src/GMSHPlugin/GMSHPlugin_GMSH_3D_Remote_i.cxx b/src/GMSHPlugin/GMSHPlugin_GMSH_3D_Remote_i.cxx new file mode 100644 index 0000000..0af0b86 --- /dev/null +++ b/src/GMSHPlugin/GMSHPlugin_GMSH_3D_Remote_i.cxx @@ -0,0 +1,78 @@ +// Copyright (C) 2007-2023 CEA, EDF, 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 +// + +// SMESH SMESH_I : idl implementation based on 'SMESH' unit's classes +// File : GMSHPlugin_GMSH_3D_Remote_i.cxx +// Author : Cesar Conopoima (OCC) +// Module : GMSHPlugin +// $Header$ +// +#include "GMSHPlugin_GMSH_3D_Remote_i.hxx" +#include "SMESH_Gen.hxx" + +#include "Utils_CorbaException.hxx" +#include "utilities.h" + +//============================================================================= +/*! + * GMSHPlugin_GMSH_3D_Remote_i::GMSHPlugin_GMSH_3D_Remote_i + * + * Constructor + */ +//============================================================================= + +GMSHPlugin_GMSH_3D_Remote_i::GMSHPlugin_GMSH_3D_Remote_i( PortableServer::POA_ptr thePOA, + ::SMESH_Gen* theGenImpl ) + : SALOME::GenericObj_i( thePOA ), + SMESH_Hypothesis_i( thePOA ), + SMESH_Algo_i( thePOA ), + SMESH_3D_Algo_i( thePOA ) +{ + myBaseImpl = new ::GMSHPlugin_GMSH_3D_Remote( theGenImpl->GetANewId(), + theGenImpl ); +} + +//============================================================================= +/*! + * GMSHPlugin_GMSH_3D_Remote_i::~GMSHPlugin_GMSH_3D_Remote_i + * + * Destructor + */ +//============================================================================= + +GMSHPlugin_GMSH_3D_Remote_i::~GMSHPlugin_GMSH_3D_Remote_i() +{ +} + +//============================================================================= +/*! + * GMSHPlugin_GMSH_3D_Remote_i::GetImpl + * + * Get implementation + */ +//============================================================================= + +::GMSHPlugin_GMSH_3D_Remote* GMSHPlugin_GMSH_3D_Remote_i::GetImpl() +{ + return ( ::GMSHPlugin_GMSH_3D_Remote* )myBaseImpl; +} + diff --git a/src/GMSHPlugin/GMSHPlugin_GMSH_3D_Remote_i.hxx b/src/GMSHPlugin/GMSHPlugin_GMSH_3D_Remote_i.hxx new file mode 100644 index 0000000..5d21549 --- /dev/null +++ b/src/GMSHPlugin/GMSHPlugin_GMSH_3D_Remote_i.hxx @@ -0,0 +1,58 @@ +// Copyright (C) 2007-2023 CEA, EDF, 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 +// + +// SMESH SMESH_I : idl implementation based on 'SMESH' unit's classes +// File : GMSHPlugin_GMSH_3D_Remote_i.hxx +// Author : Cesar Conopoima (OCC) +// Module : GMSHPlugin +// $Header$ +// +#ifndef _GMSHPlugin_GMSH_3D_REMOTE_I_HXX_ +#define _GMSHPlugin_GMSH_3D_REMOTE_I_HXX_ + +#include "GMSHPlugin_Defs.hxx" + +#include +#include CORBA_SERVER_HEADER(GMSHPlugin_Algorithm) + +#include "SMESH_3D_Algo_i.hxx" +#include "GMSHPlugin_GMSH_3D_Remote.hxx" + +// ====================================================== +// GMSH 3d algorithm +// ====================================================== +class GMSHPLUGIN_EXPORT GMSHPlugin_GMSH_3D_Remote_i: + public virtual POA_GMSHPlugin::GMSHPlugin_GMSH_3D_Remote, + public virtual SMESH_3D_Algo_i +{ +public: + // Constructor + GMSHPlugin_GMSH_3D_Remote_i( PortableServer::POA_ptr thePOA, + ::SMESH_Gen* theGenImpl ); + // Destructor + virtual ~GMSHPlugin_GMSH_3D_Remote_i(); + + // Get implementation + ::GMSHPlugin_GMSH_3D_Remote* GetImpl(); +}; + +#endif diff --git a/src/GMSHPlugin/GMSHPlugin_GMSH_3D_SA.cxx b/src/GMSHPlugin/GMSHPlugin_GMSH_3D_SA.cxx new file mode 100644 index 0000000..e284ba0 --- /dev/null +++ b/src/GMSHPlugin/GMSHPlugin_GMSH_3D_SA.cxx @@ -0,0 +1,331 @@ +// Copyright (C) 2012-2015 ALNEOS +// Copyright (C) 2016-2023 EDF +// +// 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.alneos.com/ or email : contact@alneos.fr +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// +#include "GMSHPlugin_GMSH_3D.hxx" +#include "GMSHPlugin_GMSH_3D_SA.hxx" +#include "GMSHPlugin_Hypothesis_2D.hxx" +#include "GMSHPlugin_Mesher.hxx" + +#include +#include +#include +#include + +#include + +//============================================================================= +/*! + * + */ +//============================================================================= + +GMSHPlugin_GMSH_3D_SA::GMSHPlugin_GMSH_3D_SA() + : GMSHPlugin_GMSH_3D(0, new SMESH_Gen() ) +{ + MESSAGE("GMSHPlugin_GMSH_3D_SA::GMSHPlugin_GMSH_3D_SA"); + _name = "GMSH_3D_SA"; +} + +//============================================================================= +/*! + * + */ +//============================================================================= + +GMSHPlugin_GMSH_3D_SA::~GMSHPlugin_GMSH_3D_SA() +{ + MESSAGE("GMSHPlugin_GMSH_3D_SA::~GMSHPlugin_GMSH_3D_SA"); +} + +void GMSHPlugin_GMSH_3D_SA::importGMSHParameters( const std::string hypo_file ) +{ + GMSHPlugin_Hypothesis * hypParameters = new GMSHPlugin_Hypothesis(0, GetGen()); + std::ifstream myfile(hypo_file); + std::string line; + + std::getline(myfile, line); + int hasParams = std::stoi(line); + if ( hasParams ) + { + std::getline(myfile, line); + hypParameters->Set2DAlgo( (GMSHPlugin_Hypothesis::Algo2D)(std::stoi(line)) ); + std::getline(myfile, line); + hypParameters->Set3DAlgo( (GMSHPlugin_Hypothesis::Algo3D)(std::stoi(line)) ); + std::getline(myfile, line); + hypParameters->SetRecomb2DAlgo( (GMSHPlugin_Hypothesis::Recomb2DAlgo)(std::stoi(line)) ); + std::getline(myfile, line); + hypParameters->SetRecombineAll( std::stoi(line) ); + std::getline(myfile, line); + hypParameters->SetSubdivAlgo( (GMSHPlugin_Hypothesis::SubdivAlgo)(std::stoi(line)) ); + std::getline(myfile, line); + hypParameters->SetRemeshAlgo( (GMSHPlugin_Hypothesis::RemeshAlgo)(std::stoi(line)) ); + std::getline(myfile, line); + hypParameters->SetSmouthSteps( std::stod(line) ); + std::getline(myfile, line); + hypParameters->SetSizeFactor( std::stod(line) ); + std::getline(myfile, line); +#if GMSH_MAJOR_VERSION >=4 && GMSH_MINOR_VERSION >=10 + hypParameters->SetMeshCurvatureSize( std::stod(line) ); +#endif + std::getline(myfile, line); + hypParameters->SetMaxSize( std::stod(line) ); + std::getline(myfile, line); + hypParameters->SetMinSize( std::stod(line) ); + std::getline(myfile, line); + hypParameters->SetSecondOrder( std::stoi(line) ); + std::getline(myfile, line); + hypParameters->SetUseIncomplElem( std::stoi(line) ); + std::getline(myfile, line); // Get Compound names comma separated + if ( line != "0" ) // Mark no presence of groups + { + std::stringstream compoundLine (line); + std::string compound; + while ( std::getline (compoundLine, compound, ',') ) + hypParameters->SetCompoundOnEntry( compound ); + } + _hypothesis = dynamic_cast< const GMSHPlugin_Hypothesis *> (hypParameters); + } + else + { + // in case the parameters are not defined the _hypothesis should remain undefined + // so default parameters are used to mesh! + // _hypothesis = NULL; + } +} + +/** + * @brief Write a binary file containing information on the elements/nodes + * created by the mesher + * + * @param nodeVec mapping between the mesh id and the netgen structure id + * @param mesher gmhsplugin mesher + * @param new_element_file Name of the output file + * @return true if there are some error + */ +void GMSHPlugin_GMSH_3D_SA::fillNewElementFile( std::vector< const SMDS_MeshNode* > &nodeVec, GMSHPlugin_Mesher &mesher, std::string new_element_file ) +{ + GModel* gModel = mesher.GetGModel(); + const int numberOfNodes = nodeVec.size() - 1; + const int numOfVolumens = gModel->getNumMeshElements( 3 ); + int numberOfTotalNodes = numberOfNodes; + std::map< MVertex *,int> nodeMap; + bool isOK = ( numOfVolumens > 0 ); + if ( isOK && !new_element_file.empty() ) + { + MESSAGE("Writting new elements"); + + std::ofstream df(new_element_file, ios::out|ios::binary); + double points[3]; + int volumens[4]; + + df.write((char*) &numberOfNodes, sizeof(int)); + + // To get 3D elements we need to iterate in regions + std::map vertexIdToCoordinate; + // Index nodes of new volumetric elements in order from the vol region + for ( GModel::riter it = gModel->firstRegion(); it != gModel->lastRegion(); ++it) + { + GRegion *gRegion = *it; + for( size_t i = 0; i < gRegion->mesh_vertices.size(); i++) + { + MVertex *v = gRegion->mesh_vertices[i]; + const SMDS_MeshNode * preMeshedNode = mesher.PremeshedNode( v ); + if ( !preMeshedNode ) + { + numberOfTotalNodes++; + vertexIdToCoordinate[ numberOfTotalNodes ] = v; + nodeMap.insert({ v, numberOfTotalNodes }); + } + } + } + + df.write((char*) &numberOfTotalNodes, sizeof(int)); + + for (int nodeIndex = 1 ; nodeIndex <= numberOfNodes; ++nodeIndex ) + { + //Id of the point + int id = nodeVec.at(nodeIndex)->GetID(); + df.write((char*) &id, sizeof(int)); + } + + // Writing info on new points + for (int nodeIndex = numberOfNodes+1; nodeIndex <= numberOfTotalNodes; ++nodeIndex ) + { + if ( vertexIdToCoordinate[nodeIndex] ) + { + df.write((char *) &vertexIdToCoordinate[nodeIndex]->x(), sizeof(double) ); + df.write((char *) &vertexIdToCoordinate[nodeIndex]->y(), sizeof(double) ); + df.write((char *) &vertexIdToCoordinate[nodeIndex]->z(), sizeof(double) ); + } + } + + df.write((char*) &numOfVolumens, sizeof(int)); + for ( GModel::riter it = gModel->firstRegion(); it != gModel->lastRegion(); ++it) + { + GRegion *gRegion = *it; + std::vector verts; + + for( size_t i = 0; i < gRegion->getNumMeshElements(); i++) + { + MElement *element = gRegion->getMeshElement(i); + verts.clear(); + element->getVertices(verts); + for( MVertex* v : verts ) + { + const SMDS_MeshNode * node = mesher.PremeshedNode( v ); + auto it = find(nodeVec.begin(), nodeVec.end(), node ); + int nodeId = node ? (it - nodeVec.begin()) : nodeMap[ v ]; + df.write((char*) &nodeId, sizeof(int) ); + } + } + } + df.close(); + } +} + +/** + * @brief Fill the list of elements in order and associate the read orientation (if any) to then. + * Index the elements and orientation to the ordered map so we are sure the write orientation + * match the element from the load mesh. IF no orientation file was written, then all the faces + * of the read mesh match the face orientation of his original associated face + * @param aMesh the loaded mesh + * @param element_orientation_file name of the file where to read the orientation + * @param listElements the ordered map of elements with his orientation associated + * @return the listElements filled. + */ +void GMSHPlugin_GMSH_3D_SA::fillListOfElementsOriented( SMESH_Mesh& aMesh, std::string element_orientation_file, + std::map& listElements ) +{ + + // fill the elements found in the surface to the listOfElements so it can be used by the mesher! + SMESHDS_Mesh* meshDS = aMesh.GetMeshDS(); + std::map elemOrientation; + { + // Setting all element orientation to false if there no element orientation file + if(element_orientation_file.empty()) + { + SMDS_ElemIteratorPtr iteratorElem = meshDS->elementsIterator(SMDSAbs_Face); + while ( iteratorElem->more() ) // loop on elements on a geom face + { + // check mesh face + const SMDS_MeshElement* elem = iteratorElem->next(); + listElements[elem] = false; + } + } + else + { + std::ifstream df(element_orientation_file, ios::binary|ios::in); + int nbElement; + bool orient; + + vtkIdType id; + df.read((char*)&nbElement, sizeof(int)); + + for(int ielem=0;ielemelementsIterator(SMDSAbs_Face); + while ( iteratorElem->more() ) // loop on elements on a geom face + { + // check mesh face + const SMDS_MeshElement* elem = iteratorElem->next(); + // only index registered elements + bool isIn = elemOrientation.count(elem->GetID())==1; + if(!isIn) continue; + listElements[elem] = elemOrientation[elem->GetID()]; + } + } + } +} + + +/** + * @brief Compute the mesh by first filling premeshed faces to gmsh and then calling the mesher for the upper dimension + * @param aShape the loaded shape + * @param aMesh the read Mesh (contain 2D elements covering the entire geometry) + * @param new_element_file output file containing info the elements created by the mesher + * @param element_orientation_file Binary file containing the orientation of surface elemnts + * @param output_mesh whether or not write the created elements into the mesh + * @return negation of mesh fail: true, false + * */ +bool GMSHPlugin_GMSH_3D_SA::Compute( TopoDS_Shape &aShape, SMESH_Mesh& aMesh, std::string new_element_file, + std::string element_orientation_file, bool output_mesh ) +{ + GMSHPlugin_Mesher mesher(&aMesh, aShape,/*2d=*/false, true); + std::vector< const SMDS_MeshNode* > nodeVec; // to save premeshed elements + mesher.SetParameters(dynamic_cast(_hypothesis)); + + std::map listElements; + fillListOfElementsOriented( aMesh, element_orientation_file, listElements ); + bool Compute = mesher.Compute3D( nodeVec, listElements, output_mesh ); + + fillNewElementFile( nodeVec, mesher, new_element_file ); + mesher.finalizeGModel(); + return Compute; +} + +/** + * @brief Running the mesher on the given files + * + * @param input_mesh_file Mesh file (containing 2D elements) + * @param shape_file Shape file (BREP or STEP format) + * @param hypo_file Ascii file containing the gmsh parameters + * @param element_orientation_file Binary file containing the orientation of surface elements + * @param new_element_file output file containing info the elements created by the mesher + * @param output_mesh_file output mesh file (if empty it will not be created) + * @return int + */ +int GMSHPlugin_GMSH_3D_SA::run(const std::string input_mesh_file, + const std::string shape_file, + const std::string hypo_file, + const std::string element_orientation_file, + const std::string new_element_file, + const std::string output_mesh_file) +{ + std::unique_ptr myMesh(_gen->CreateMesh(false)); + + SMESH_DriverMesh::importMesh(input_mesh_file, *myMesh); + + // Importing shape + TopoDS_Shape myShape; + SMESH_DriverShape::importShape(shape_file, myShape); + + // Define _hypothesis to then be able to call SetParameters( hypothesis ) + importGMSHParameters(hypo_file); + + MESSAGE("Meshing with gmsh3d"); + int ret = Compute(myShape, *myMesh, new_element_file, element_orientation_file, !output_mesh_file.empty()); + + if(ret){ + std::cerr << "Meshing failed" << std::endl; + return ret; + } + + if(!output_mesh_file.empty()){ + std::string meshName = "MESH"; + SMESH_DriverMesh::exportMesh(output_mesh_file, *myMesh, meshName); + } + + return ret; +} \ No newline at end of file diff --git a/src/GMSHPlugin/GMSHPlugin_GMSH_3D_SA.hxx b/src/GMSHPlugin/GMSHPlugin_GMSH_3D_SA.hxx new file mode 100644 index 0000000..5d72a38 --- /dev/null +++ b/src/GMSHPlugin/GMSHPlugin_GMSH_3D_SA.hxx @@ -0,0 +1,57 @@ +// Copyright (C) 2012-2015 ALNEOS +// Copyright (C) 2016-2023 EDF +// +// 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.alneos.com/ or email : contact@alneos.fr +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// +#ifndef _GMSHPlugin_GMSH_3D_SA_HXX_ +#define _GMSHPlugin_GMSH_3D_SA_HXX_ + +#include "GMSHPlugin_Defs.hxx" +#include "GMSHPlugin_GMSH_3D.hxx" + +#include +#include + +#include "SMESH_Algo.hxx" +#include "SMESH_subMesh.hxx" +#include "SMESH_Mesh.hxx" + +class GMSHPLUGIN_EXPORT GMSHPlugin_GMSH_3D_SA: public GMSHPlugin_GMSH_3D +{ +public: + GMSHPlugin_GMSH_3D_SA(); + virtual ~GMSHPlugin_GMSH_3D_SA(); + + int run(const std::string input_mesh_file, + const std::string shape_file, + const std::string hypo_file, + const std::string element_orientation_file, + const std::string new_element_file, + const std::string output_mesh_file); + + bool Compute( TopoDS_Shape &aShape, SMESH_Mesh& aMesh, std::string new_element_file, std::string element_orientation_file, bool output_mesh ); + +private: + + void fillNewElementFile( std::vector< const SMDS_MeshNode* > &nodeVec, GMSHPlugin_Mesher &mesher, std::string new_element_file ); + void importGMSHParameters( const std::string hypo_file ); + void fillListOfElementsOriented(SMESH_Mesh& aMesh, std::string element_orientation_file, + std::map& listElements ); +}; + +#endif diff --git a/src/GMSHPlugin/GMSHPlugin_Mesher.cxx b/src/GMSHPlugin/GMSHPlugin_Mesher.cxx index b51ed77..8753e41 100644 --- a/src/GMSHPlugin/GMSHPlugin_Mesher.cxx +++ b/src/GMSHPlugin/GMSHPlugin_Mesher.cxx @@ -43,6 +43,7 @@ #include #include +#include #include #include @@ -241,6 +242,236 @@ void GMSHPlugin_Mesher::SetMaxThreadsGmsh() } #endif + +//================================================================================ +/*! + * \brief Check that the passed nodes are all IN the face + * \param element the element + * \param F the geom Face + * \param uvValues a vector of size elem->NbCornerNodes() to save the uv coordinate points on the face + * \return true if all the nodes are IN the face + */ + //================================================================================ +bool GMSHPlugin_Mesher::IsAllNodesInSameFace( const SMDS_MeshElement* element, const TopoDS_Face& F, + std::vector& uvValues ) +{ + Handle(ShapeAnalysis_Surface) sprojector = new ShapeAnalysis_Surface( BRep_Tool::Surface( F )); + double tol = BRep_Tool::MaxTolerance( F, TopAbs_FACE ); + int nbN = element->NbCornerNodes(); + gp_Pnt surfPnt(0,0,0); + for ( int i = 0; i < nbN; ++i ) + { + SMESH_NodeXYZ nXYZ( element->GetNode( i ) ); + gp_XY uv = sprojector->ValueOfUV( nXYZ, tol ).XY(); + surfPnt = sprojector->Value( uv ); + double dist = surfPnt.Distance( nXYZ ); + if ( dist > tol ) + return false; + else + uvValues[ i ] = uv; + } + return true; +} + +//================================================================================ +/*! + * \brief Associate mesh elements to geometrical faces. + * \param the list of elements + * \return a map between faces (incremental order) and mesh elements found to be placed on the face + */ + //================================================================================ +std::map>>> GMSHPlugin_Mesher::AssociateElementsToFaces( std::map& listElements ) +{ + // Map faces to elementId and uv of nodes. + // Index by face id + // Index vector with element smIdType, [ gp_XY_0, gp_XY_1, gp_XY_2 ] + std::map>>> elementToFaceMap; + SMESHDS_Mesh* meshDS = _mesh->GetMeshDS(); + SMDS_ElemIteratorPtr iteratorElem = meshDS->elementsIterator(SMDSAbs_Face); + for ( auto const& [elem, IsReverse] : listElements ) // loop on elements on a geom face + { + int nbN = elem->NbCornerNodes(); + std::vector uvValues(nbN); + if ( nbN > 4 /*this restriction might be eliminated. Have to adapt FillGeomMapMeshUsing2DMeshIterator function too */) + throw std::string("Polygon sub-meshes not supported"); + + int faceId = 1; + for( GModel::fiter it = _gModel->firstFace(); it != _gModel->lastFace(); ++it ) + { + GFace *gFace = *it; + TopoDS_Face topoFace = *((TopoDS_Face*)gFace->getNativePtr()); + if ( IsAllNodesInSameFace( elem, topoFace, uvValues ) ) + { + elementToFaceMap[ faceId ].push_back( std::make_tuple( elem->GetID(), IsReverse, uvValues ) ); + break; + } + faceId++; + } + } + return elementToFaceMap; +} + +//================================================================================ +/*! + * \brief Add the elements found associated to the face as gmsh elements + */ + //================================================================================ +void GMSHPlugin_Mesher::Set2DMeshes( std::vector< const SMDS_MeshNode* >& nodeVec, std::map& listElements ) +{ + std::map< const SMDS_MeshNode* , const MVertex * > nodes2mvertMap; + SMESHDS_Mesh* meshDS = _mesh->GetMeshDS(); + auto elementToFaceMap = AssociateElementsToFaces( listElements ); + int faceId = 1; + std::vector mVertices; + + for(GModel::fiter it = _gModel->firstFace(); it != _gModel->lastFace(); ++it) + { + GFace *gFace = *it; + gFace->deleteMesh(); + + auto element2uv = elementToFaceMap.find( faceId )->second; + + const int numberOfEntries = element2uv.size(); + + for (int el = 0; el < numberOfEntries; el++) + { + const smIdType elementId = std::get<0>( element2uv[ el ] ); // smesh element id + bool isReverse = std::get<1>( element2uv[ el ] ); + const SMDS_MeshElement* elem = meshDS->FindElement( elementId ); + + int nbN = elem->NbCornerNodes(); + mVertices.resize( nbN ); + + for ( int i = 0; i < nbN; ++i ) + { + const SMDS_MeshNode* n = elem->GetNode( i ); + MVertex * mv = nullptr; + auto n2v = nodes2mvertMap.find( n ); + if ( n2v != nodes2mvertMap.end() ) + { + mv = const_cast< MVertex*>( n2v->second ); + } + else + { + if ( n->GetPosition()->GetDim() < 2 ) + throw std::string("Wrong mapping of edge nodes to GMSH nodes"); + SMESH_NodeXYZ xyz = n; + gp_XY uv = std::get<2>(element2uv[ el ])[ i ]; + mv = new MFaceVertex( xyz.X(), xyz.Y(), xyz.Z(), gFace, uv.X(), uv.Y() ); + gFace->mesh_vertices.push_back( mv ); + nodes2mvertMap.insert({ n, mv }); + _nodeMap.insert ({ mv, n }); + _premeshednodeMap.insert({ mv, n }); + } + mVertices[ i ] = mv; + } + // create GMSH mesh faces + switch ( nbN ) { + case 3: + if ( isReverse ) + gFace->triangles.push_back (new MTriangle(mVertices[0], mVertices[2], mVertices[1])); + else + gFace->triangles.push_back (new MTriangle(mVertices[0], mVertices[1], mVertices[2])); + break; + case 4: + if ( isReverse ) + gFace->quadrangles.push_back (new MQuadrangle(mVertices[0], mVertices[3], + mVertices[2], mVertices[1])); + else + gFace->quadrangles.push_back (new MQuadrangle(mVertices[0], mVertices[1], + mVertices[2], mVertices[3])); + break; + default:; + } + } + faceId++; // face counter + } // iterator in the face + + // Fill the node + nodeVec.resize( nodes2mvertMap.size() + 1, 0 ); + int count = 1; + for (auto k : nodes2mvertMap ) + { + nodeVec[ count ] = k.first; // Index the node id to the smesh node itself + count++; + } +} + + +//================================================================================ +/*! + * \brief Initialize GMSH model with mesh elements as geometry objects. + * Nodes are vertexes and element connections are geom lines + */ + //================================================================================ +void GMSHPlugin_Mesher::FillGeomMapMeshUsing2DMeshIterator( std::map& listElements ) +{ + gmsh::initialize(); + gmsh::model::add("mesh"); + // typedef for maps + typedef map< const SMDS_MeshNode*, int, TIDCompare > TNodeToIDMap; + typedef TNodeToIDMap::value_type TN2ID; + typedef map, int> TLineToIDMap; + TNodeToIDMap aNodeToID; + TLineToIDMap aLineToID; + + int aNbOfNodes = 0; + int aNbOfLines = 0; + + const int invalid_ID = -1; + std::vector aTrinagle( 3, 0 ); + + // Playing around with SMESHDS_Mesh structure + SMESHDS_Mesh* meshDS = _mesh->GetMeshDS(); + + for ( auto const& [elem, IsReverse] : listElements ) // loop on elements on a geom face + { + if ( elem->NbCornerNodes() != 3 ) + return; + + for (int iN = 0; iN < 3; ++iN) + { + const SMDS_MeshNode* aNode = elem->GetNode(iN); + + int& ngID = aNodeToID.insert(TN2ID(aNode, invalid_ID)).first->second; + if (ngID == invalid_ID) + { + ngID = ++aNbOfNodes; + gmsh::model::occ::addPoint(aNode->X(), aNode->Y(), aNode->Z(), 1.e-2, ngID); + } + + aTrinagle[ IsReverse ? 2 - iN : iN ] = ngID; + } + // add triangle + if ((aTrinagle[0] == aTrinagle[1] || + aTrinagle[0] == aTrinagle[2] || + aTrinagle[2] == aTrinagle[1])) + continue; + + std::vector LinesID(3, 0); + for (int anIndex = 0; anIndex < 3; ++anIndex) + { + int aNextIndex = (anIndex + 1) % 3; + if (aLineToID.find({ aTrinagle[anIndex], aTrinagle[aNextIndex] }) == aLineToID.end() + && aLineToID.find({ aTrinagle[aNextIndex], aTrinagle[anIndex] }) == aLineToID.end()) + { + LinesID[anIndex] = aLineToID.insert({ { aTrinagle[aNextIndex], aTrinagle[anIndex] }, ++aNbOfLines }).first->second; + gmsh::model::occ::addLine(aTrinagle[anIndex], aTrinagle[aNextIndex], LinesID[anIndex]); + } + else + { + LinesID[anIndex] = aLineToID.find({ aTrinagle[anIndex], aTrinagle[aNextIndex] })->second; + if (LinesID[anIndex] == 0) + LinesID[anIndex] = aLineToID.find({ aTrinagle[aNextIndex], aTrinagle[anIndex] })->second; + + } + } + // if (!aProxyMesh->IsTemporary(ls.first)) + // swap(aTrinagle[1], aTrinagle[2]); + gmsh::model::occ::addCurveLoop(LinesID); + } +} + //================================================================================ /*! * \brief Initialize GMSH model @@ -254,7 +485,6 @@ void GMSHPlugin_Mesher::FillGMSHMesh() SMESHDS_Mesh* meshDS = _mesh->GetMeshDS(); int aNbOfNodes = 0; - int aNbOfCurves = 0; int aNbOfLines = 0; std::vector aTrinagle(3, 0); @@ -284,7 +514,6 @@ void GMSHPlugin_Mesher::FillGMSHMesh() { const TopoDS_Shape& aShapeFace = exFa.Current(); int faceID = meshDS->ShapeToIndex(aShapeFace); - bool isRev = false; if (aCheckReverse && aHelper.NbAncestors(aShapeFace, *_mesh, _shape.ShapeType()) > 1) // IsReversedSubMesh() can work wrong on strongly curved faces, @@ -292,8 +521,8 @@ void GMSHPlugin_Mesher::FillGMSHMesh() isRev = aHelper.IsReversedSubMesh(TopoDS::Face(aShapeFace)); const SMESHDS_SubMesh* aSubMeshDSFace = aProxyMesh->GetSubMesh(aShapeFace); - if (!aSubMeshDSFace) continue; - + if (!aSubMeshDSFace) + continue; SMDS_ElemIteratorPtr iteratorElem = aSubMeshDSFace->GetElements(); if (aHelper.IsQuadraticSubMesh(_shape) && dynamic_cast(aSubMeshDSFace)) @@ -344,8 +573,8 @@ void GMSHPlugin_Mesher::FillGMSHMesh() // add triangle if (hasDegen && (aTrinagle[0] == aTrinagle[1] || aTrinagle[0] == aTrinagle[2] || - aTrinagle[2] == aTrinagle[1])) - continue; + aTrinagle[2] == aTrinagle[1])) + continue; std::vector LinesID(3, 0); @@ -369,7 +598,7 @@ void GMSHPlugin_Mesher::FillGMSHMesh() if (!aProxyMesh->IsTemporary(ls.first)) swap(aTrinagle[1], aTrinagle[2]); - int aTag = gmsh::model::occ::addCurveLoop(LinesID); + gmsh::model::occ::addCurveLoop(LinesID); } // Generate 1D and 2D mesh @@ -634,6 +863,22 @@ const SMDS_MeshNode* GMSHPlugin_Mesher::Node( const MVertex* v ) return nullptr; } +//================================================================================ +/*! + * \brief Get a node by a GMSH mesh vertex + */ +//================================================================================ + +const SMDS_MeshNode* GMSHPlugin_Mesher::PremeshedNode( const MVertex* v ) +{ + std::map< const MVertex *, const SMDS_MeshNode* >::iterator v2n = _premeshednodeMap.find( v ); + if ( v2n != _premeshednodeMap.end() ) + return v2n->second; + + return nullptr; +} + + //================================================================================ /*! * \brief Return a corresponding sub-mesh if a shape is meshed @@ -1308,6 +1553,66 @@ void GMSHPlugin_Mesher::mymsg::operator()(std::string level, std::string msg) } } +bool GMSHPlugin_Mesher::Compute3D( std::vector< const SMDS_MeshNode* >& nodeVec, std::map& listElements, bool addElements ) +{ + MESSAGE("GMSHPlugin_Mesher::Compute3D"); + int err = 0; + _maxThreads = 1; +#if GMSH_MAJOR_VERSION >=4 && GMSH_MINOR_VERSION >=3 + _maxThreads = 1; +#endif + + char* argv[] = {"-noenv"}; + GmshInitialize(1,argv); + SetGmshOptions(); + _gModel = new GModel(); + mymsg msg(_gModel); + GmshSetMessageHandler(&msg); + + _gModel->importOCCShape((void*)&_shape); + try + { + HideComputedEntities( _gModel, true ); + // fill geometry with elements as geom objects + FillGeomMapMeshUsing2DMeshIterator( listElements ); + Set2DMeshes( nodeVec, listElements ); + _gModel->mesh( /*dim=*/ 3); + } + catch (std::string& str) + { + err = 1; + MESSAGE(str); + } + catch (...) + { + err = 1; + MESSAGE("Unrecoverable error during Generation of Gmsh Mesh"); + } + + if (!err) + { +#if GMSH_MAJOR_VERSION >=4 && GMSH_MINOR_VERSION >=8 + if (_compounds.size() > 0) + SetCompoundMeshVisibility(); +#endif + } + + if ( addElements ) + FillSMesh(); + MESSAGE("GMSHPlugin_Mesher::Compute3D:End"); + return err; +} + +void GMSHPlugin_Mesher::finalizeGModel() +{ + if ( _gModel ) + { + GmshSetMessageHandler(nullptr); + delete _gModel; + GmshFinalize(); + } +} + //============================================================================= /*! * Here we are going to use the GMSH mesher @@ -1339,15 +1644,12 @@ bool GMSHPlugin_Mesher::Compute() if (_is3d) { FillGMSHMesh(); - Set2DSubMeshes(_gModel); - _gModel->mesh( /*dim=*/ 3); } else { //CTX::instance()->mesh.maxNumThreads1D=1; - _gModel->mesh( /*dim=*/ 1); Set1DSubMeshes(_gModel); @@ -1601,7 +1903,7 @@ void GMSHPlugin_Mesher::Set2DSubMeshes( GModel* gModel ) */ //================================================================================ -void GMSHPlugin_Mesher::HideComputedEntities( GModel* gModel ) +void GMSHPlugin_Mesher::HideComputedEntities( GModel* gModel, bool hideAnyway ) { CTX::instance()->mesh.meshOnlyVisible = true; @@ -1617,7 +1919,7 @@ void GMSHPlugin_Mesher::HideComputedEntities( GModel* gModel ) continue; TopoDS_Edge topoEdge = *((TopoDS_Edge*)gEdge->getNativePtr()); - if ( HasSubMesh( topoEdge )) + if ( HasSubMesh( topoEdge ) || hideAnyway ) gEdge->setVisibility(0); } @@ -1634,7 +1936,7 @@ void GMSHPlugin_Mesher::HideComputedEntities( GModel* gModel ) continue; TopoDS_Face topoFace = *((TopoDS_Face*)gFace->getNativePtr()); - if ( HasSubMesh( topoFace )) + if ( HasSubMesh( topoFace ) || hideAnyway ) gFace->setVisibility(0); } } diff --git a/src/GMSHPlugin/GMSHPlugin_Mesher.hxx b/src/GMSHPlugin/GMSHPlugin_Mesher.hxx index 062b318..49b1bb2 100644 --- a/src/GMSHPlugin/GMSHPlugin_Mesher.hxx +++ b/src/GMSHPlugin/GMSHPlugin_Mesher.hxx @@ -42,6 +42,7 @@ #endif #include "MElement.h" +#include #include "GMSHPlugin_Defs.hxx" #include "SMESH_Algo.hxx" @@ -72,6 +73,9 @@ class GMSHPLUGIN_EXPORT GMSHPlugin_Mesher void SetParameters(const GMSHPlugin_Hypothesis* hyp); + bool Compute3D( std::vector< const SMDS_MeshNode* >& nodeVec, + std::map& listElements, + bool addElements ); bool Compute(); bool Evaluate(MapShapeNbElems& aResMap); @@ -79,6 +83,11 @@ class GMSHPLUGIN_EXPORT GMSHPlugin_Mesher static float DistBoundingBox(const SBoundingBox3d& bounds, const SPoint3& point); void FillGMSHMesh(); + void FillGeomMapMeshUsing2DMeshIterator( std::map& listElements ); + GModel* GetGModel(){ return _gModel;}; + void finalizeGModel(); + const SMDS_MeshNode* Node( const MVertex* v ); + const SMDS_MeshNode* PremeshedNode( const MVertex* v ); private: SMESH_Mesh* _mesh; @@ -108,18 +117,21 @@ class GMSHPLUGIN_EXPORT GMSHPlugin_Mesher std::set _compounds; std::map< const MVertex *, const SMDS_MeshNode* > _nodeMap; - - const SMDS_MeshNode* Node( const MVertex* v ); + std::map< const MVertex *, const SMDS_MeshNode* > _premeshednodeMap; // used for the SA version SMESHDS_SubMesh* HasSubMesh( const TopoDS_Shape& s ); void SetGmshOptions(); void CreateGmshCompounds(); void FillSMesh(); - void HideComputedEntities( GModel* gModel ); + void HideComputedEntities( GModel* gModel, bool hideAnyway = false ); void RestoreVisibility( GModel* gModel ); void Set1DSubMeshes( GModel* ); void Set2DSubMeshes( GModel* ); void toPython( GModel* ); + bool IsAllNodesInSameFace( const SMDS_MeshElement* triangle, const TopoDS_Face& F, std::vector& uvValues ); + std::map>>> AssociateElementsToFaces( std::map& listElements ); + void Set2DMeshes( std::vector< const SMDS_MeshNode* >& nodeVec, std::map& listElements ); + #if GMSH_MAJOR_VERSION >=4 && GMSH_MINOR_VERSION >=3 void SetMaxThreadsGmsh(); void SetCompoundMeshVisibility(); diff --git a/src/GMSHPlugin/GMSHPlugin_Runner_main.cxx b/src/GMSHPlugin/GMSHPlugin_Runner_main.cxx new file mode 100644 index 0000000..03046c4 --- /dev/null +++ b/src/GMSHPlugin/GMSHPlugin_Runner_main.cxx @@ -0,0 +1,109 @@ +// Copyright (C) 2007-2023 CEA, EDF, 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 : GMSHPlugin_Runner_main.cxx +// Author : Cesar Conopoima, Open Cascade +// Module : GMSH +// + +#include "GMSHPlugin_GMSH_3D_SA.hxx" + +#include +#include +#include +#include + +/** + * @brief Main function + * + * @param argc Number of arguments + * @param argv Arguments + * + * @return error code + */ +int main(int argc, char *argv[]){ + + if(argc!=8||(argc==2 && (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help")==0))){ + std::cout << "Error in number of arguments "<< argc-1<<" given expected 7" <> thing; + + if (output_mesh_file == "NONE") + output_mesh_file = ""; + if (element_orientation_file == "NONE") + element_orientation_file = ""; + if (new_element_file == "NONE") + new_element_file = ""; + + std::cout << mesher << "\n"; + std::cout << input_mesh_file << "\n"; + std::cout << shape_file << "\n"; + std::cout << hypo_file << "\n"; + std::cout << element_orientation_file << "\n"; + std::cout << new_element_file << "\n"; + std::cout << output_mesh_file << "\n"; + + if (mesher=="GMSH3D"){ + GMSHPlugin_GMSH_3D_SA myplugin; + std::cout << input_mesh_file << "\n"; + std::cout << shape_file << "\n"; + std::cout << hypo_file << "\n"; + std::cout << new_element_file << "\n"; + std::cout << output_mesh_file << "\n"; + int run = myplugin.run(input_mesh_file, + shape_file, + hypo_file, + element_orientation_file, + new_element_file, + output_mesh_file ); + } + else { + std::cerr << "Unknown mesher:" << mesher << std::endl; + return 1; + } + return 0; +} diff --git a/src/GMSHPlugin/GMSHPlugin_i.cxx b/src/GMSHPlugin/GMSHPlugin_i.cxx index c49edcc..2682e55 100644 --- a/src/GMSHPlugin/GMSHPlugin_i.cxx +++ b/src/GMSHPlugin/GMSHPlugin_i.cxx @@ -26,6 +26,7 @@ #include "GMSHPlugin_GMSH_i.hxx" #include "GMSHPlugin_GMSH_2D_i.hxx" #include "GMSHPlugin_GMSH_3D_i.hxx" +#include "GMSHPlugin_GMSH_3D_Remote_i.hxx" template class GMSHPlugin_Creator_i:public HypothesisCreator_i { @@ -57,6 +58,8 @@ extern "C" aCreator = new GMSHPlugin_Creator_i; else if (strcmp(aHypName, "GMSH_3D") == 0) aCreator = new GMSHPlugin_Creator_i; + else if (strcmp(aHypName, "GMSH_3D_Remote") == 0) + aCreator = new GMSHPlugin_Creator_i; // Hypotheses else if (strcmp(aHypName, "GMSH_Parameters") == 0) aCreator = new GMSHPlugin_Creator_i; -- 2.39.2