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
# calls back to LAMMPS via "lmp" instance
# lammps() must be called with ptr=lmpptr for this to work
from __future__ import print_function
def loop(N,cut0,thresh,lmpptr):
print "LOOP ARGS",N,cut0,thresh,lmpptr
print("LOOP ARGS",N,cut0,thresh,lmpptr)
from lammps import lammps
lmp = lammps(ptr=lmpptr)
natoms = lmp.get_natoms()
@ -12,11 +13,12 @@ def loop(N,cut0,thresh,lmpptr):
cut = cut0 + i*0.1
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 %d" % cut) # LAMMPS command option
lmp.command("pair_coeff * * 1.0 1.0") # ditto
lmp.command("run 10") # ditto
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

View File

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

View File

@ -30,7 +30,7 @@ from collections import namedtuple
import os
import select
import re
import sys
class MPIAbortException(Exception):
def __init__(self, message):
@ -151,9 +151,16 @@ class lammps(object):
else:
# magic to convert ptr to ctypes ptr
pythonapi.PyCObject_AsVoidPtr.restype = c_void_p
pythonapi.PyCObject_AsVoidPtr.argtypes = [py_object]
self.lmp = c_void_p(pythonapi.PyCObject_AsVoidPtr(ptr))
if sys.version_info >= (3, 0):
# Python 3 (uses PyCapsule API)
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):
if self.lmp and self.opened:
@ -305,7 +312,7 @@ class lammps(object):
def set_variable(self,name,value):
if name: name = name.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

View File

@ -25,6 +25,22 @@ enum{NONE,INT,DOUBLE,STRING,PTR};
#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)
@ -257,8 +273,10 @@ void Python::invoke_function(int ifunc, char *result)
error->all(FLERR,"Could not evaluate Python function input variable");
}
pValue = PyInt_FromLong(atoi(str));
} else pValue = PyInt_FromLong(pfuncs[ifunc].ivalue[i]);
pValue = PY_INT_FROM_LONG(atoi(str));
} else {
pValue = PY_INT_FROM_LONG(pfuncs[ifunc].ivalue[i]);
}
} else if (itype == DOUBLE) {
if (pfuncs[ifunc].ivarflag[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));
} else pValue = PyFloat_FromDouble(pfuncs[ifunc].dvalue[i]);
} else {
pValue = PyFloat_FromDouble(pfuncs[ifunc].dvalue[i]);
}
} else if (itype == STRING) {
if (pfuncs[ifunc].ivarflag[i]) {
str = input->variable->retrieve(pfuncs[ifunc].svalue[i]);
@ -277,10 +297,13 @@ void Python::invoke_function(int ifunc, char *result)
PyGILState_Release(gstate);
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) {
pValue = PyCObject_FromVoidPtr((void *) lmp,NULL);
pValue = PY_VOID_POINTER(lmp);
}
PyTuple_SetItem(pArgs,i,pValue);
}
@ -304,11 +327,11 @@ void Python::invoke_function(int ifunc, char *result)
if (pfuncs[ifunc].noutput) {
int otype = pfuncs[ifunc].otype;
if (otype == INT) {
sprintf(result,"%ld",PyInt_AsLong(pValue));
sprintf(result,"%ld",PY_INT_AS_LONG(pValue));
} else if (otype == DOUBLE) {
sprintf(result,"%.15g",PyFloat_AsDouble(pValue));
} else if (otype == STRING) {
char *pystr = PyString_AsString(pValue);
char *pystr = PY_STRING_AS_STRING(pValue);
if (pfuncs[ifunc].longstr)
strncpy(pfuncs[ifunc].longstr,pystr,pfuncs[ifunc].length_longstr);
else strncpy(result,pystr,VALUELENGTH-1);