Add Python 3 compatibility to PYTHON package

This commit is contained in:
Richard Berger
2017-04-11 20:24:42 -04:00
parent 4da8c1c4e2
commit 9a027a01da
6 changed files with 58 additions and 23 deletions

View File

@ -1,9 +1,10 @@
# Python function that implements a loop of short runs # Python function that implements a loop of short runs
# calls back to LAMMPS via "lmp" instance # calls back to LAMMPS via "lmp" instance
# lammps() must be called with ptr=lmpptr for this to work # lammps() must be called with ptr=lmpptr for this to work
from __future__ import print_function
def loop(N,cut0,thresh,lmpptr): def loop(N,cut0,thresh,lmpptr):
print "LOOP ARGS",N,cut0,thresh,lmpptr print("LOOP ARGS",N,cut0,thresh,lmpptr)
from lammps import lammps from lammps import lammps
lmp = lammps(ptr=lmpptr) lmp = lammps(ptr=lmpptr)
natoms = lmp.get_natoms() natoms = lmp.get_natoms()
@ -12,11 +13,12 @@ def loop(N,cut0,thresh,lmpptr):
cut = cut0 + i*0.1 cut = cut0 + i*0.1
lmp.set_variable("cut",cut) # set a variable in LAMMPS lmp.set_variable("cut",cut) # set a variable in LAMMPS
lmp.command("pair_style lj/cut ${cut}") # LAMMPS command lmp.command("pair_style lj/cut ${cut}") # LAMMPS command
#lmp.command("pair_style lj/cut %d" % cut) # LAMMPS command option #lmp.command("pair_style lj/cut %d" % cut) # LAMMPS command option
lmp.command("pair_coeff * * 1.0 1.0") # ditto lmp.command("pair_coeff * * 1.0 1.0") # ditto
lmp.command("run 10") # ditto lmp.command("run 10") # ditto
pe = lmp.extract_compute("thermo_pe",0,0) # extract total PE from LAMMPS pe = lmp.extract_compute("thermo_pe",0,0) # extract total PE from LAMMPS
print "PE",pe/natoms,thresh print("PE",pe/natoms,thresh)
if pe/natoms < thresh: return if pe/natoms < thresh: return

View File

@ -25,13 +25,14 @@ run 10
# example of catching a syntax error # example of catching a syntax error
python simple here """ python simple here """
from __future__ import print_function
def simple(): def simple():
import exceptions print("Inside simple function")
print "Inside simple function"
try: try:
foo += 1 foo += 1
except Exception, e: except Exception as e:
print "FOO error:",e print("FOO error:", e)
""" """
python simple invoke python simple invoke

View File

@ -0,0 +1,6 @@
# Settings that the LAMMPS build will import when this package library is used
# See the README file for more explanation
python_SYSINC = $(shell which python3-config > /dev/null 2>&1 && python3-config --includes || python-config --includes )
python_SYSLIB = $(shell which python3-config > /dev/null 2>&1 && python3-config --ldflags || python-config --ldflags)
python_SYSPATH =

View File

@ -6,10 +6,6 @@ and your version of Python, and copy it to Makefile.lammps before
building LAMMPS itself. You may need to edit one of the provided building LAMMPS itself. You may need to edit one of the provided
files to match your system. files to match your system.
Note that is not currently possible to use the PYTHON package with
Python 3, only with Python 2. The C API changed from Python 2 to 3
and the LAMMPS code is not compatible with both.
If you create a new Makefile.lammps file suitable for some version of If you create a new Makefile.lammps file suitable for some version of
Python on some system, that is not a match to one of the provided Python on some system, that is not a match to one of the provided
Makefile.lammps.* files, you can send it to the developers, and we can Makefile.lammps.* files, you can send it to the developers, and we can

View File

@ -30,7 +30,7 @@ from collections import namedtuple
import os import os
import select import select
import re import re
import sys
class MPIAbortException(Exception): class MPIAbortException(Exception):
def __init__(self, message): def __init__(self, message):
@ -151,9 +151,16 @@ class lammps(object):
else: else:
# magic to convert ptr to ctypes ptr # magic to convert ptr to ctypes ptr
pythonapi.PyCObject_AsVoidPtr.restype = c_void_p if sys.version_info >= (3, 0):
pythonapi.PyCObject_AsVoidPtr.argtypes = [py_object] # Python 3 (uses PyCapsule API)
self.lmp = c_void_p(pythonapi.PyCObject_AsVoidPtr(ptr)) pythonapi.PyCapsule_GetPointer.restype = c_void_p
pythonapi.PyCapsule_GetPointer.argtypes = [py_object, c_char_p]
self.lmp = c_void_p(pythonapi.PyCapsule_GetPointer(ptr, None))
else:
# Python 2 (uses PyCObject API)
pythonapi.PyCObject_AsVoidPtr.restype = c_void_p
pythonapi.PyCObject_AsVoidPtr.argtypes = [py_object]
self.lmp = c_void_p(pythonapi.PyCObject_AsVoidPtr(ptr))
def __del__(self): def __del__(self):
if self.lmp and self.opened: if self.lmp and self.opened:
@ -305,7 +312,7 @@ class lammps(object):
def set_variable(self,name,value): def set_variable(self,name,value):
if name: name = name.encode() if name: name = name.encode()
if value: value = str(value).encode() if value: value = str(value).encode()
return self.lib.lammps_set_variable(self.lmp,name,str(value)) return self.lib.lammps_set_variable(self.lmp,name,value)
# return current value of thermo keyword # return current value of thermo keyword

View File

@ -25,6 +25,22 @@ enum{NONE,INT,DOUBLE,STRING,PTR};
#define VALUELENGTH 64 // also in variable.cpp #define VALUELENGTH 64 // also in variable.cpp
// Wrap API changes between Python 2 and 3 using macros
#if PY_MAJOR_VERSION == 2
#define PY_INT_FROM_LONG(X) PyInt_FromLong(X)
#define PY_INT_AS_LONG(X) PyInt_AsLong(X)
#define PY_STRING_FROM_STRING(X) PyString_FromString(X)
#define PY_VOID_POINTER(X) PyCObject_FromVoidPtr((void *) X, NULL)
#define PY_STRING_AS_STRING(X) PyString_AsString(X)
#elif PY_MAJOR_VERSION == 3
#define PY_INT_FROM_LONG(X) PyLong_FromLong(X)
#define PY_INT_AS_LONG(X) PyLong_AsLong(X)
#define PY_STRING_FROM_STRING(X) PyUnicode_FromString(X)
#define PY_VOID_POINTER(X) PyCapsule_New((void *) X, NULL, NULL)
#define PY_STRING_AS_STRING(X) PyUnicode_AsUTF8(X)
#endif
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
Python::Python(LAMMPS *lmp) : Pointers(lmp) Python::Python(LAMMPS *lmp) : Pointers(lmp)
@ -257,8 +273,10 @@ void Python::invoke_function(int ifunc, char *result)
error->all(FLERR,"Could not evaluate Python function input variable"); error->all(FLERR,"Could not evaluate Python function input variable");
} }
pValue = PyInt_FromLong(atoi(str)); pValue = PY_INT_FROM_LONG(atoi(str));
} else pValue = PyInt_FromLong(pfuncs[ifunc].ivalue[i]); } else {
pValue = PY_INT_FROM_LONG(pfuncs[ifunc].ivalue[i]);
}
} else if (itype == DOUBLE) { } else if (itype == DOUBLE) {
if (pfuncs[ifunc].ivarflag[i]) { if (pfuncs[ifunc].ivarflag[i]) {
str = input->variable->retrieve(pfuncs[ifunc].svalue[i]); str = input->variable->retrieve(pfuncs[ifunc].svalue[i]);
@ -269,7 +287,9 @@ void Python::invoke_function(int ifunc, char *result)
} }
pValue = PyFloat_FromDouble(atof(str)); pValue = PyFloat_FromDouble(atof(str));
} else pValue = PyFloat_FromDouble(pfuncs[ifunc].dvalue[i]); } else {
pValue = PyFloat_FromDouble(pfuncs[ifunc].dvalue[i]);
}
} else if (itype == STRING) { } else if (itype == STRING) {
if (pfuncs[ifunc].ivarflag[i]) { if (pfuncs[ifunc].ivarflag[i]) {
str = input->variable->retrieve(pfuncs[ifunc].svalue[i]); str = input->variable->retrieve(pfuncs[ifunc].svalue[i]);
@ -277,10 +297,13 @@ void Python::invoke_function(int ifunc, char *result)
PyGILState_Release(gstate); PyGILState_Release(gstate);
error->all(FLERR,"Could not evaluate Python function input variable"); error->all(FLERR,"Could not evaluate Python function input variable");
} }
pValue = PyString_FromString(str);
} else pValue = PyString_FromString(pfuncs[ifunc].svalue[i]); pValue = PY_STRING_FROM_STRING(str);
} else {
pValue = PY_STRING_FROM_STRING(pfuncs[ifunc].svalue[i]);
}
} else if (itype == PTR) { } else if (itype == PTR) {
pValue = PyCObject_FromVoidPtr((void *) lmp,NULL); pValue = PY_VOID_POINTER(lmp);
} }
PyTuple_SetItem(pArgs,i,pValue); PyTuple_SetItem(pArgs,i,pValue);
} }
@ -304,11 +327,11 @@ void Python::invoke_function(int ifunc, char *result)
if (pfuncs[ifunc].noutput) { if (pfuncs[ifunc].noutput) {
int otype = pfuncs[ifunc].otype; int otype = pfuncs[ifunc].otype;
if (otype == INT) { if (otype == INT) {
sprintf(result,"%ld",PyInt_AsLong(pValue)); sprintf(result,"%ld",PY_INT_AS_LONG(pValue));
} else if (otype == DOUBLE) { } else if (otype == DOUBLE) {
sprintf(result,"%.15g",PyFloat_AsDouble(pValue)); sprintf(result,"%.15g",PyFloat_AsDouble(pValue));
} else if (otype == STRING) { } else if (otype == STRING) {
char *pystr = PyString_AsString(pValue); char *pystr = PY_STRING_AS_STRING(pValue);
if (pfuncs[ifunc].longstr) if (pfuncs[ifunc].longstr)
strncpy(pfuncs[ifunc].longstr,pystr,pfuncs[ifunc].length_longstr); strncpy(pfuncs[ifunc].longstr,pystr,pfuncs[ifunc].length_longstr);
else strncpy(result,pystr,VALUELENGTH-1); else strncpy(result,pystr,VALUELENGTH-1);