Salome HOME
updated copyright message
[plugins/netgenplugin.git] / src / NETGENPlugin / NETGENPlugin_NETGEN_3D_Remote.cxx
1 // Copyright (C) 2007-2023  CEA, EDF, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
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.
10 //
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.
15 //
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
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 //=============================================================================
24 // File      : NETGENPlugin_NETGEN_3D_Remote.cxx
25 // Created   : lundi 19 Septembre 2022
26 // Author    : Yoann AUDOUIN (CEA)
27 // Project   : SALOME
28 //=============================================================================
29 //
30 //
31 #include "NETGENPlugin_NETGEN_3D_Remote.hxx"
32
33 #include "NETGENPlugin_NETGEN_3D.hxx"
34
35 #include "NETGENPlugin_DriverParam.hxx"
36 #include "NETGENPlugin_Hypothesis.hxx"
37
38 #include "Utils_SALOME_Exception.hxx"
39
40 #include <SMESH_Gen.hxx>
41 #include <SMESH_Mesh.hxx>
42 #include <SMESH_MesherHelper.hxx>
43 #include <SMESH_DriverShape.hxx>
44 #include <SMESH_DriverMesh.hxx>
45 #include <SMESHDS_Mesh.hxx>
46 #include <SMESH_MeshLocker.hxx>
47
48 #include <QString>
49 #include <QProcess>
50
51 #ifdef WIN32
52 #include <filesystem>
53 namespace fs = std::filesystem;
54 #else
55 #include <boost/filesystem.hpp>
56 namespace fs = boost::filesystem;
57 #endif
58
59 /*
60   Netgen include files
61 */
62
63 #ifndef OCCGEOMETRY
64 #define OCCGEOMETRY
65 #endif
66 #include <occgeom.hpp>
67
68 #ifdef NETGEN_V5
69 #include <ngexception.hpp>
70 #endif
71 #ifdef NETGEN_V6
72 #include <core/exception.hpp>
73 #endif
74
75 namespace nglib {
76 #include <nglib.h>
77 }
78 namespace netgen {
79
80   NETGENPLUGIN_DLL_HEADER
81   extern MeshingParameters mparam;
82
83   NETGENPLUGIN_DLL_HEADER
84   extern volatile multithreadt multithread;
85 }
86 using namespace nglib;
87
88 //=============================================================================
89 /*!
90  * Constructor
91  */
92 //=============================================================================
93
94 NETGENPlugin_NETGEN_3D_Remote::NETGENPlugin_NETGEN_3D_Remote(int hypId, SMESH_Gen * gen)
95   : NETGENPlugin_NETGEN_3D(hypId, gen)
96 {
97   _name = "NETGEN_3D_Remote";
98 }
99
100 //=============================================================================
101 /*!
102  * Destructor
103  */
104 //=============================================================================
105
106 NETGENPlugin_NETGEN_3D_Remote::~NETGENPlugin_NETGEN_3D_Remote()
107 {
108 }
109
110 /**
111  * @brief Fill the structure netgen_param with the information from the hypothesis
112  *
113  * @param hyp the hypothesis
114  * @param aParams the netgen_param structure
115  */
116 void NETGENPlugin_NETGEN_3D_Remote::fillParameters(const NETGENPlugin_Hypothesis* hyp, netgen_params &aParams)
117 {
118   aParams.maxh               = hyp->GetMaxSize();
119   aParams.minh               = hyp->GetMinSize();
120   aParams.segmentsperedge    = hyp->GetNbSegPerEdge();
121   aParams.grading            = hyp->GetGrowthRate();
122   aParams.curvaturesafety    = hyp->GetNbSegPerRadius();
123   aParams.secondorder        = hyp->GetSecondOrder() ? 1 : 0;
124   aParams.quad               = hyp->GetQuadAllowed() ? 1 : 0;
125   aParams.optimize           = hyp->GetOptimize();
126   aParams.fineness           = hyp->GetFineness();
127   aParams.uselocalh          = hyp->GetSurfaceCurvature();
128   aParams.merge_solids       = hyp->GetFuseEdges();
129   aParams.chordalError       = hyp->GetChordalErrorEnabled() ? hyp->GetChordalError() : -1.;
130   aParams.optsteps2d         = aParams.optimize ? hyp->GetNbSurfOptSteps() : 0;
131   aParams.optsteps3d         = aParams.optimize ? hyp->GetNbVolOptSteps()  : 0;
132   aParams.elsizeweight       = hyp->GetElemSizeWeight();
133   aParams.opterrpow          = hyp->GetWorstElemMeasure();
134   aParams.delaunay           = hyp->GetUseDelauney();
135   aParams.checkoverlap       = hyp->GetCheckOverlapping();
136   aParams.checkchartboundary = hyp->GetCheckChartBoundary();
137 #ifdef NETGEN_V6
138   // std::string
139   aParams.meshsizefilename = hyp->GetMeshSizeFile();
140   aParams.closeedgefac = 2;
141   aParams.nbThreads = hyp->GetNbThreads();
142 #else
143   // const char*
144   aParams.meshsizefilename = hyp->GetMeshSizeFile();
145   aParams.closeedgefac = 0;
146   aParams.nbThreads = 0;
147 #endif
148 }
149
150 //
151
152 /**
153  * @brief write in a binary file the orientation for each surface element of the mesh
154  *
155  * @param aMesh The mesh
156  * @param aShape the shape associated to the mesh
157  * @param output_file name of the binary file
158  */
159 void NETGENPlugin_NETGEN_3D_Remote::exportElementOrientation(SMESH_Mesh& aMesh,
160                                                       const TopoDS_Shape& aShape,
161                                                       const std::string output_file)
162 {
163   SMESH_MesherHelper helper(aMesh);
164   NETGENPlugin_Internals internals( aMesh, aShape, /*is3D=*/true );
165   SMESH_ProxyMesh::Ptr proxyMesh( new SMESH_ProxyMesh( aMesh ));
166   std::map<vtkIdType, bool> elemOrientation;
167
168   for ( TopExp_Explorer exFa( aShape, TopAbs_FACE ); exFa.More(); exFa.Next())
169   {
170     const TopoDS_Shape& aShapeFace = exFa.Current();
171     int faceID = aMesh.GetMeshDS()->ShapeToIndex( aShapeFace );
172     bool isInternalFace = internals.isInternalShape( faceID );
173     bool isRev = false;
174     if ( !isInternalFace &&
175           helper.NbAncestors(aShapeFace, aMesh, aShape.ShapeType()) > 1 )
176       // IsReversedSubMesh() can work wrong on strongly curved faces,
177       // so we use it as less as possible
178       isRev = helper.IsReversedSubMesh( TopoDS::Face( aShapeFace ));
179
180     const SMESHDS_SubMesh * aSubMeshDSFace = proxyMesh->GetSubMesh( aShapeFace );
181     if ( !aSubMeshDSFace ) continue;
182
183     SMDS_ElemIteratorPtr iteratorElem = aSubMeshDSFace->GetElements();
184     if ( _quadraticMesh &&
185           dynamic_cast< const SMESH_ProxyMesh::SubMesh*>( aSubMeshDSFace ))
186     {
187       // add medium nodes of proxy triangles to helper (#16843)
188       while ( iteratorElem->more() )
189         helper.AddTLinks( static_cast< const SMDS_MeshFace* >( iteratorElem->next() ));
190
191       iteratorElem = aSubMeshDSFace->GetElements();
192     }
193     while ( iteratorElem->more() ) // loop on elements on a geom face
194     {
195       // check mesh face
196       const SMDS_MeshElement* elem = iteratorElem->next();
197       if ( !elem )
198         error( COMPERR_BAD_INPUT_MESH, "Null element encounters");
199       if ( elem->NbCornerNodes() != 3 )
200         error( COMPERR_BAD_INPUT_MESH, "Not triangle element encounters");
201       elemOrientation[elem->GetID()] = isRev;
202     } // loop on elements on a face
203   } // loop on faces of a SOLID or SHELL
204
205   {
206     std::ofstream df(output_file, ios::out|ios::binary);
207     int size=elemOrientation.size();
208
209     df.write((char*)&size, sizeof(int));
210     for(auto const& [id, orient]:elemOrientation){
211       df.write((char*)&id, sizeof(vtkIdType));
212       df.write((char*)&orient, sizeof(bool));
213     }
214   }
215 }
216
217 /**
218  * @brief Compute mesh associate to shape
219  *
220  * @param aMesh The mesh
221  * @param aShape The shape
222  * @return true fi there are some error
223  */
224 bool NETGENPlugin_NETGEN_3D_Remote::Compute(SMESH_Mesh&         aMesh,
225                                            const TopoDS_Shape& aShape)
226 {
227   {
228     SMESH_MeshLocker myLocker(&aMesh);
229     SMESH_Hypothesis::Hypothesis_Status hypStatus;
230     NETGENPlugin_NETGEN_3D::CheckHypothesis(aMesh, aShape, hypStatus);
231   }
232
233
234   // Temporary folder for run
235 #ifdef WIN32
236   // On windows mesh does not have GetTmpFolder
237   fs::path tmp_folder = fs::path("Volume-%%%%-%%%%");
238 #else
239   fs::path tmp_folder = aMesh.GetTmpFolder() / fs::unique_path(fs::path("Volume-%%%%-%%%%"));
240 #endif
241   fs::create_directories(tmp_folder);
242   // Using MESH2D generated after all triangles where created.
243 #ifdef WIN32
244   fs::path mesh_file=fs::path("Mesh2D.med");
245 #else
246   fs::path mesh_file=aMesh.GetTmpFolder() / fs::path("Mesh2D.med");
247 #endif
248   fs::path element_orientation_file=tmp_folder / fs::path("element_orientation.dat");
249   fs::path new_element_file=tmp_folder / fs::path("new_elements.dat");
250   fs::path tmp_mesh_file=tmp_folder / fs::path("tmp_mesh.med");
251   // Not used kept for debug
252   //fs::path output_mesh_file=tmp_folder / fs::path("output_mesh.med");
253   fs::path shape_file=tmp_folder / fs::path("shape.brep");
254   fs::path param_file=tmp_folder / fs::path("netgen3d_param.txt");
255   fs::path log_file=tmp_folder / fs::path("run.log");
256   fs::path cmd_file=tmp_folder / fs::path("cmd.log");
257   std::string mesh_name = "MESH";
258
259   {
260     SMESH_MeshLocker myLocker(&aMesh);
261     //Writing Shape
262     SMESH_DriverShape::exportShape(shape_file.string(), aShape);
263
264     //Writing hypo
265     netgen_params aParams;
266     fillParameters(_hypParameters, aParams);
267
268     exportNetgenParams(param_file.string(), aParams);
269
270     // Exporting element orientation
271     exportElementOrientation(aMesh, aShape, element_orientation_file.string());
272   }
273
274   // Calling run_mesher
275   std::string cmd;
276   fs::path run_mesher_exe =
277     fs::path(std::getenv("NETGENPLUGIN_ROOT_DIR"))/
278     fs::path("bin")/
279     fs::path("salome")/
280 #ifdef WIN32
281     fs::path("NETGENPlugin_Runner.exe");
282 #else
283     fs::path("NETGENPlugin_Runner");
284 #endif
285
286   cmd = run_mesher_exe.string() +
287                   " NETGEN3D " + mesh_file.string() + " "
288                                + shape_file.string() + " "
289                                + param_file.string() + " "
290                                + element_orientation_file.string() + " "
291                                + new_element_file.string() + " "
292                                + "NONE";
293   // Writing command in log
294   {
295     std::ofstream flog(cmd_file.string());
296     flog << cmd << endl;
297     flog << endl;
298   }
299   MESSAGE("Running command: ");
300   MESSAGE(cmd);
301
302
303   // Building arguments for QProcess
304   QString program = run_mesher_exe.string().c_str();
305   QStringList arguments;
306   arguments << "NETGEN3D";
307   arguments << mesh_file.string().c_str();
308   arguments << shape_file.string().c_str();
309   arguments << param_file.string().c_str();
310   arguments << element_orientation_file.string().c_str();
311   arguments << new_element_file.string().c_str();
312   arguments << "NONE";
313   QString out_file = log_file.string().c_str();
314   QProcess myProcess;
315   myProcess.setStandardOutputFile(out_file);
316
317   myProcess.start(program, arguments);
318   // Waiting for process to finish (argument -1 make it wait until the end of
319   // the process otherwise it just waits 30 seconds)
320   myProcess.waitForFinished(-1);
321   int ret = myProcess.exitStatus();
322
323   if(ret != 0){
324     // Run crahed
325     std::string msg = "Issue with command: \n";
326     msg += "See log for more details: " + log_file.string() + "\n";
327     msg += cmd + "\n";
328     throw SALOME_Exception(msg);
329   }
330
331   {
332     SMESH_MeshLocker myLocker(&aMesh);
333     std::ifstream df(new_element_file.string(), ios::binary);
334
335     int Netgen_NbOfNodes;
336     int Netgen_NbOfNodesNew;
337     int Netgen_NbOfTetra;
338     double Netgen_point[3];
339     int    Netgen_tetrahedron[4];
340     int nodeID;
341
342     SMESH_MesherHelper helper(aMesh);
343     // This function is mandatory for setElementsOnShape to work
344     helper.IsQuadraticSubMesh(aShape);
345     helper.SetElementsOnShape( true );
346
347     // Number of nodes in intial mesh
348     df.read((char*) &Netgen_NbOfNodes, sizeof(int));
349     // Number of nodes added by netgen
350     df.read((char*) &Netgen_NbOfNodesNew, sizeof(int));
351
352     // Filling nodevec (correspondence netgen numbering mesh numbering)
353     vector< const SMDS_MeshNode* > nodeVec ( Netgen_NbOfNodesNew + 1 );
354     //vector<int> nodeTmpVec ( Netgen_NbOfNodesNew + 1 );
355     SMESHDS_Mesh * meshDS = helper.GetMeshDS();
356     for (int nodeIndex = 1 ; nodeIndex <= Netgen_NbOfNodes; ++nodeIndex )
357     {
358       //Id of the point
359       df.read((char*) &nodeID, sizeof(int));
360       nodeVec.at(nodeIndex) = meshDS->FindNode(nodeID);
361     }
362
363     // Add new points and update nodeVec
364     for (int nodeIndex = Netgen_NbOfNodes +1 ; nodeIndex <= Netgen_NbOfNodesNew; ++nodeIndex )
365     {
366       df.read((char *) &Netgen_point, sizeof(double)*3);
367
368       nodeVec.at(nodeIndex) = helper.AddNode(Netgen_point[0],
369                                  Netgen_point[1],
370                                  Netgen_point[2]);
371     }
372
373     // Add tetrahedrons
374     df.read((char*) &Netgen_NbOfTetra, sizeof(int));
375
376     for ( int elemIndex = 1; elemIndex <= Netgen_NbOfTetra; ++elemIndex )
377     {
378       df.read((char*) &Netgen_tetrahedron, sizeof(int)*4);
379       helper.AddVolume(
380                     nodeVec.at( Netgen_tetrahedron[0] ),
381                     nodeVec.at( Netgen_tetrahedron[1] ),
382                     nodeVec.at( Netgen_tetrahedron[2] ),
383                     nodeVec.at( Netgen_tetrahedron[3] ));
384     }
385   }
386
387   return true;
388 }
389
390 /**
391  * @brief Assign submeshes to compute
392  *
393  * @param aSubMesh submesh to add
394  */
395 void NETGENPlugin_NETGEN_3D_Remote::setSubMeshesToCompute(SMESH_subMesh * aSubMesh)
396 {
397   SMESH_MeshLocker myLocker(aSubMesh->GetFather());
398   SMESH_Algo::setSubMeshesToCompute(aSubMesh);
399 }