1 // Copyright (C) 2007-2021 CEA/DEN, EDF R&D
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 // Lesser General Public License for more details.
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 // File : GHS3DPRLPlugin_GHS3DPRL.cxx
22 // Author : Christian VAN WAMBEKE (CEA) (from Hexotic plugin Lioka RAZAFINDRAZAKA)
25 #include "GHS3DPRLPlugin_GHS3DPRL.hxx"
26 #include "GHS3DPRLPlugin_Hypothesis.hxx"
27 #include "MG_TetraHPC_API.hxx"
29 #include <SMESHDS_Mesh.hxx>
30 #include <SMESH_Gen.hxx>
31 #include <SMESH_TypeDefs.hxx>
32 #include <SMESH_subMesh.hxx>
33 #include <SMESH_MesherHelper.hxx>
35 #include "utilities.h"
39 #include <BRepGProp.hxx>
40 #include <GProp_GProps.hxx>
41 #include <OSD_File.hxx>
42 #include <Standard_ProgramError.hxx>
43 #include <TCollection_AsciiString.hxx>
44 #include <TopExp_Explorer.hxx>
45 #include <TopTools_MapOfShape.hxx>
47 #include <TopoDS_Edge.hxx>
48 #include <TopoDS_Face.hxx>
50 #define GMFVERSION GmfDouble
51 #define GMFDIMENSION 3
55 static void removeFile( const TCollection_AsciiString& fileName )
58 OSD_File( fileName ).Remove();
60 catch ( Standard_ProgramError& ) {
61 MESSAGE("Can't remove file: " << fileName.ToCString() << " ; file does not exist or permission denied");
65 //=============================================================================
66 GHS3DPRLPlugin_GHS3DPRL::GHS3DPRLPlugin_GHS3DPRL(int hypId, SMESH_Gen* gen)
67 : SMESH_3D_Algo(hypId, gen)
69 MESSAGE("GHS3DPRLPlugin_GHS3DPRL::GHS3DPRLPlugin_GHS3DPRL");
70 _name = "MG-Tetra Parallel";
71 _shapeType = (1 << TopAbs_SHELL) | (1 << TopAbs_SOLID);// 1 bit /shape type
72 _onlyUnaryInput = false; // Compute() will be called on a compound of solids
75 _compatibleHypothesis.push_back(GHS3DPRLPlugin_Hypothesis::GetHypType());
79 //=============================================================================
80 GHS3DPRLPlugin_GHS3DPRL::~GHS3DPRLPlugin_GHS3DPRL()
82 MESSAGE("GHS3DPRLPlugin_GHS3DPRL::~GHS3DPRLPlugin_GHS3DPRL");
85 //=============================================================================
86 bool GHS3DPRLPlugin_GHS3DPRL::CheckHypothesis
88 const TopoDS_Shape& aShape,
89 SMESH_Hypothesis::Hypothesis_Status& aStatus)
91 //MESSAGE("GHS3DPRLPlugin_GHS3DPRL::CheckHypothesis");
94 list<const SMESHDS_Hypothesis*>::const_iterator itl;
95 const SMESHDS_Hypothesis* theHyp;
97 const list<const SMESHDS_Hypothesis*>& hyps = GetUsedHypothesis(aMesh, aShape);
98 int nbHyp = hyps.size();
101 aStatus = SMESH_Hypothesis::HYP_MISSING;
102 return false; // can't work with no hypothesis
106 theHyp = (*itl); // use only the first hypothesis
108 string hypName = theHyp->GetName();
109 if (hypName == GHS3DPRLPlugin_Hypothesis::GetHypType())
111 _hypothesis = static_cast<const GHS3DPRLPlugin_Hypothesis*> (theHyp);
113 aStatus = SMESH_Hypothesis::HYP_OK;
116 aStatus = SMESH_Hypothesis::HYP_INCOMPATIBLE;
118 return aStatus == SMESH_Hypothesis::HYP_OK;
121 //=============================================================================
122 // Pass parameters to GHS3DPRL
123 void GHS3DPRLPlugin_GHS3DPRL::SetParameters(const GHS3DPRLPlugin_Hypothesis* hyp)
126 MESSAGE("GHS3DPRLPlugin_GHS3DPRL::SetParameters");
127 _MEDName = hyp->GetMEDName(); //"DOMAIN\0"
128 _NbPart = hyp->GetNbPart();
129 _KeepFiles = hyp->GetKeepFiles();
130 _Background = hyp->GetBackground();
131 _Multithread = hyp->GetMultithread();
132 _Gradation = hyp->GetGradation();
133 _MinSize = hyp->GetMinSize();
134 _MaxSize = hyp->GetMaxSize();
135 _AdvOptions = hyp->GetAdvancedOption();
139 //=======================================================================
140 //before launching salome
141 //SALOME_TMP_DIR (for keep tepal intermediates files) could be set in user's directories
142 static TCollection_AsciiString getTmpDir()
144 TCollection_AsciiString aTmpDir;
145 char *Tmp_dir = getenv("SALOME_TMP_DIR");
146 if (Tmp_dir == NULL) Tmp_dir = getenv("TMP");
151 if(aTmpDir.Value(aTmpDir.Length()) != '\\') aTmpDir+='\\';
153 if(aTmpDir.Value(aTmpDir.Length()) != '/') aTmpDir+='/';
159 aTmpDir = TCollection_AsciiString("C:\\");
161 aTmpDir = TCollection_AsciiString("/tmp/");
167 //=============================================================================
168 // Write a skin mesh into a GMF file or pass it to MG-TetraHPC API
169 static void exportGMF(MG_TetraHPC_API* theTetraInput,
171 const SMESHDS_Mesh* theMeshDS)
173 int meshID = theTetraInput->GmfOpenMesh( theFile, GmfWrite, GMFVERSION, GMFDIMENSION);
176 int iN = 0, nbNodes = theMeshDS->NbNodes();
177 theTetraInput->GmfSetKwd( meshID, GmfVertices, nbNodes );
178 std::map< const SMDS_MeshNode*, int, TIDCompare > node2IdMap;
179 SMDS_NodeIteratorPtr nodeIt = theMeshDS->nodesIterator();
181 while ( nodeIt->more() )
183 n.Set( nodeIt->next() );
184 theTetraInput->GmfSetLin( meshID, GmfVertices, n.X(), n.Y(), n.Z(), n._node->getshapeId() );
185 node2IdMap.insert( node2IdMap.end(), std::make_pair( n._node, ++iN ));
189 SMDS_ElemIteratorPtr elemIt = theMeshDS->elementGeomIterator( SMDSGeom_TRIANGLE );
190 if ( elemIt->more() )
192 int nbTria = theMeshDS->GetMeshInfo().NbElements( SMDSGeom_TRIANGLE );
193 theTetraInput->GmfSetKwd(meshID, GmfTriangles, nbTria );
194 for ( int gmfID = 1; elemIt->more(); ++gmfID )
196 const SMDS_MeshElement* tria = elemIt->next();
197 theTetraInput->GmfSetLin(meshID, GmfTriangles,
198 node2IdMap[ tria->GetNode( 0 )],
199 node2IdMap[ tria->GetNode( 1 )],
200 node2IdMap[ tria->GetNode( 2 )],
201 tria->getshapeId() );
204 theTetraInput->GmfCloseMesh( meshID );
207 //=============================================================================
208 // Here we are going to use the GHS3DPRL mesher for tetra-hpc (formerly tepal in v3 (2014))
209 bool GHS3DPRLPlugin_GHS3DPRL::Compute(SMESH_Mesh& theMesh,
210 const TopoDS_Shape& /*theShape*/)
212 SMESH_MesherHelper helper( theMesh );
213 bool ok = Compute( theMesh, &helper );
217 //=============================================================================
218 // Here we are going to use the GHS3DPRL mesher for tetra-hpc (formerly tepal in v3 (2014))
219 bool GHS3DPRLPlugin_GHS3DPRL::Compute(SMESH_Mesh& theMesh,
220 SMESH_MesherHelper* /*theHelper*/)
223 TCollection_AsciiString pluginerror("ghs3dprl: ");
224 SMESHDS_Mesh* meshDS = theMesh.GetMeshDS();
225 if ( theMesh.NbTriangles() == 0 )
226 return error( COMPERR_BAD_INPUT_MESH, "No triangles in the mesh" );
228 if (_hypothesis==NULL){
229 pluginerror += "No existing parameters/hypothesis for GHS3DPRL";
230 cout <<"\n"<<pluginerror<<"\n\n";
231 error(COMPERR_ALGO_FAILED, pluginerror.ToCString() );
234 SetParameters(_hypothesis);
235 cout << "\n" << _name << " parameters:" << endl;
236 cout << " generic path/name of MED Files = " << _MEDName << endl;
237 cout << " number of partitions = " << _NbPart << endl;
238 cout << " gradation = " << _Gradation << endl;
239 cout << " min_size = " << _MinSize << endl;
240 cout << " max_size = " << _MaxSize << endl;
241 cout << " keep intermediates files (from tetra-hpc) = " << _KeepFiles << endl;
242 cout << " background (from tetra-hpc) = " << _Background << "\n";
243 cout << " multithread = " << _Multithread << "\n\n";
244 cout << " adv. options = '" << _AdvOptions << "'\n\n";
246 TCollection_AsciiString
253 run_GHS3DPRL("tetrahpc2med "),
263 casenamemed; //_MEDName.c_str());
264 int res = 0; // 0 is OK
266 casenamemed += (char *)_MEDName.c_str();
267 int n=casenamemed.SearchFromEnd('/');
269 path=casenamemed.SubString(1,n);
270 casenamemed=casenamemed.SubString(n+1,casenamemed.Length());
275 if (casenamemed.Length()>20){
276 casenamemed=casenamemed.SubString(1,20);
277 cerr<<"MEDName truncated (no more 20 characters) = "<<casenamemed<<endl;
280 map <int,int> aSmdsToGHS3DPRLIdMap;
281 map <int,const SMDS_MeshNode*> aGHS3DPRLIdToNodeMap;
282 GHS3DPRL_In = path + "GHS3DPRL";
283 GHS3DPRL_Out = path + casenamemed;
284 GHS3DPRL_Out_Mesh = path + casenamemed + "_out.mesh";
285 GHS3DPRL_Outxml = path + casenamemed + ".xml"; //master file
286 logFileName = path + casenamemed + ".log"; // MG library output
288 Gradation=_Gradation;
293 //tetrahpc2med --casename=/home/whoami/tmp/GHS3DPRL --number=5 --medname=DOMAIN
294 // --gradation=1.05 --min_size=1e-3 --max_size=1e-2
295 // --verbose=0 --menu=no --launchtetra=yes;
297 run_GHS3DPRL = run_GHS3DPRL +
298 " --casename=" + GHS3DPRL_In +
299 " --number=" + NbPart +
300 " --medname=" + GHS3DPRL_Out +
301 " --launchtetra=yes" +
302 " --gradation=" + Gradation +
303 " --min_size=" + MinSize +
304 " --max_size=" + MaxSize +
306 " " + _AdvOptions.c_str();
307 if (_Background) run_GHS3DPRL += " --background=yes"; else run_GHS3DPRL += " --background=no";
308 if (_Multithread) run_GHS3DPRL += " --multithread=yes"; else run_GHS3DPRL += " --multithread=no";
309 run_nokeep_files = rm +GHS3DPRL_In + "* " + path + "tetrahpc.log";
310 system( run_nokeep_files.ToCString() ); //clean files
311 run_nokeep_files = rm + GHS3DPRL_In + "* ";
313 fileskinmesh=path + "GHS3DPRL.mesh";
314 GHS3DPRL_Out = path + casenamemed;
315 removeFile( GHS3DPRL_Outxml ); //only the master xml file
317 MG_TetraHPC_API mgTetraHPC( _computeCanceled, _progress );
318 bool useLib = ( mgTetraHPC.IsLibrary() && !_Background && _Multithread );
320 mgTetraHPC.SetUseExecutable();
322 exportGMF( &mgTetraHPC, fileskinmesh.ToCString(), meshDS );
324 if ( true /*useLib*/ )
326 TCollection_AsciiString cmd = TCollection_AsciiString("mg-tetra_hpc.exe") +
327 " --number_of_subdomains=" + NbPart +
328 " --gradation=" + Gradation +
329 " --min_size=" + MinSize +
330 " --max_size=" + MaxSize +
332 " --out=" + GHS3DPRL_Out_Mesh +
333 " " + _AdvOptions.c_str();
336 << " Run mg-tetra_hpc as library. Creating a mesh file " << GHS3DPRL_Out_Mesh << endl
337 << " Creating a log file : " << logFileName << endl << endl;
338 mgTetraHPC.SetLogFile( logFileName.ToCString() );
341 mgTetraHPC.Compute( cmd.ToCString(), log );
344 log = mgTetraHPC.GetLog();
345 if ( log.find(" Dlim " ) != std::string::npos ||
346 log.find(" license ") != std::string::npos )
347 return error("License problem");
349 // set --launchtetra=no
350 int yesPos = run_GHS3DPRL.Search("launchtetra") + sizeof("launchtetra");
351 run_GHS3DPRL.SetValue( yesPos+0, 'n' );
352 run_GHS3DPRL.SetValue( yesPos+1, 'o' );
353 run_GHS3DPRL.SetValue( yesPos+2, ' ' );
357 cout<<" Write input file for mg-tetra_hpc "<<fileskinmesh<<"...";
360 fileskinmed=path + "GHS3DPRL_skin.med";
361 cout<<" Write file "<<fileskinmed<<"...";
362 theMesh.ExportMED(fileskinmed.ToCString(),"SKIN_INITIAL",true);
363 cout<<" ...done\n\n";
365 cout<<"GHS3DPRL command :\n "<<run_GHS3DPRL.ToCString()<<endl;
366 //sometimes it is better to wait flushing files on slow filesystem...
368 //launch tetrahpc2med which launch mg-tetra_hpc.py which launch mg-tetra_hpc(_mpi?).exe
369 res = system( run_GHS3DPRL.ToCString() );
372 pluginerror = pluginerror + "PROBLEM tetrahpc2med command";
373 cout<<pluginerror<<endl;
374 error(COMPERR_ALGO_FAILED, pluginerror.ToCString());
375 return false; //but it is not a problem but if true my message is overwritten
380 pluginerror = pluginerror + "backgrounding... plugin is not waiting for output files "+ casenamemed + "_*.med";
381 cout<<pluginerror<<endl;
382 error(COMPERR_NO_MESH_ON_SHAPE, pluginerror.ToCString());
383 return false; //but it is not a problem but if true my message is overwritten
384 //return true; //but it is not a problem,
387 // read a result, GHS3DPRL_Out is the name of master file (previous xml format)
388 FILE * aResultFile = fopen( GHS3DPRL_Outxml.ToCString(), "r" );
392 cout<<"GHS3DPRL OK output master file "<<casenamemed<<".xml exist !\n\n";
393 pluginerror = pluginerror + "MG-tetra_hpc mesh(es) not loaded in memory, are stored in files "+ casenamemed + "_*.med";
394 cout<<pluginerror<<endl;
395 error(COMPERR_WARNING, pluginerror.ToCString() );
396 if (!_KeepFiles) system( run_nokeep_files.ToCString() );
399 Ok = false; //it is a problem AND my message is NOT overwritten
400 pluginerror = pluginerror + "output master file " + casenamemed + ".xml do not exist";
401 cout<<pluginerror<<endl;
402 error(COMPERR_ALGO_FAILED, pluginerror.ToCString() );
403 cout<<"GHS3DPRL KO output files "<<GHS3DPRL_Out<<" do not exist ! see intermediates files keeped:\n";
404 TCollection_AsciiString run_list_files("ls -alt ");
405 run_list_files += GHS3DPRL_Out + "* " + GHS3DPRL_In + "* " + path + "tetrahpc.log";
406 system( run_list_files.ToCString() );
415 //=============================================================================
419 //=============================================================================
420 bool GHS3DPRLPlugin_GHS3DPRL::Evaluate(SMESH_Mesh& aMesh,
421 const TopoDS_Shape& aShape,
422 MapShapeNbElems& aResMap)
424 smIdType nbtri = 0, nbqua = 0;
425 double fullArea = 0.0;
426 for (TopExp_Explorer exp(aShape, TopAbs_FACE); exp.More(); exp.Next()) {
427 TopoDS_Face F = TopoDS::Face( exp.Current() );
428 SMESH_subMesh *sm = aMesh.GetSubMesh(F);
429 MapShapeNbElemsItr anIt = aResMap.find(sm);
430 if( anIt==aResMap.end() ) {
431 SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
432 smError.reset( new SMESH_ComputeError(COMPERR_ALGO_FAILED,
433 "Submesh can not be evaluated",this));
436 std::vector<smIdType> aVec = (*anIt).second;
437 nbtri += std::max(aVec[SMDSEntity_Triangle],aVec[SMDSEntity_Quad_Triangle]);
438 nbqua += std::max(aVec[SMDSEntity_Quadrangle],aVec[SMDSEntity_Quad_Quadrangle]);
440 BRepGProp::SurfaceProperties(F,G);
441 double anArea = G.Mass();
445 // collect info from edges
446 smIdType nb0d_e = 0, nb1d_e = 0;
447 bool IsQuadratic = false;
449 TopTools_MapOfShape tmpMap;
450 for (TopExp_Explorer exp(aShape, TopAbs_EDGE); exp.More(); exp.Next()) {
451 const TopoDS_Edge& E = TopoDS::Edge(exp.Current());
452 if( tmpMap.Contains(E) )
455 SMESH_subMesh *aSubMesh = aMesh.GetSubMesh(exp.Current());
456 MapShapeNbElemsItr anIt = aResMap.find(aSubMesh);
457 std::vector<smIdType> aVec = (*anIt).second;
458 nb0d_e += aVec[SMDSEntity_Node];
459 nb1d_e += std::max(aVec[SMDSEntity_Edge],aVec[SMDSEntity_Quad_Edge]);
461 IsQuadratic = (aVec[SMDSEntity_Quad_Edge] > aVec[SMDSEntity_Edge]);
467 double ELen = sqrt(2.* ( fullArea/(nbtri+nbqua*2) ) / sqrt(3.0) );
470 BRepGProp::VolumeProperties(aShape,G);
471 double aVolume = G.Mass();
472 double tetrVol = 0.1179*ELen*ELen*ELen;
473 double CoeffQuality = 0.9;
474 smIdType nbVols = (smIdType)aVolume/tetrVol/CoeffQuality;
475 smIdType nb1d_f = (nbtri*3 + nbqua*4 - nb1d_e) / 2;
476 smIdType nb1d_in = (smIdType) ( nbVols*6 - nb1d_e - nb1d_f ) / 5;
477 std::vector<smIdType> aVec(SMDSEntity_Last);
478 for(smIdType i=0; i<SMDSEntity_Last; i++) aVec[i]=0;
480 aVec[SMDSEntity_Node] = nb1d_in/6 + 1 + nb1d_in;
481 aVec[SMDSEntity_Quad_Tetra] = nbVols - nbqua*2;
482 aVec[SMDSEntity_Quad_Pyramid] = nbqua;
485 aVec[SMDSEntity_Node] = nb1d_in/6 + 1;
486 aVec[SMDSEntity_Tetra] = nbVols - nbqua*2;
487 aVec[SMDSEntity_Pyramid] = nbqua;
489 SMESH_subMesh *sm = aMesh.GetSubMesh(aShape);
490 aResMap.insert(std::make_pair(sm,aVec));