2 # -*- coding: utf-8 -*-
4 # Copyright (C) 2008-2019 CEA/DEN
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
25 run mg_tetra_hpc.exe or mg_tetra_hpc_mpi.exe
28 | ./mg-tetra_hpc.py --help
29 | ./mg-tetra_hpc.py -n 3 --in /tmp/GHS3DPRL.mesh --out /tmp/GHS3DPRL_out.mesh --gradation 1.05 --min_size 0.001 --max_size 1.1 --multithread no > /tmp/tetrahpc.log
32 | openmpi environment have to be set, may be as user choice
33 | example for centos7/redhat7:
34 | module load mpi/openmpi-x86_64
36 | example without module load (old):
37 | # may be prefer set before salome launch
38 | OPENMPI_INSTALL_DIR = ".../openmpi-1.8.4/FROM_nothing/bin"
39 | export PATH=${OPENMPI_INSTALL_DIR}:${PATH}
48 import multiprocessing as MP # cpu_count
49 import pprint as PP # pretty print
50 import subprocess as SP # Popen
60 NB_PROCS = MP.cpu_count() # current cpu number of proc
61 NAME_OS = platform.system() # 'Linux' or 'Windows'
64 ##########################################################################
66 ##########################################################################
69 ##########################################################################
72 log_print("DEBUG", msg)
75 log_print("INFO", msg)
78 log_print("WARNING", msg)
81 log_print("ERROR", msg)
83 def log_print(level, msg):
84 print("%-7s : %s" % (level, indent(msg)))
86 def indent(msg, ind='\n | '):
88 return msg.replace('\n', ind)
99 ##########################################################################
100 def okToSys(aResult, verbose=False):
102 to get windows or linux result of script
105 def extendList(alist):
106 """utility extend list of lists of string results with ok or KO"""
107 # bad: list(itertools.chain.from_list(alist)) iterate on str
109 if type(alist) != list:
116 res.extend(extendList(i))
119 resList = extendList(aResult)
121 log_error("result no clear with empty list of OK/KO")
126 if ri[0:2].upper() != OK:
127 log_debug("KO in '%s'" % ri)
130 log_info("result is %s" % rr)
137 ##########################################################################
138 def getDirAndName(datafile):
140 returns (directory_realpath, os.getcwd(), name_file)
142 rootpath = os.getcwd()
143 path, namefile = os.path.split(os.path.realpath(datafile))
144 return (path, rootpath, namefile)
147 ##########################################################################
148 class ArgRange(object):
150 ArgumentParser utility for range float or in in arguments
152 def __init__(self, start, end):
156 def __eq__(self, other):
157 return self.start <= other <= self.end
160 return "[%s,%s]" % (self.start, self.end)
162 ##########################################################################
163 def exec_command(cmd, verbose=False):
164 """Exec ONE command with popen"""
166 time.sleep(3) # wait for (MPI) flush files
167 log_debug("launch subprocess:\n%s" % cmd)
169 pipe = SP.Popen(cmd, shell=True, stdout=SP.PIPE, stderr=SP.STDOUT)
170 except Exception as e:
171 log_error("Problem launching subprocess:\n%s" % cmd)
172 result = "KO Problem launching subprocess"
175 (out, error) = pipe.communicate()
178 log_info("subprocess log:\n%s" % out.decode("utf-8"))
179 time.sleep(3) # wait for (MPI) flush files
181 if pipe.returncode == 0: # return code of last command
182 result = "OK subprocess result (of last command)"
185 log_error("KO subprocess result (of last command): %s" % pipe.returncode)
186 result = "KO for:\n%s" % cmd
188 if error is not None:
189 log_error("KO subprocess stderr:\n%s" % error.decode("utf-8"))
190 result = "KO for:\n%s" % cmd
194 ##########################################################################
195 def force_DISTENE_LICENSE_FILE():
197 conditionaly overriding/set environ variable DISTENE_LICENSE_FILE,
198 from/if existing FORCE_DISTENE_LICENSE_FILE environ variable
199 (useful when test new version MeshGems with local DISTENE_LICENSE_FILE)
202 DISTENE_LICENSE_FILE=Use global envvar: DLIM8VAR
203 DLIM8VAR=dlim8 1:1:29030@132.166.151.49/84c419b8::87af... etc.
205 FORCE_DISTENE_LICENSE_FILE=/home/$USER/licence_meshgems2.3/dlim8.key
207 force = os.getenv("FORCE_DISTENE_LICENSE_FILE")
209 os.environ["DISTENE_LICENSE_FILE"] = force
210 os.environ["DLIM8VAR"] = "NOTHING"
213 ##########################################################################
214 def launchMultithread(args):
215 log_info("launch multithread for %s" % NAME_OS)
217 if NAME_OS == 'Linux':
218 # --out is ONE file: basename_tetra_hpc.mesh
219 args.outputs = os.path.splitext(args.outputFiles)[0] # use args.__dict__
220 args.currentdir = os.path.splitext(args.outputFiles)[0] # use args.__dict__
222 # args.outputMulti = os.path.splitext(args.inputFile)[0] + "_tetra_hpc.mesh" # only one file if mg-tetra_hpc.exe multithread
223 args.outputMultiAsMpi = os.path.splitext(args.outputFiles)[0] + ".000001.mesh" # create one output file named as mpi (as only one process)
228 # mpirun mg-tetra_hpc_mpi.exe
229 rm %(outputs)s* # clean previous case
231 --max_number_of_threads %(number)s \
233 --out %(outputFiles)s \
234 --gradation %(gradation)s \
235 --max_size %(max_size)s \
236 --min_size %(min_size)s
237 # create force symbolic link as no duplicate multithread output file as MPI output file (as only one process)
238 ln -sf %(outputFiles)s %(outputMultiAsMpi)s
240 # args.number, args.inputFile, args.gradation, args.max_size, args.min_size, outputMulti, outputMultiAsMpi, outputs
242 msg = "unknown operating system: %s" % NAME_OS
246 result = exec_command(cmd, verbose=True)
251 ##########################################################################
253 log_info("launch MPI for %s" % NAME_OS)
255 if NAME_OS == 'Linux':
256 args.outputs = os.path.splitext(args.outputFiles)[0] # use args.__dict__
257 args.currentdir = os.path.splitext(args.outputFiles)[0] # use args.__dict__
261 which mg-tetra_hpc_mpi.exe
262 if [ $? -ne 0 ] # exit if not found
267 # needed as openmpi environment have to be set, may be as user or root (for cluster) choice
268 which mpirun || module load mpi/openmpi-x86_64 # example for centos7/redhat7
270 # compile libmeshgems_mpi.so if needed (i.e. if empty stubs)
271 mg-tetra_hpc_mpi.exe --help &> /tmp/help_mg-tetra_hpc_mpi_${USER}.txt
272 if [ $? -ne 0 ] # if empty MPI stubs library, have to compile libmeshgems_mpi.so
274 # SALOME env var MESHGEMSHOME is something like .../INSTALL/MeshGems
275 MESHGEMSHOME=${MESHGEMSHOME:-/tmp/MeshGems} # default value if unset
276 COMPILDIR=$MESHGEMSHOME/stubs
277 TARGETDIR=$MESHGEMSHOME/lib/Linux_64
279 mpicc meshgems_mpi.c -DMESHGEMS_LINUX_BUILD -I../include -shared -fPIC -o ${TARGETDIR}/libmeshgems_mpi.so
282 # mpirun mg-tetra_hpc_mpi.exe
283 rm %(outputs)s* # clean previous case
285 mpirun --n %(number)s mg-tetra_hpc_mpi.exe \
287 --out %(outputFiles)s \
288 --gradation %(gradation)s \
289 --max_size %(max_size)s \
290 --min_size %(min_size)s
291 # ls -alt %(outputs)s*
293 # args.number, args.inputFile, args.outputFiles, args.gradation, args.max_size, args.min_size, outputs
295 msg = "unknown operating system: %s" % NAME_OS
299 result = exec_command(cmd, verbose=True)
303 ##########################################################################
305 parser = AP.ArgumentParser(description='launch tetra_hpc.exe or tetra_hpc_mpi.exe for mesh computation', argument_default=None)
309 help='set verbose, for debug',
314 help='make test, for script debug',
319 help='if multithread: number of threads, else distributed: number of processes MPI',
320 choices=[ArgRange(1, 999999)],
322 metavar='integer >= 0',
323 default=NB_PROCS # as automatic, no more local number of cpu (multithread or MPI)
326 '-m', '--multithread',
327 help='launch tetra_hpc multithread instead tetra_hpc distributed (MPI)',
328 choices=["no", "yes"],
333 help='size ratio adjacent cell, default 0 is 1.05',
335 choices=[ArgRange(0.0, 3.0)],
336 metavar='float in [0.,3]',
341 help='min size cell, default 0 is no constraint',
343 choices=[ArgRange(0.0, 9e99)],
344 metavar='float >= 0',
349 help='max size cell, default 0 is no constraint',
351 choices=[ArgRange(0.0, 9e99)],
352 metavar='float >= 0',
357 help='input file name',
359 metavar='.../inputFile.mesh',
360 default='/tmp/GHS3DPRL.mesh'
363 '-o', '--outputFiles',
364 help='output basename file(s) name',
366 metavar='.../outputFile.mesh',
367 default='/tmp/GHS3DPRL_out.mesh'
372 '-x', '--xoneargument',
376 help='one argument, for example',
383 ##########################################################################
385 ##########################################################################
387 if __name__ == '__main__':
389 args is 'Namespace' class, may use it as global to store
390 parameters, data, used arrays and results, and other...
393 args = parser.parse_args()
395 verbose = args.verbose
396 log_debug("%s arguments are:\n%s" % (__file__, PP.pformat(args.__dict__)))
398 if len(sys.argv) == 1: # no args as --help
402 if args.test is True:
403 test_log('one line message')
404 test_log('first line\nsecond line')
407 if args.inputFile == None:
408 log_error("no --inputFile defined in arguments:\n%s" % PP.pformat(args.__dict__))
409 log_info("arguments should be:\n%s" % parser.format_help())
410 # parser.print_help()
413 if not os.path.isfile(args.inputFile):
414 log_error("inexisting input file:\n%s" % os.path.realpath(args.inputFile))
417 if args.outputFiles == None:
418 tmp, _ = os.path.splitext(args.inputFile)
419 args.outputFiles = tmp + "_out.mesh"
421 tmp, _ = os.path.split(args.outputFiles)
422 args.outputDir = os.path.realpath(tmp)
424 log_info("output directory:\n%s" % args.outputDir)
426 force_DISTENE_LICENSE_FILE() # if FORCE_DISTENE_LICENSE_FILE environ variable exists, manually set by user
428 DLIM8VAR = os.getenv("DLIM8VAR")
429 DISTENE_LICENSE_FILE = os.getenv("DISTENE_LICENSE_FILE")
431 %s assume distene licence file set:
433 DISTENE_LICENSE_FILE=%s
434 """ % (__file__, DLIM8VAR, DISTENE_LICENSE_FILE)
436 if None in [DLIM8VAR, DISTENE_LICENSE_FILE]:
437 log_error(msg + "Abandon.")
442 if args.multithread == "yes":
443 result = launchMultithread(args)
445 result = launchMpi(args)
447 sys.exit(okToSys(result, verbose=True))