1 // Copyright (C) 2007-2023 CEA, EDF, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 //=============================================================================
24 // File : GMSHPlugin_GMSH_3D_Remote.hxx
25 // Created : 09 Septembre 2023
26 // Author : Cesar Conopoima (OCC)
28 //=============================================================================
32 #include "GMSHPlugin_GMSH_3D_Remote.hxx"
33 #include "GMSHPlugin_Hypothesis.hxx"
34 #include "Utils_SALOME_Exception.hxx"
36 #include <SMESH_Gen.hxx>
37 #include <SMESH_Mesh.hxx>
38 #include <SMESH_ParallelMesh.hxx>
39 #include <SMESH_MesherHelper.hxx>
40 #include <SMESH_DriverShape.hxx>
41 #include <SMESH_DriverMesh.hxx>
42 #include <SMESHDS_Mesh.hxx>
43 #include <SMESH_MeshLocker.hxx>
44 #include <SMESH_ProxyMesh.hxx>
48 #include <TopExp_Explorer.hxx>
55 namespace fs = std::filesystem;
57 #include <boost/filesystem.hpp>
58 namespace fs = boost::filesystem;
61 //=============================================================================
65 //=============================================================================
67 GMSHPlugin_GMSH_3D_Remote::GMSHPlugin_GMSH_3D_Remote(int hypId, SMESH_Gen * gen)
68 : GMSHPlugin_GMSH_3D(hypId, gen)
70 _name = "GMSH_3D_Remote";
73 //=============================================================================
77 //=============================================================================
79 GMSHPlugin_GMSH_3D_Remote::~GMSHPlugin_GMSH_3D_Remote()
84 * @brief Fill the structure netgen_param with the information from the hypothesis
86 * @param param_file name of the file to saven the gmsh parameter
87 * @param hyp the hypothesis
89 void GMSHPlugin_GMSH_3D_Remote::exportGmshParams( const std::string param_file, const SMESHDS_Hypothesis* hyp )
91 std::ofstream myfile(param_file);
92 if ( const GMSHPlugin_Hypothesis* gmshHypo = dynamic_cast<const GMSHPlugin_Hypothesis*>(hyp) )
94 myfile << 1 << std::endl; // Mark existence of correct hypothesis
95 myfile << (int) gmshHypo->Get2DAlgo() << std::endl;
96 myfile << (int) gmshHypo->Get3DAlgo() << std::endl;
97 myfile << (int) gmshHypo->GetRecomb2DAlgo() << std::endl;
98 myfile << (int) gmshHypo->GetRecombineAll() << std::endl;
99 myfile << (int) gmshHypo->GetSubdivAlgo() << std::endl;
100 myfile << (int) gmshHypo->GetRemeshAlgo() << std::endl;
101 myfile << (double) gmshHypo->GetSmouthSteps() << std::endl;
102 myfile << (double) gmshHypo->GetSizeFactor() << std::endl;
103 #if GMSH_MAJOR_VERSION >=4 && GMSH_MINOR_VERSION >=10
104 myfile << (double) gmshHypo->GetMeshCurvatureSize() << std::endl;
106 myfile << -1.0 << std::endl; // dummy value writte for conformity
108 myfile << (double) gmshHypo->GetMaxSize() << std::endl;
109 myfile << (double) gmshHypo->GetMinSize() << std::endl;
110 myfile << (int) gmshHypo->GetSecondOrder() << std::endl;
111 myfile << (int) gmshHypo->GetUseIncomplElem() << std::endl;
112 if ( gmshHypo->GetCompoundOnEntries().size() > 0 )
114 TCompound defCompounds = gmshHypo->GetCompoundOnEntries();
115 for (TCompound::const_iterator it = defCompounds.begin(); it != defCompounds.end(); ++it )
117 std::string token = (it !=--defCompounds.end()) ? "," : "";
118 myfile << *it << token;
124 myfile << 0 << std::endl; // mark the absence of compounds
129 //Mark tha absence of parameters in the file
130 myfile << 0 << std::endl;
138 * @brief write in a binary file the orientation for each surface element of the mesh
140 * @param aMesh The mesh
141 * @param aShape the shape associated to the mesh
142 * @param output_file name of the binary file
144 void GMSHPlugin_GMSH_3D_Remote::exportElementOrientation(SMESH_Mesh& aMesh,
145 const TopoDS_Shape& aShape,
146 const std::string output_file)
148 SMESH_MesherHelper helper(aMesh);
149 SMESH_ProxyMesh::Ptr proxyMesh( new SMESH_ProxyMesh( aMesh ));
150 std::map<vtkIdType, bool> elemOrientation;
152 for ( TopExp_Explorer exFa( aShape, TopAbs_FACE ); exFa.More(); exFa.Next())
154 const TopoDS_Shape& aShapeFace = exFa.Current();
155 int faceID = aMesh.GetMeshDS()->ShapeToIndex( aShapeFace );
157 if ( helper.NbAncestors(aShapeFace, aMesh, aShape.ShapeType()) > 1 )
158 // IsReversedSubMesh() can work wrong on strongly curved faces,
159 // so we use it as less as possible
160 isRev = helper.IsReversedSubMesh( TopoDS::Face( aShapeFace ));
162 const SMESHDS_SubMesh * aSubMeshDSFace = proxyMesh->GetSubMesh( aShapeFace );
163 if ( !aSubMeshDSFace ) continue;
165 SMDS_ElemIteratorPtr iteratorElem = aSubMeshDSFace->GetElements();
167 while ( iteratorElem->more() ) // loop on elements on a geom face
170 const SMDS_MeshElement* elem = iteratorElem->next();
172 error( COMPERR_BAD_INPUT_MESH, "Null element encounters");
173 if ( elem->NbCornerNodes() != 3 )
174 error( COMPERR_BAD_INPUT_MESH, "Not triangle element encounters");
175 elemOrientation[elem->GetID()] = isRev;
176 } // loop on elements on a face
177 } // loop on faces of a SOLID or SHELL
180 std::ofstream df(output_file, ios::out|ios::binary);
181 int size = elemOrientation.size();
182 df.write((char*)&size, sizeof(int));
183 for(auto const& [id, orient]:elemOrientation)
185 df.write((char*)&id, sizeof(vtkIdType));
186 df.write((char*)&orient, sizeof(bool));
193 * @brief Compute mesh associate to shape
195 * @param aMesh The mesh
196 * @param aShape The shape
197 * @return true fi there are some error
199 bool GMSHPlugin_GMSH_3D_Remote::Compute(SMESH_Mesh& aMesh,
200 const TopoDS_Shape& aShape)
203 SMESH_MeshLocker myLocker(&aMesh);
204 SMESH_Hypothesis::Hypothesis_Status hypStatus;
205 GMSHPlugin_GMSH_3D::CheckHypothesis(aMesh, aShape, hypStatus); //in this call the _hypothesis is defined!
208 SMESH_ParallelMesh& aParMesh = dynamic_cast<SMESH_ParallelMesh&>(aMesh);
209 // Temporary folder for run
211 // On windows mesh does not have GetTmpFolder
212 fs::path tmp_folder = aParMesh.GetTmpFolder() / fs::path("Volume-%%%%-%%%%");
214 fs::path tmp_folder = aParMesh.GetTmpFolder() / fs::unique_path(fs::path("Volume-%%%%-%%%%"));
216 fs::create_directories(tmp_folder);
217 // Using MESH2D generated after all triangles where created.
218 fs::path mesh_file= aParMesh.GetTmpFolder() / fs::path("Mesh2D.med");
219 fs::path element_orientation_file=tmp_folder / fs::path("element_orientation.dat");
220 fs::path new_element_file=tmp_folder / fs::path("new_elements.dat");
221 //fs::path tmp_mesh_file=tmp_folder / fs::path("tmp_mesh.med");
222 // Not used kept for debug
223 // fs::path output_mesh_file=tmp_folder / fs::path("output_mesh.med");
224 fs::path shape_file=tmp_folder / fs::path("shape.brep");
225 fs::path param_file=tmp_folder / fs::path("gmsh_param.txt");
226 fs::path log_file=tmp_folder / fs::path("run.log");
227 fs::path cmd_file=tmp_folder / fs::path("cmd.txt");
229 std::string mesh_name = "MESH";
232 SMESH_MeshLocker myLocker(&aMesh);
234 SMESH_DriverShape::exportShape(shape_file.string(), aShape);
236 //Writing hypothesis to file
237 exportGmshParams(param_file.string(), _hypothesis);
239 // Exporting element orientation
240 exportElementOrientation(aMesh, aShape, element_orientation_file.string());
243 // Calling run_mesher
244 // Path to mesher script
245 fs::path mesher_launcher = fs::path(std::getenv("SMESH_ROOT_DIR"))/
248 fs::path("mesher_launcher.py");
250 std::string s_program="python3";
251 std::list<std::string> params;
252 params.push_back(mesher_launcher.string());
253 params.push_back("GMSH3D");
254 params.push_back(mesh_file.string());
255 params.push_back(shape_file.string());
256 params.push_back(param_file.string());
257 params.push_back("--elem-orient-file=" + element_orientation_file.string());
258 params.push_back("--new-element-file=" + new_element_file.string());
259 // params.push_back("--output-mesh-file=" + output_mesh_file.string());
261 // Parallelism method parameters
262 int method = aParMesh.GetParallelismMethod();
263 if(method == ParallelismMethod::MultiThread){
264 params.push_back("--method=local");
265 } else if (method == ParallelismMethod::MultiNode){
266 params.push_back("--method=cluster");
267 params.push_back("--resource="+aParMesh.GetResource());
268 params.push_back("--wc-key="+aParMesh.GetWcKey());
269 params.push_back("--nb-proc=1");
270 params.push_back("--nb-proc-per-node="+std::to_string(aParMesh.GetNbProcPerNode()));
271 params.push_back("--nb-node="+std::to_string(aParMesh.GetNbNode()));
272 params.push_back("--walltime="+aParMesh.GetWalltime());
274 throw SALOME_Exception("Unknown parallelism method "+method);
277 std::string cmd = "";
279 for(auto arg: params){
282 MESSAGE("Running command: ");
284 // Writing command in cmd.log
286 std::ofstream flog(cmd_file.string());
290 // Building arguments for QProcess
291 QString program = QString::fromStdString(s_program);
292 QStringList arguments;
293 for(auto arg : params){
294 arguments << arg.c_str();
297 QString out_file = log_file.string().c_str();
299 // myProcess.setProcessChannelMode(QProcess::MergedChannels);
300 myProcess.setProcessChannelMode(QProcess::ForwardedChannels);
301 myProcess.setStandardOutputFile(out_file);
303 myProcess.start(program, arguments);
304 // Waiting for process to finish (argument -1 make it wait until the end of
305 // the process otherwise it just waits 30 seconds)
306 bool finished = myProcess.waitForFinished(-1);
307 int ret = myProcess.exitCode();
308 if(ret != 0 || !finished){
310 std::string msg = "Issue with mesh_launcher: \n";
311 msg += "See log for more details: " + log_file.string() + "\n";
313 throw SALOME_Exception(msg);
316 SMESH_MeshLocker myLocker(&aMesh);
317 // Binary file written from SA version of the mesher
318 std::ifstream df(new_element_file.string(), ios::binary);
321 int totalNumberOfNodes;
327 SMESH_MesherHelper helper(aMesh);
328 // This function is mandatory for setElementsOnShape to work
329 helper.IsQuadraticSubMesh(aShape);
330 helper.SetElementsOnShape( true );
332 // Number of nodes in intial mesh
333 df.read((char*) &numberOfNodes, sizeof(int));
334 // Number of nodes added by netgen
335 df.read((char*) &totalNumberOfNodes, sizeof(int));
336 // Filling nodevec (correspondence netgen numbering mesh numbering)
337 std::vector< const SMDS_MeshNode* > nodeVec ( totalNumberOfNodes + 1 );
338 SMESHDS_Mesh * meshDS = helper.GetMeshDS();
339 for (int nodeIndex = 1 ; nodeIndex <= numberOfNodes; ++nodeIndex )
342 df.read((char*) &nodeID, sizeof(int));
343 nodeVec.at(nodeIndex) = meshDS->FindNode(nodeID);
346 // Add new points and update nodeVec
347 for (int nodeIndex = numberOfNodes +1; nodeIndex <= totalNumberOfNodes; ++nodeIndex )
349 df.read((char *) &meshNodes, sizeof(double)*3);
350 nodeVec.at(nodeIndex) = helper.AddNode(meshNodes[0], meshNodes[1], meshNodes[2]);
354 df.read((char*) &numberOfVolumes, sizeof(int));
355 for ( int elemIndex = 1; elemIndex <= numberOfVolumes; ++elemIndex )
357 df.read((char*) &volNodes, sizeof(int)*4);
358 auto n0 = meshDS->FindNode(nodeVec[volNodes[0]]->GetID());
359 auto n1 = meshDS->FindNode(nodeVec[volNodes[1]]->GetID());
360 auto n2 = meshDS->FindNode(nodeVec[volNodes[2]]->GetID());
361 auto n3 = meshDS->FindNode(nodeVec[volNodes[3]]->GetID());
362 if ( n0 && n1 && n2 && n3 )
363 helper.AddVolume( n0, n2, n1, n3 );
371 * @brief Assign submeshes to compute
373 * @param aSubMesh submesh to add
375 void GMSHPlugin_GMSH_3D_Remote::setSubMeshesToCompute(SMESH_subMesh * aSubMesh)
377 SMESH_MeshLocker myLocker(aSubMesh->GetFather());
378 SMESH_Algo::setSubMeshesToCompute(aSubMesh);