Salome HOME
ok mpi and multithread, see example/ghs3dprh_multithread_cube_one_face.py
[plugins/ghs3dprlplugin.git] / bin / mg-tetra_hpc.py
old mode 100644 (file)
new mode 100755 (executable)
index 30bb1e5..b244d13
 run mg_tetra_hpc.exe or mg_tetra_hpc_mpi.exe
 
 example linux usage:
-- simple run:
-  ./mg-tetra_hpc.py --help
-  ./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
+| ./mg-tetra_hpc.py --help
+|  ./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
+
+note:
+|  openmpi environment have to be set, may be as user choice
+|  example for centos7/redhat7:
+|    module load mpi/openmpi-x86_64
+|    which mpicc
+|  example without module load (old):
+|    # may be prefer set before salome launch
+|    OPENMPI_INSTALL_DIR = ".../openmpi-1.8.4/FROM_nothing/bin"
+|    export PATH=${OPENMPI_INSTALL_DIR}:${PATH}
 """
 
 import os
@@ -41,14 +50,14 @@ import pprint as PP  # pretty print
 import subprocess as SP  # Popen
 
 
-verbose = False
+debug = False
 
-OK = "ok"
+OK = "OK"
 KO = "KO"
 OKSYS = 0  # for linux
 KOSYS = 1  # for linux
 
-NB_PROCS = MP.cpu_count()  # current cpu number of proc
+NB_PROCS = MP.cpu_count()    # current cpu number of proc
 NAME_OS = platform.system()  # 'Linux' or 'Windows'
 
 
@@ -56,8 +65,42 @@ NAME_OS = platform.system()  # 'Linux' or 'Windows'
 # utilities
 ##########################################################################
 
+
+##########################################################################
+def log_debug(msg):
+  if args.verbose: 
+    log_print("DEBUG", msg)
+
+def log_info(msg):
+  log_print("INFO", msg)
+
+def log_warning(msg):
+  log_print("WARNING", msg)
+
+def log_error(msg):
+  log_print("ERROR", msg)
+
+def log_print(level, msg):
+  print("%-7s : %s" % (level, indent(msg)))
+
+def indent(msg, ind='\n | '):
+  if '\n' in msg:
+    return msg.replace('\n', ind)
+  else:
+    return msg
+
+def test_log(msg):
+  log_debug(msg)
+  log_info(msg)
+  log_warning(msg)
+  log_error(msg)
+
+
+##########################################################################
 def okToSys(aResult, verbose=False):
-  """to get windows or linux result of script"""
+  """
+  to get windows or linux result of script
+  """
 
   def extendList(alist):
     """utility extend list of lists of string results with ok or KO"""
@@ -75,25 +118,29 @@ def okToSys(aResult, verbose=False):
 
   resList = extendList(aResult)
   if resList == []:
-    if verbose: print("WARNING: result no clear: []")
+    log_error("result no clear with empty list of OK/KO")
     return KOSYS
 
   rr = OK
   for ri in resList:
-    if ri[0:2] != OK:
-      if verbose: print(ri)
+    if ri[0:2].upper() != OK:
+      log_debug("KO in '%s'" % ri)
       rr = KO
 
-  if verbose: print(("INFO: result: %s" % rr))
+  log_info("result is %s" % rr)
   if rr == OK:
     return OKSYS
   else:
     return KOSYS
 
+
 ##########################################################################
 def getDirAndName(datafile):
-  path, namefile = os.path.split(os.path.realpath(datafile))
+  """
+  returns (directory_realpath, os.getcwd(), name_file)
+  """
   rootpath = os.getcwd()
+  path, namefile = os.path.split(os.path.realpath(datafile))
   return (path, rootpath, namefile)
 
 
@@ -112,74 +159,89 @@ class ArgRange(object):
   def __repr__(self):
     return "[%s,%s]" % (self.start, self.end)
 
-
 ##########################################################################
 def exec_command(cmd, verbose=False):
   """Exec ONE command with popen"""
 
   time.sleep(3)  # wait for (MPI) flush files
-  if verbose: print(("launch process:\n  %s" % cmd))
+  log_debug("launch subprocess:\n%s" % cmd)
   try:
-    pipe = SP.Popen(cmd, shell=True, stdout=SP.PIPE, stderr=SP.PIPE)
+    pipe = SP.Popen(cmd, shell=True, stdout=SP.PIPE, stderr=SP.STDOUT)
   except Exception as e:
-    result = KO + " ERROR: we have a problem popen on: %s" % PP.pformat(cmd)
+    log_error("Problem launching subprocess:\n%s" % cmd)
+    result = "KO Problem launching subprocess"
     return result
 
   (out, error) = pipe.communicate()
   pipe.wait()
 
-  print(out)
-  print(error)
-
-  result = OK + " INFO: seems final ok for: %s" % PP.pformat(cmd)
+  log_info("subprocess log:\n%s" % out.decode("utf-8"))
   time.sleep(3)  # wait for (MPI) flush files
-  return result
 
+  if pipe.returncode == 0: # return code of last command
+    result = "OK subprocess result (of last command)"
+    log_debug(result)
+  else:
+    log_error("KO subprocess result (of last command): %s" % pipe.returncode)
+    result = "KO for:\n%s" % cmd
+  
+  if error is not None:
+    log_error("KO subprocess stderr:\n%s" % error.decode("utf-8"))
+    result = "KO for:\n%s" % cmd
 
+  return result
 
 ##########################################################################
 def force_DISTENE_LICENSE_FILE():
   """
   conditionaly overriding/set environ variable DISTENE_LICENSE_FILE,
   from/if existing FORCE_DISTENE_LICENSE_FILE environ variable
-  (for test new version MeshGems etc...)
-  """
-  """
-  #example:
-  DISTENE_LICENSE_FILE=Use global envvar: DLIM8VAR
-  DLIM8VAR=dlim8 1:1:29030@132.166.151.49/84c419b8::87af196ab2a936ab31363624539bff8096fbe1f3c83028c8f6b399b0a904ef85
-  overriden by
-  FORCE_DISTENE_LICENSE_FILE=/export/home/wambeke/essai_meshgems2.3/dlim8.key
+  (useful when test new version MeshGems with local DISTENE_LICENSE_FILE)
+  
+  example:
+    DISTENE_LICENSE_FILE=Use global envvar: DLIM8VAR
+    DLIM8VAR=dlim8 1:1:29030@132.166.151.49/84c419b8::87af... etc.
+    overriden by
+    FORCE_DISTENE_LICENSE_FILE=/home/$USER/licence_meshgems2.3/dlim8.key
   """
   force = os.getenv("FORCE_DISTENE_LICENSE_FILE")
   if force != None:
     os.environ["DISTENE_LICENSE_FILE"] = force
     os.environ["DLIM8VAR"] = "NOTHING"
-    """
-    #export PATH=/export/home/prerequisites_SALOME_780_LAURENT/openmpi-1.8.4/FROM_nothing/bin:$PATH
-    #do not work prefer set before salome launch
-    OPENMPI_INSTALL_DIR = "/export/home/prerequisites_SALOME_780_LAURENT/openmpi-1.8.4/FROM_nothing/bin"
-    sys.path.insert(0, OPENMPI_INSTALL_DIR)
-    #INSTALL_DIR = /export/home/prerequisites_SALOME_780_LAURENT/openmpi-1.8.4/FROM_nothing
-    for i in sys.path[0:10]: print "PATH",i
-    """
-
-
 
 
 ##########################################################################
 def launchMultithread(args):
-  if verbose: print(("INFO: launchMultithread for %s" % NAME_OS))
+  log_info("launch multithread for %s" % NAME_OS)
 
   if NAME_OS == 'Linux':
     # --out is ONE file: basename_tetra_hpc.mesh
-    outputMulti = os.path.splitext(args.inputFile)[0] + "_tetra_hpc.mesh"  # only one file if Multithread
-    outputs = os.path.splitext(args.outputFiles)[0]
-    outputMultiAsMpi = os.path.splitext(args.outputFiles)[0] + ".000001.mesh"  # create one output file named as only one from mpi
-    cmd = "mg-tetra_hpc.exe --max_number_of_threads %i --in %s --gradation %s --max_size %s --min_size %s; cp %s %s; ls -alt %s*; " % \
-          (args.number, args.inputFile, args.gradation, args.max_size, args.min_size, outputMulti, outputMultiAsMpi, outputs)
+    args.outputs = os.path.splitext(args.outputFiles)[0] # use args.__dict__
+    args.currentdir = os.path.splitext(args.outputFiles)[0] # use args.__dict__
+
+    # args.outputMulti = os.path.splitext(args.inputFile)[0] + "_tetra_hpc.mesh"  # only one file if mg-tetra_hpc.exe multithread
+    args.outputMultiAsMpi = os.path.splitext(args.outputFiles)[0] + ".000001.mesh"  # create one output file named as mpi (as only one process)
+
+    cmd = """
+set -x
+
+# mpirun mg-tetra_hpc_mpi.exe
+  rm %(outputs)s*   # clean previous case
+  mg-tetra_hpc.exe \
+--max_number_of_threads %(number)s \
+--in %(inputFile)s \
+--out %(outputFiles)s \
+--gradation %(gradation)s \
+--max_size %(max_size)s \
+--min_size %(min_size)s
+  # create force symbolic link as no duplicate multithread output file as MPI output file (as only one process)
+  ln -sf %(outputFiles)s %(outputMultiAsMpi)s
+""" % args.__dict__ 
+    # args.number, args.inputFile, args.gradation, args.max_size, args.min_size, outputMulti, outputMultiAsMpi, outputs
   else:
-    return KO + " ERROR: unknown operating system: %s" % NAME_OS
+    msg = "unknown operating system: %s" % NAME_OS
+    log_error(msg)
+    return KO + msg
 
   result = exec_command(cmd, verbose=True)
   return result
@@ -188,48 +250,77 @@ def launchMultithread(args):
 
 ##########################################################################
 def launchMpi(args):
-  if verbose: print(("INFO: launchMpi for %s" % NAME_OS))
+  log_info("launch MPI for %s" % NAME_OS)
 
   if NAME_OS == 'Linux':
-    cmd = ""
-
-    """ compile libmeshgems_mpi.so: no needs
-    COMPILDIR=os.getenv("MESHGEMSHOME") + "/stubs"
-    TARGETDIR=os.getenv("MESHGEMSHOME") + "/lib/Linux_64"
-    cmd  = "which mg-tetra_hpc_mpi.exe; which mpicc; rm /tmp/GHS3DPRL_out*; "
-    cmd += "cd %s; mpicc meshgems_mpi.c -DMESHGEMS_LINUX_BUILD -I../include -shared -fPIC -o %s/libmeshgems_mpi.so; " % (COMPILDIR, TARGETDIR)
-    """
-
-    outputs = os.path.splitext(args.outputFiles)[0]
-    cmd += "mpirun -n %i mg-tetra_hpc_mpi.exe --in %s --out %s --gradation %s --max_size %s --min_size %s; ls -alt %s*; " % \
-          (args.number, args.inputFile, args.outputFiles, args.gradation, args.max_size, args.min_size, outputs)
+    args.outputs = os.path.splitext(args.outputFiles)[0] # use args.__dict__
+    args.currentdir = os.path.splitext(args.outputFiles)[0] # use args.__dict__
+    cmd = """
+set -x
+
+  which mg-tetra_hpc_mpi.exe
+  if [ $? -ne 0 ]  # exit if not found
+  then
+    exit 1
+  fi
+
+  # needed as openmpi environment have to be set, may be as user or root (for cluster) choice
+  which mpirun || module load mpi/openmpi-x86_64 # example for centos7/redhat7
+
+# compile libmeshgems_mpi.so if needed (i.e. if empty stubs)
+  mg-tetra_hpc_mpi.exe --help &> /tmp/help_mg-tetra_hpc_mpi_${USER}.txt
+  if [ $? -ne 0 ] # if empty MPI stubs library, have to compile libmeshgems_mpi.so
+  then
+    # SALOME env var MESHGEMSHOME is something like .../INSTALL/MeshGems
+    MESHGEMSHOME=${MESHGEMSHOME:-/tmp/MeshGems} # default value if unset
+    COMPILDIR=$MESHGEMSHOME/stubs
+    TARGETDIR=$MESHGEMSHOME/lib/Linux_64
+    cd ${COMPILDIR}
+    mpicc meshgems_mpi.c -DMESHGEMS_LINUX_BUILD -I../include -shared -fPIC -o ${TARGETDIR}/libmeshgems_mpi.so
+  fi
+
+# mpirun mg-tetra_hpc_mpi.exe
+  rm %(outputs)s*   # clean previous case
+  cd %(outputDir)s
+  mpirun --n %(number)s mg-tetra_hpc_mpi.exe \
+--in %(inputFile)s \
+--out %(outputFiles)s \
+--gradation %(gradation)s \
+--max_size %(max_size)s \
+--min_size %(min_size)s
+  # ls -alt %(outputs)s*
+""" % args.__dict__ 
+    # args.number, args.inputFile, args.outputFiles, args.gradation, args.max_size, args.min_size, outputs
   else:
-    return KO + " ERROR: unknown operating system: %s" % NAME_OS
+    msg = "unknown operating system: %s" % NAME_OS
+    log_error(msg)
+    return KO + msg
 
   result = exec_command(cmd, verbose=True)
   return result
 
 
 ##########################################################################
-# main
-##########################################################################
-
-if __name__ == '__main__':
-  parser = AP.ArgumentParser(description='launch tetra_hpc.exe or tetra_hpc_mpi.exe mesh computation', argument_default=None)
-  # ./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
+def getParser():
+  parser = AP.ArgumentParser(description='launch tetra_hpc.exe or tetra_hpc_mpi.exe for mesh computation', argument_default=None)
 
   parser.add_argument(
     '-v', '--verbose',
     help='set verbose, for debug',
     action='store_true',
   )
+  parser.add_argument(
+    '-t', '--test',
+    help='make test, for script debug',
+    action='store_true',
+  )
   parser.add_argument(
     '-n', '--number',
     help='if multithread: number of threads, else distributed: number of processes MPI',
     choices=[ArgRange(1, 999999)],
     type=int,
     metavar='integer >= 0',
-    default=1,
+    default=NB_PROCS # as automatic, no more local number of cpu (multithread or MPI)
   )
   parser.add_argument(
     '-m', '--multithread',
@@ -265,15 +356,18 @@ if __name__ == '__main__':
     '-i', '--inputFile',
     help='input file name',
     # nargs='?',
-    metavar='.../inputFile.mesh'
+    metavar='.../inputFile.mesh',
+    default='/tmp/GHS3DPRL.mesh'
   )
   parser.add_argument(
     '-o', '--outputFiles',
     help='output basename file(s) name',
     # nargs='?',
-    metavar='.../outputFile.mesh'
+    metavar='.../outputFile.mesh',
+    default='/tmp/GHS3DPRL_out.mesh'
   )
   """
+  # example
   parser.add_argument(
     '-x', '--xoneargument',
     nargs='?',
@@ -283,39 +377,72 @@ if __name__ == '__main__':
     default='0'
   )
   """
+  # help(parser)
+  return parser
 
+##########################################################################
+# main
+##########################################################################
 
+if __name__ == '__main__':
   """
-  args is Namespace, may use it as global to store
-  parameters, data, used arrays and results and other...
+  args is 'Namespace' class, may use it as global to store
+  parameters, data, used arrays and results, and other...
   """
+  parser = getParser()
   args = parser.parse_args()
 
   verbose = args.verbose
-  if verbose: print(("INFO: args:\n%s" % PP.pformat(args.__dict__)))
+  log_debug("%s arguments are:\n%s" % (__file__, PP.pformat(args.__dict__)))
 
   if len(sys.argv) == 1:  # no args as --help
     parser.print_help()
     sys.exit(KOSYS)
 
+  if args.test is True:
+    test_log('one line message')
+    test_log('first line\nsecond line')
+    sys.exit(OKSYS)
+
   if args.inputFile == None:
-    print(("\nERROR: Nothing to do: no input files\n\n%s\n" % PP.pformat(args)))
-    parser.print_help()
+    log_error("no --inputFile defined in arguments:\n%s" % PP.pformat(args.__dict__))
+    log_info("arguments should be:\n%s" % parser.format_help())
+    # parser.print_help()
+    sys.exit(KOSYS)
+
+  if not os.path.isfile(args.inputFile):
+    log_error("inexisting input file:\n%s" % os.path.realpath(args.inputFile))
     sys.exit(KOSYS)
 
   if args.outputFiles == None:
     tmp, _ = os.path.splitext(args.inputFile)
     args.outputFiles = tmp + "_out.mesh"
-    print(("\nWARNING: Default ouput files: %s" % args.outputFiles))
-
-  force_DISTENE_LICENSE_FILE()
-
-  print(("INFO: mg-tetra_hpc.py assume licence file set:\n  DLIM8VAR=%s\n  DISTENE_LICENSE_FILE=%s" % \
-       (os.getenv("DLIM8VAR"), os.getenv("DISTENE_LICENSE_FILE"))))
+  
+  tmp, _ = os.path.split(args.outputFiles)
+  args.outputDir = os.path.realpath(tmp)
+
+  log_info("output directory:\n%s" % args.outputDir)
+    
+  force_DISTENE_LICENSE_FILE() # if FORCE_DISTENE_LICENSE_FILE environ variable exists, manually set by user
+
+  DLIM8VAR = os.getenv("DLIM8VAR")
+  DISTENE_LICENSE_FILE = os.getenv("DISTENE_LICENSE_FILE")
+  msg = """\
+%s assume distene licence file set:
+DLIM8VAR=%s
+DISTENE_LICENSE_FILE=%s
+""" % (__file__, DLIM8VAR, DISTENE_LICENSE_FILE)
+  
+  if None in [DLIM8VAR, DISTENE_LICENSE_FILE]:
+    log_error(msg + "Abandon.")
+    sys.exit(KOSYS)
+  else:
+    log_info(msg)
 
   if args.multithread == "yes":
     result = launchMultithread(args)
   else:
     result = launchMpi(args)
+
   sys.exit(okToSys(result, verbose=True))