diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index e189aa291e..9b3125f04b 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -381,19 +381,10 @@ if(PKG_MSCG OR PKG_USER-ATC OR PKG_USER-AWPMD OR PKG_USER-QUIP OR PKG_LATTE) endif() if(PKG_PYTHON) - find_package(PythonInterp REQUIRED) find_package(PythonLibs REQUIRED) add_definitions(-DLMP_PYTHON) include_directories(${PYTHON_INCLUDE_DIR}) list(APPEND LAMMPS_LINK_LIBS ${PYTHON_LIBRARY}) - if(BUILD_LIB AND BUILD_SHARED_LIBS) - if(NOT PYTHON_INSTDIR) - execute_process(COMMAND ${PYTHON_EXECUTABLE} - -c "import distutils.sysconfig as cg; print(cg.get_python_lib(1,0,prefix='${CMAKE_INSTALL_PREFIX}'))" - OUTPUT_VARIABLE PYTHON_INSTDIR OUTPUT_STRIP_TRAILING_WHITESPACE) - endif() - install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/../python/lammps.py DESTINATION ${PYTHON_INSTDIR}) - endif() endif() find_package(JPEG QUIET) @@ -1467,6 +1458,49 @@ install( DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/profile.d ) +############################################################################### +# Install LAMMPS lib and python module into site-packages folder with +# "install-python" target. Behaves exactly like "make install-python" for +# conventional build. Only available, if a shared library is built. +# This is primarily for people that only want to use the Python wrapper. +############################################################################### +if(BUILD_LIB AND BUILD_SHARED_LIBS) + find_package(PythonInterp) + if (PYTHONINTERP_FOUND) + add_custom_target( + install-python + ${PYTHON_EXECUTABLE} install.py -v ${LAMMPS_SOURCE_DIR}/version.h + -m ${CMAKE_CURRENT_SOURCE_DIR}/../python/lammps.py + -l ${CMAKE_BINARY_DIR}/liblammps${CMAKE_SHARED_LIBRARY_SUFFIX} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../python + COMMENT "Installing LAMMPS Python module") + else() + add_custom_target( + install-python + ${CMAKE_COMMAND} -E echo "Must have Python installed to install the LAMMPS Python module") + endif() +else() + add_custom_target( + install-python + ${CMAKE_COMMAND} -E echo "Must build LAMMPS as a shared library to use the Python module") +endif() + +############################################################################### +# Add LAMMPS python module to "install" target. This is taylored for building +# LAMMPS for package managers and with different prefix settings. +# This requires either a shared library or that the PYTHON package is included. +############################################################################### +if((BUILD_LIB AND BUILD_SHARED_LIBS) OR (PKG_PYTHON)) + find_package(PythonInterp) + if (PYTHONINTERP_FOUND) + execute_process(COMMAND ${PYTHON_EXECUTABLE} + -c "import distutils.sysconfig as cg; print(cg.get_python_lib(1,0,prefix='${CMAKE_INSTALL_PREFIX}'))" + OUTPUT_VARIABLE PYTHON_DEFAULT_INSTDIR OUTPUT_STRIP_TRAILING_WHITESPACE) + set(PYTHON_INSTDIR ${PYTHON_DEFAULT_INSTDIR} CACHE PATH "Installation folder for LAMMPS Python module") + install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/../python/lammps.py DESTINATION ${PYTHON_INSTDIR}) + endif() +endif() + ############################################################################### # Testing # diff --git a/doc/src/Howto_pylammps.txt b/doc/src/Howto_pylammps.txt index b731a8e31a..54f17d912a 100644 --- a/doc/src/Howto_pylammps.txt +++ b/doc/src/Howto_pylammps.txt @@ -57,6 +57,17 @@ library is then loaded by the Python interface. In this example we enable the MOLECULE package and compile LAMMPS with C++ exceptions, PNG, JPEG and FFMPEG output support enabled. +Step 1a: For the CMake based build system, the steps are: + +mkdir $LAMMPS_DIR/build-shared +cd $LAMMPS_DIR/build-shared :pre + +# MPI, PNG, Jpeg, FFMPEG are auto-detected +cmake ../cmake -DPKG_MOLECULE=yes -DLAMMPS_EXCEPTIONS=yes -DBUILD_LIB=yes -DBUILD_SHARED_LIBS=yes +make :pre + +Step 1b: For the legacy, make based build system, the steps are: + cd $LAMMPS_DIR/src :pre # add packages if necessary @@ -68,10 +79,9 @@ make mpi mode=shlib LMP_INC="-DLAMMPS_PNG -DLAMMPS_JPEG -DLAMMPS_FFMPEG -DLAMMPS Step 2: Installing the LAMMPS Python package :h6 PyLammps is part of the lammps Python package. To install it simply install -that package into your current Python installation. +that package into your current Python installation with: -cd $LAMMPS_DIR/python -python install.py :pre +make install-python :pre NOTE: Recompiling the shared library requires re-installing the Python package @@ -94,14 +104,21 @@ apt-get install python-virtualenv :pre Creating a virtualenv with lammps installed :h6 -# create virtualenv name 'testing' :pre +# create virtualenv named 'testing' +virtualenv $HOME/python/testing :pre # activate 'testing' environment -source testing/bin/activate :pre +source $HOME/python/testing/bin/activate :pre + +Now configure and compile the LAMMPS shared library as outlined above. +When using CMake and the shared library has already been build, you +need to re-run CMake to update the location of the python executable +to the location in the virtual environment with: + +cmake . -DPYTHON_EXECUTABLE=$(which python) :pre # install LAMMPS package in virtualenv -(testing) cd $LAMMPS_DIR/python -(testing) python install.py :pre +(testing) make install-python :pre # install other useful packages (testing) pip install matplotlib jupyter mpi4py :pre diff --git a/doc/src/Python_install.txt b/doc/src/Python_install.txt index 97f6bf3c3a..2c134974f5 100644 --- a/doc/src/Python_install.txt +++ b/doc/src/Python_install.txt @@ -12,16 +12,23 @@ Installing LAMMPS in Python :h3 For Python to invoke LAMMPS, there are 2 files it needs to know about: python/lammps.py -src/liblammps.so :ul +liblammps.so or liblammps.dylib :ul -Lammps.py is the Python wrapper on the LAMMPS library interface. -Liblammps.so is the shared LAMMPS library that Python loads, as -described above. +The python source code in lammps.py is the Python wrapper on the +LAMMPS library interface. The liblammps.so or liblammps.dylib file +is the shared LAMMPS library that Python loads dynamically. -You can insure Python can find these files in one of two ways: +You can achieve that Python can find these files in one of two ways: -set two environment variables -run the python/install.py script :ul +set two environment variables pointing to the location in the source tree +run "make install-python" or run the python/install.py script explicitly :ul + +When calling "make install-python" LAMMPS will try to install the +python module and the shared library into the python site-packages folders; +either the system-wide ones, or the local users ones (in case of insufficient +permissions for the global install). Python will then find the module +and shared library file automatically. The exact location of these folders +depends on your python version and your operating system. If you set the paths to these files as environment variables, you only have to do it once. For the csh or tcsh shells, add something like @@ -30,42 +37,28 @@ this to your ~/.cshrc file, one line for each of the two files: setenv PYTHONPATH $\{PYTHONPATH\}:/home/sjplimp/lammps/python setenv LD_LIBRARY_PATH $\{LD_LIBRARY_PATH\}:/home/sjplimp/lammps/src :pre -If you use the python/install.py script, you need to invoke it every -time you rebuild LAMMPS (as a shared library) or make changes to the -python/lammps.py file. +On MacOSX you may also need to set DYLD_LIBRARY_PATH accordingly. +For Bourne/Korn shells accordingly into the corresponding files using +the "export" shell builtin. -You can invoke install.py from the python directory as +If you use "make install-python" or the python/install.py script, you need +to invoke it every time you rebuild LAMMPS (as a shared library) or +make changes to the python/lammps.py file, so that the site-packages +files are updated with the new version. -% python install.py \[libdir\] \[pydir\] :pre +If the default settings of "make install-python" are not what you want, +you can invoke install.py from the python directory manually as -The optional libdir is where to copy the LAMMPS shared library to; the -default is /usr/local/lib. The optional pydir is where to copy the -lammps.py file to; the default is the site-packages directory of the -version of Python that is running the install script. +% python install.py -m \ -l -v \[-d \\] :pre -Note that libdir must be a location that is in your default -LD_LIBRARY_PATH, like /usr/local/lib or /usr/lib. And pydir must be a -location that Python looks in by default for imported modules, like -its site-packages dir. If you want to copy these files to -non-standard locations, such as within your own user space, you will -need to set your PYTHONPATH and LD_LIBRARY_PATH environment variables -accordingly, as above. +The -m flag points to the lammps.py python module file to be installed, +the -l flag points to the LAMMPS shared library file to be installed, +the -v flag points to the version.h file in the LAMMPS source +and the optional -d flag to a custom (legacy) installation folder :ul -If the install.py script does not allow you to copy files into system -directories, prefix the python command with "sudo". If you do this, -make sure that the Python that root runs is the same as the Python you -run. E.g. you may need to do something like - -% sudo /usr/local/bin/python install.py \[libdir\] \[pydir\] :pre - -You can also invoke install.py from the make command in the src -directory as - -% make install-python :pre - -In this mode you cannot append optional arguments. Again, you may -need to prefix this with "sudo". In this mode you cannot control -which Python is invoked by root. +If you use a legacy installation folder, you will need to set your +PYTHONPATH and LD_LIBRARY_PATH (and/or DYLD_LIBRARY_PATH) environment +variables accordingly, as described above. Note that if you want Python to be able to load different versions of the LAMMPS shared library (see "this section"_Python_shlib.html), you will diff --git a/doc/src/Python_overview.txt b/doc/src/Python_overview.txt index a5d6a469ff..0195ec2b20 100644 --- a/doc/src/Python_overview.txt +++ b/doc/src/Python_overview.txt @@ -13,11 +13,11 @@ Overview of Python and LAMMPS :h3 LAMMPS can work together with Python in three ways. First, Python can wrap LAMMPS through the its "library interface"_Howto_library.html, so that a Python script can create one or more instances of LAMMPS and -launch one or more simulations. In Python lingo, this is "extending" -Python with LAMMPS. +launch one or more simulations. In Python lingo, this is called +"extending" Python with a LAMMPS module. Second, a lower-level Python interface can be used indirectly through -provided PyLammps and IPyLammps wrapper classes, written in Python. +the provided PyLammps and IPyLammps wrapper classes, written in Python. These wrappers try to simplify the usage of LAMMPS in Python by providing an object-based interface to common LAMMPS functionality. They also reduces the amount of code necessary to parameterize LAMMPS @@ -25,11 +25,12 @@ scripts through Python and make variables and computes directly accessible. Third, LAMMPS can use the Python interpreter, so that a LAMMPS -input script can invoke Python code directly, and pass information -back-and-forth between the input script and Python functions you -write. This Python code can also callback to LAMMPS to query or change -its attributes. In Python lingo, this is "embedding" Python in -LAMMPS. When used in this mode, Python can perform operations that -the simple LAMMPS input script syntax cannot. +input script or styles can invoke Python code directly, and pass +information back-and-forth between the input script and Python +functions you write. This Python code can also callback to LAMMPS +to query or change its attributes through the LAMMPS Python module +mentioned above. In Python lingo, this is "embedding" Python in +LAMMPS. When used in this mode, Python can perform script operations +that the simple LAMMPS input script syntax can not. diff --git a/doc/utils/sphinx-config/false_positives.txt b/doc/utils/sphinx-config/false_positives.txt index 6eb158396f..14f5b89072 100644 --- a/doc/utils/sphinx-config/false_positives.txt +++ b/doc/utils/sphinx-config/false_positives.txt @@ -250,6 +250,7 @@ Boresch Botero Botu Bouguet +Bourne boxcolor bp bpls @@ -627,6 +628,7 @@ dVx dW dx dy +dylib dyn dyne dynes @@ -1298,6 +1300,7 @@ Kondor konglt Koning Kooser +Korn Koskinen Koster Kosztin diff --git a/python/README b/python/README index 6b13959f4d..204ca2c28d 100644 --- a/python/README +++ b/python/README @@ -9,12 +9,12 @@ doc/Section_python.html and in doc/Section_start.html#start_5. Basically you need to follow these steps in the src directory: % make g++ mode=shlib # build for whatever machine target you wish -% make install-python # may need to do this via sudo +% make install-python # install into site-packages folder You can replace the last step by a one-time setting of environment variables in your shell script. Or you can run the python/install.py script directly to give you more control over where the two relevant -files are installed. See doc/Section_python.html for details. +files are installed. See doc/Python_install.html for details. You should then be able to launch Python and instantiate an instance of LAMMPS: diff --git a/python/install.py b/python/install.py index 9308506fd2..7f7062103a 100644 --- a/python/install.py +++ b/python/install.py @@ -1,56 +1,96 @@ #!/usr/bin/env python -# copy LAMMPS src/liblammps.so and lammps.py to system dirs - -from __future__ import print_function - -instructions = """ -Syntax: python install.py [-h] [pydir] - pydir = target dir for lammps.py and liblammps.so - default = Python site-packages dir +""" +Installer script to install the LAMMPS python module and the corresponding +shared library into either the system-wide site-packages tree, or - failing +that - into the corresponding user tree. Called from the 'install-python' +build target in the conventional and CMake based build systems """ +# copy LAMMPS shared library and lammps.py to system dirs + +from __future__ import print_function import sys,os,shutil +from argparse import ArgumentParser -if (len(sys.argv) > 1 and sys.argv[1] == "-h") or len(sys.argv) > 2: - print(instructions) - sys.exit() +parser = ArgumentParser(prog='install.py', + description='LAMMPS python module installer script') -if len(sys.argv) == 2: pydir = sys.argv[1] -else: pydir = "" +parser.add_argument("-m", "--module", required=True, + help="path to the source of the LAMMPS Python module") +parser.add_argument("-l", "--lib", required=True, + help="path to the compiled LAMMPS shared library") +parser.add_argument("-v", "--version", required=True, + help="path to the LAMMPS version.h header file") -# copy lammps.py to pydir if it exists -# if pydir not specified, install in site-packages via distutils setup() +parser.add_argument("-d","--dir", + help="Legacy custom installation folder selection for module and library") -if pydir: - if not os.path.isdir(pydir): - print( "ERROR: pydir %s does not exist" % pydir) - sys.exit() - str = "cp ../python/lammps.py %s" % pydir - print(str) +args = parser.parse_args() + +# validate arguments and make paths absolute + +if args.module: + if not os.path.exists(args.module): + print( "ERROR: LAMMPS module file %s does not exist" % args.module) + parser.print_help() + sys.exit(1) + else: + args.module = os.path.abspath(args.module) + +if args.lib: + if not os.path.exists(args.lib): + print( "ERROR: LAMMPS shared library %s does not exist" % args.lib) + parser.print_help() + sys.exit(1) + else: + args.lib = os.path.abspath(args.lib) + +if args.version: + if not os.path.exists(args.version): + print( "ERROR: LAMMPS version header file %s does not exist" % args.version) + parser.print_help() + sys.exit(1) + else: + args.version = os.path.abspath(args.version) + +if args.dir: + if not os.path.isdir(args.dir): + print( "ERROR: Installation folder %s does not exist" % args.dir) + parser.print_help() + sys.exit(1) + else: + args.dir = os.path.abspath(args.dir) + +# if a custom directory is given, we copy the files directly +# without any special processing or additional steps to that folder + +if args.dir: + print("Copying LAMMPS Python module to custom folder %s" % args.dir) try: - shutil.copyfile("../python/lammps.py", os.path.join(pydir,'lammps.py') ) + shutil.copyfile(args.module, os.path.join(args.dir,'lammps.py')) except shutil.Error: - pass # source and destination are identical + pass # fail silently - str = "cp ../src/liblammps.so %s" % pydir - print(str) + print("Copying LAMMPS shared library to custom folder %s" % args.dir) try: - shutil.copyfile("../src/liblammps.so", os.path.join(pydir,"liblammps.so") ) + shutil.copyfile(args.lib, os.path.join(args.dir,os.path.basename(args.lib))) except shutil.Error: - pass # source and destination are identical - sys.exit() - -print("installing lammps.py in Python site-packages dir") + pass # fail silently -os.chdir('../python') # in case invoked via make in src dir + sys.exit() # extract version string from header -fp = open('../src/version.h','r') +fp = open(args.version,'r') txt=fp.read().split('"')[1].split() verstr=txt[0]+txt[1]+txt[2] fp.close() +print("Installing LAMMPS Python module version %s into site-packages folder" % verstr) + +# we need to switch to the folder of the python module +os.chdir(os.path.dirname(args.module)) + from distutils.core import setup from distutils.sysconfig import get_python_lib import site @@ -62,28 +102,26 @@ try: version = verstr, author = "Steve Plimpton", author_email = "sjplimp@sandia.gov", - url = "http://lammps.sandia.gov", - description = "LAMMPS molecular dynamics library", + url = "https://lammps.sandia.gov", + description = "LAMMPS Molecular Dynamics Python module", + license = "GPL", py_modules = ["lammps"], - data_files = [(get_python_lib(), ["../src/liblammps.so"])]) + data_files = [(get_python_lib(), [args.lib])]) except: tryuser=True - print ("Installation into global site-packages dir failed.\nTrying user site dir %s now." % site.USER_SITE) - + print ("Installation into global site-packages folder failed.\nTrying user folder %s now." % site.USER_SITE) if tryuser: try: sys.argv = ["setup.py","install","--user"] # as if had run "python setup.py install --user" setup(name = "lammps", - version = verstr, - author = "Steve Plimpton", - author_email = "sjplimp@sandia.gov", - url = "http://lammps.sandia.gov", - description = "LAMMPS molecular dynamics library", - py_modules = ["lammps"], - data_files = [(site.USER_SITE, ["../src/liblammps.so"])]) - except: - print("Installation into user site package dir failed.\nGo to ../python and install manually.") - - - + version = verstr, + author = "Steve Plimpton", + author_email = "sjplimp@sandia.gov", + url = "https://lammps.sandia.gov", + description = "LAMMPS Molecular Dynamics Python module", + license = "GPL", + py_modules = ["lammps"], + data_files = [(site.USER_SITE, [args.lib])]) + except: + print("Installation into user site package folder failed.") diff --git a/src/Makefile b/src/Makefile index f1030ae08f..3be4e3e78f 100644 --- a/src/Makefile +++ b/src/Makefile @@ -275,7 +275,8 @@ mpi-stubs: sinclude ../lib/python/Makefile.lammps install-python: - @$(PYTHON) ../python/install.py + @$(PYTHON) ../python/install.py -v ../src/version.h \ + -m ../python/lammps.py -l ../src/liblammps.so # Create a tarball of src dir and packages