diff --git a/python/README b/python/README index 9cca886b86..f0e95d3c45 100644 --- a/python/README +++ b/python/README @@ -39,7 +39,8 @@ scripts in the examples sub-directory: trivial.py read/run a LAMMPS input script thru Python demo.py invoke various LAMMPS library interface routines -simple.py mimic operation of examples/COUPLE/simple/simple.cpp +simple.py parallel example, mimicing examples/COUPLE/simple/simple.cpp +split.py parallel example mc.py Monte Carlo energy relaxation wrapper on LAMMPS gui.py GUI go/stop/temperature-slider to control LAMMPS plot.py real-time temeperature plot with GnuPlot via Pizza.py @@ -77,30 +78,53 @@ The latter link is to the open-source version. ------------------------------------------------------------------- +Each example script has more documentation in the file that explains +how to use it and what it is doing. + You can run a particular script in either of the following ways: % trivial.py in.trivial % python -i trivial.py in.trivial The former assumes that you have changed the first line of the script -to point to the Python installed on your box. +to point to the Python installed on your box and made the script +exectable (e.g. chmod +x trivial.py). -Run the Python scripts with the following LAMMPS input scripts and -arguments: +The example scripts take the following arguments. The in.* args are +LAMPS input scripts. trivial.py in.trivial demo.py -simple.py in.simple +simple.py in.simple # can run in parallel (see below) +split.py in.simple # can run in parallel (see below) + gui.py in.gui 100 plot.py in.plot 10 1000 thermo_temp viz_tool.py in.viz 100 5000 vizplotgui_tool.py in.viz 100 thermo_temp -You can un-comment the Pypar calls if you want to run these in -parallel. Then, for example, you can type: +To run LAMMPS in parallel from Python, so something like this: + +% mpirun -np 4 simple.py in.simple +% mpirun -np 4 python split.py in.simple + +If you run simple.py as-is, this will invoke P instances of a +one-processor run, where both Python and LAMMPS will run on single +processors. Each running job will read the same input file, and write +to same log.lammps file, which isn't too useful. + +However, if you have either the Pypar or mpi4py packages installed in +your Python, and uncomment the Pypar or mpi4py code in simple.py, then +the above commands will invoke 1 instance of a P-processor run. Both +Python and LAMMPS will run on P processors. The job will read the +input file and write a single log.lammps file. + +The split.py script can also be run in parallel. It uses mpi4py +version 2.0.0 (or later), which makes it possible to pass a +communicator when creating the LAMMPS object and thus run multiple +instances of LAMMPS at the same time, each on a different subset of +MPI ranks. Or run LAMMPS on one subset and some other program on the +rest of the MPI ranks, concurrently. See comments in the split.py +script for more details. -% mpirun -np 4 trivial.py in.trivial -% mpirun -np 4 python trivial.py in.trivial -Each script has more documentation in the file that explains how to -use it and what it is doing. diff --git a/python/lammps.py b/python/lammps.py index 01c20bc3b5..1db2747247 100644 --- a/python/lammps.py +++ b/python/lammps.py @@ -19,9 +19,24 @@ from os.path import dirname,abspath,join from inspect import getsourcefile class lammps: - def __init__(self,name="",cmdargs=None,ptr=None): + + # detect if Python is using version of mpi4py that can pass a communicator + + has_mpi4py_v2 = False + try: + from mpi4py import MPI + from mpi4py import __version__ as mpi4py_version + if mpi4py_version.split('.')[0] == '2': + has_mpi4py_v2 = True + except: + pass + + # create instance of LAMMPS + + def __init__(self,name="",cmdargs=None,ptr=None,comm=None): # determine module location + modpath = dirname(abspath(getsourcefile(lambda:0))) # load liblammps.so by default @@ -37,6 +52,7 @@ class lammps: # if no ptr provided, create an instance of LAMMPS # don't know how to pass an MPI communicator from PyPar + # but we can pass an MPI communicator from mpi4py v2.0.0 and later # no_mpi call lets LAMMPS use MPI_COMM_WORLD # cargs = array of C strings from args # if ptr, then are embedding Python in LAMMPS input script @@ -44,18 +60,49 @@ class lammps: # just convert it to ctypes ptr and store in self.lmp if not ptr: - self.opened = 1 - if cmdargs: - cmdargs.insert(0,"lammps.py") - narg = len(cmdargs) - cargs = (c_char_p*narg)(*cmdargs) + # with mpi4py v2, can pass MPI communicator to LAMMPS + # need to adjust for type of MPI communicator object + # allow for int (like MPICH) or void* (like OpenMPI) + + if lammps.has_mpi4py_v2 and comm != None: + if lammps.MPI._sizeof(lammps.MPI.Comm) == sizeof(c_int): + MPI_Comm = c_int + else: + MPI_Comm = c_void_p + + narg = 0 + cargs = 0 + if cmdargs: + cmdargs.insert(0,"lammps.py") + narg = len(cmdargs) + cargs = (c_char_p*narg)(*cmdargs) + self.lib.lammps_open.argtypes = [c_int, c_char_p*narg, \ + MPI_Comm, c_void_p()] + else: + self.lib.lammps_open.argtypes = [c_int, c_int, \ + MPI_Comm, c_void_p()] + + self.lib.lammps_open.restype = None + self.opened = 1 self.lmp = c_void_p() - self.lib.lammps_open_no_mpi(narg,cargs,byref(self.lmp)) + comm_ptr = lammps.MPI._addressof(comm) + comm_val = MPI_Comm.from_address(comm_ptr) + self.lib.lammps_open(narg,cargs,comm_val,byref(self.lmp)) + else: - self.lmp = c_void_p() - self.lib.lammps_open_no_mpi(0,None,byref(self.lmp)) - # could use just this if LAMMPS lib interface supported it - # self.lmp = self.lib.lammps_open_no_mpi(0,None) + self.opened = 1 + if cmdargs: + cmdargs.insert(0,"lammps.py") + narg = len(cmdargs) + cargs = (c_char_p*narg)(*cmdargs) + self.lmp = c_void_p() + self.lib.lammps_open_no_mpi(narg,cargs,byref(self.lmp)) + else: + self.lmp = c_void_p() + self.lib.lammps_open_no_mpi(0,None,byref(self.lmp)) + # could use just this if LAMMPS lib interface supported it + # self.lmp = self.lib.lammps_open_no_mpi(0,None) + else: self.opened = 0 # magic to convert ptr to ctypes ptr