Page MenuHomec4science

python_impl.cpp
No OneTemporary

File Metadata

Created
Sun, Jun 2, 22:28

python_impl.cpp

/* ----------------------------------------------------------------------
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
http://lammps.sandia.gov, Sandia National Laboratories
Steve Plimpton, sjplimp@sandia.gov
Copyright (2003) Sandia Corporation. Under the terms of Contract
DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
certain rights in this software. This software is distributed under
the GNU General Public License.
See the README file in the top-level LAMMPS directory.
------------------------------------------------------------------------- */
#include <Python.h>
#include "python.h"
#include "force.h"
#include "input.h"
#include "variable.h"
#include "memory.h"
#include "error.h"
using namespace LAMMPS_NS;
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
/* ---------------------------------------------------------------------- */
PythonImpl::PythonImpl(LAMMPS *lmp) : Pointers(lmp)
{
// pfuncs stores interface info for each Python function
nfunc = 0;
pfuncs = NULL;
// one-time initialization of Python interpreter
// pymain stores pointer to main module
external_interpreter = Py_IsInitialized();
Py_Initialize();
PyEval_InitThreads();
PyGILState_STATE gstate = PyGILState_Ensure();
PyObject *pModule = PyImport_AddModule("__main__");
if (!pModule) error->all(FLERR,"Could not initialize embedded Python");
pyMain = (void *) pModule;
PyGILState_Release(gstate);
}
/* ---------------------------------------------------------------------- */
PythonImpl::~PythonImpl()
{
// clean up
PyGILState_STATE gstate = PyGILState_Ensure();
for (int i = 0; i < nfunc; i++) {
delete [] pfuncs[i].name;
deallocate(i);
PyObject *pFunc = (PyObject *) pfuncs[i].pFunc;
Py_XDECREF(pFunc);
}
// shutdown Python interpreter
if (pyMain && !external_interpreter) {
Py_Finalize();
}
else {
PyGILState_Release(gstate);
}
memory->sfree(pfuncs);
}
/* ---------------------------------------------------------------------- */
void PythonImpl::command(int narg, char **arg)
{
if (narg < 2) error->all(FLERR,"Invalid python command");
// if invoke is only keyword, invoke the previously defined function
if (narg == 2 && strcmp(arg[1],"invoke") == 0) {
int ifunc = find(arg[0]);
if (ifunc < 0) error->all(FLERR,"Python invoke of undefined function");
char *str = NULL;
if (noutput) {
str = input->variable->pythonstyle(pfuncs[ifunc].ovarname,
pfuncs[ifunc].name);
if (!str)
error->all(FLERR,"Python variable does not match Python function");
}
invoke_function(ifunc,str);
return;
}
// parse optional args, invoke is not allowed in this mode
ninput = noutput = 0;
istr = NULL;
ostr = NULL;
format = NULL;
length_longstr = 0;
char *pyfile = NULL;
char *herestr = NULL;
int existflag = 0;
int iarg = 1;
while (iarg < narg) {
if (strcmp(arg[iarg],"input") == 0) {
if (iarg+2 > narg) error->all(FLERR,"Invalid python command");
ninput = force->inumeric(FLERR,arg[iarg+1]);
if (ninput < 0) error->all(FLERR,"Invalid python command");
iarg += 2;
istr = new char*[ninput];
if (iarg+ninput > narg) error->all(FLERR,"Invalid python command");
for (int i = 0; i < ninput; i++) istr[i] = arg[iarg+i];
iarg += ninput;
} else if (strcmp(arg[iarg],"return") == 0) {
if (iarg+2 > narg) error->all(FLERR,"Invalid python command");
noutput = 1;
ostr = arg[iarg+1];
iarg += 2;
} else if (strcmp(arg[iarg],"format") == 0) {
if (iarg+2 > narg) error->all(FLERR,"Invalid python command");
int n = strlen(arg[iarg+1]) + 1;
format = new char[n];
strcpy(format,arg[iarg+1]);
iarg += 2;
} else if (strcmp(arg[iarg],"length") == 0) {
if (iarg+2 > narg) error->all(FLERR,"Invalid python command");
length_longstr = force->inumeric(FLERR,arg[iarg+1]);
if (length_longstr <= 0) error->all(FLERR,"Invalid python command");
iarg += 2;
} else if (strcmp(arg[iarg],"file") == 0) {
if (iarg+2 > narg) error->all(FLERR,"Invalid python command");
delete[] pyfile;
int n = strlen(arg[iarg+1]) + 1;
pyfile = new char[n];
strcpy(pyfile,arg[iarg+1]);
iarg += 2;
} else if (strcmp(arg[iarg],"here") == 0) {
if (iarg+2 > narg) error->all(FLERR,"Invalid python command");
herestr = arg[iarg+1];
iarg += 2;
} else if (strcmp(arg[iarg],"exists") == 0) {
existflag = 1;
iarg++;
} else error->all(FLERR,"Invalid python command");
}
if (pyfile && herestr) error->all(FLERR,"Invalid python command");
if (pyfile && existflag) error->all(FLERR,"Invalid python command");
if (herestr && existflag) error->all(FLERR,"Invalid python command");
// create or overwrite entry in pfuncs vector with name = arg[0]
int ifunc = create_entry(arg[0]);
PyGILState_STATE gstate = PyGILState_Ensure();
// send Python code to Python interpreter
// file: read the file via PyRun_SimpleFile()
// here: process the here string directly
// exist: do nothing, assume code has already been run
if (pyfile) {
FILE *fp = fopen(pyfile,"r");
if (fp == NULL) {
PyGILState_Release(gstate);
error->all(FLERR,"Could not open Python file");
}
int err = PyRun_SimpleFile(fp,pyfile);
if (err) {
PyGILState_Release(gstate);
error->all(FLERR,"Could not process Python file");
}
fclose(fp);
} else if (herestr) {
int err = PyRun_SimpleString(herestr);
if (err) {
PyGILState_Release(gstate);
error->all(FLERR,"Could not process Python string");
}
}
// pFunc = function object for requested function
PyObject *pModule = (PyObject *) pyMain;
PyObject *pFunc = PyObject_GetAttrString(pModule,pfuncs[ifunc].name);
if (!pFunc) {
PyGILState_Release(gstate);
error->all(FLERR,"Could not find Python function");
}
if (!PyCallable_Check(pFunc)) {
PyGILState_Release(gstate);
error->all(FLERR,"Python function is not callable");
}
pfuncs[ifunc].pFunc = (void *) pFunc;
// clean-up input storage
delete [] istr;
delete [] format;
delete [] pyfile;
PyGILState_Release(gstate);
}
/* ------------------------------------------------------------------ */
void PythonImpl::invoke_function(int ifunc, char *result)
{
PyGILState_STATE gstate = PyGILState_Ensure();
PyObject *pValue;
char *str;
PyObject *pFunc = (PyObject *) pfuncs[ifunc].pFunc;
// create Python tuple of input arguments
int ninput = pfuncs[ifunc].ninput;
PyObject *pArgs = PyTuple_New(ninput);
if (!pArgs) {
PyGILState_Release(gstate);
error->all(FLERR,"Could not create Python function arguments");
}
for (int i = 0; i < ninput; i++) {
int itype = pfuncs[ifunc].itype[i];
if (itype == INT) {
if (pfuncs[ifunc].ivarflag[i]) {
str = input->variable->retrieve(pfuncs[ifunc].svalue[i]);
if (!str) {
PyGILState_Release(gstate);
error->all(FLERR,"Could not evaluate Python function input variable");
}
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]);
if (!str) {
PyGILState_Release(gstate);
error->all(FLERR,"Could not evaluate Python function input variable");
}
pValue = PyFloat_FromDouble(atof(str));
} 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]);
if (!str) {
PyGILState_Release(gstate);
error->all(FLERR,"Could not evaluate Python function input variable");
}
pValue = PY_STRING_FROM_STRING(str);
} else {
pValue = PY_STRING_FROM_STRING(pfuncs[ifunc].svalue[i]);
}
} else if (itype == PTR) {
pValue = PY_VOID_POINTER(lmp);
}
PyTuple_SetItem(pArgs,i,pValue);
}
// call the Python function
// error check with one() since only some procs may fail
pValue = PyObject_CallObject(pFunc,pArgs);
if (!pValue) {
PyGILState_Release(gstate);
error->one(FLERR,"Python function evaluation failed");
}
Py_DECREF(pArgs);
// function returned a value
// assign it to result string stored by python-style variable
// or if user specified a length, assign it to longstr
if (pfuncs[ifunc].noutput) {
int otype = pfuncs[ifunc].otype;
if (otype == INT) {
sprintf(result,"%ld",PY_INT_AS_LONG(pValue));
} else if (otype == DOUBLE) {
sprintf(result,"%.15g",PyFloat_AsDouble(pValue));
} else if (otype == STRING) {
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);
}
Py_DECREF(pValue);
}
PyGILState_Release(gstate);
}
/* ------------------------------------------------------------------ */
int PythonImpl::find(char *name)
{
for (int i = 0; i < nfunc; i++)
if (strcmp(name,pfuncs[i].name) == 0) return i;
return -1;
}
/* ------------------------------------------------------------------ */
int PythonImpl::variable_match(char *name, char *varname, int numeric)
{
int ifunc = find(name);
if (ifunc < 0) return -1;
if (pfuncs[ifunc].noutput == 0) return -1;
if (strcmp(pfuncs[ifunc].ovarname,varname) != 0) return -1;
if (numeric && pfuncs[ifunc].otype == STRING) return -1;
return ifunc;
}
/* ------------------------------------------------------------------ */
char *PythonImpl::long_string(int ifunc)
{
return pfuncs[ifunc].longstr;
}
/* ------------------------------------------------------------------ */
int PythonImpl::create_entry(char *name)
{
// ifunc = index to entry by name in pfuncs vector, can be old or new
// free old vectors if overwriting old pfunc
int ifunc = find(name);
if (ifunc < 0) {
ifunc = nfunc;
nfunc++;
pfuncs = (PyFunc *)
memory->srealloc(pfuncs,nfunc*sizeof(struct PyFunc),"python:pfuncs");
int n = strlen(name) + 1;
pfuncs[ifunc].name = new char[n];
strcpy(pfuncs[ifunc].name,name);
} else deallocate(ifunc);
pfuncs[ifunc].ninput = ninput;
pfuncs[ifunc].noutput = noutput;
if (!format && ninput+noutput)
error->all(FLERR,"Invalid python command");
else if (format && strlen(format) != ninput+noutput)
error->all(FLERR,"Invalid python command");
// process inputs as values or variables
pfuncs[ifunc].itype = new int[ninput];
pfuncs[ifunc].ivarflag = new int[ninput];
pfuncs[ifunc].ivalue = new int[ninput];
pfuncs[ifunc].dvalue = new double[ninput];
pfuncs[ifunc].svalue = new char*[ninput];
for (int i = 0; i < ninput; i++) {
pfuncs[ifunc].svalue[i] = NULL;
char type = format[i];
if (type == 'i') {
pfuncs[ifunc].itype[i] = INT;
if (strstr(istr[i],"v_") == istr[i]) {
pfuncs[ifunc].ivarflag[i] = 1;
int n = strlen(&istr[i][2]) + 1;
pfuncs[ifunc].svalue[i] = new char[n];
strcpy(pfuncs[ifunc].svalue[i],&istr[i][2]);
} else {
pfuncs[ifunc].ivarflag[i] = 0;
pfuncs[ifunc].ivalue[i] = force->inumeric(FLERR,istr[i]);
}
} else if (type == 'f') {
pfuncs[ifunc].itype[i] = DOUBLE;
if (strstr(istr[i],"v_") == istr[i]) {
pfuncs[ifunc].ivarflag[i] = 1;
int n = strlen(&istr[i][2]) + 1;
pfuncs[ifunc].svalue[i] = new char[n];
strcpy(pfuncs[ifunc].svalue[i],&istr[i][2]);
} else {
pfuncs[ifunc].ivarflag[i] = 0;
pfuncs[ifunc].dvalue[i] = force->numeric(FLERR,istr[i]);
}
} else if (type == 's') {
pfuncs[ifunc].itype[i] = STRING;
if (strstr(istr[i],"v_") == istr[i]) {
pfuncs[ifunc].ivarflag[i] = 1;
int n = strlen(&istr[i][2]) + 1;
pfuncs[ifunc].svalue[i] = new char[n];
strcpy(pfuncs[ifunc].svalue[i],&istr[i][2]);
} else {
pfuncs[ifunc].ivarflag[i] = 0;
int n = strlen(istr[i]) + 1;
pfuncs[ifunc].svalue[i] = new char[n];
strcpy(pfuncs[ifunc].svalue[i],istr[i]);
}
} else if (type == 'p') {
pfuncs[ifunc].ivarflag[i] = 0;
pfuncs[ifunc].itype[i] = PTR;
if (strcmp(istr[i],"SELF") != 0)
error->all(FLERR,"Invalid python command");
} else error->all(FLERR,"Invalid python command");
}
// process output as value or variable
pfuncs[ifunc].ovarname = NULL;
pfuncs[ifunc].longstr = NULL;
if (!noutput) return ifunc;
char type = format[ninput];
if (type == 'i') pfuncs[ifunc].otype = INT;
else if (type == 'f') pfuncs[ifunc].otype = DOUBLE;
else if (type == 's') pfuncs[ifunc].otype = STRING;
else error->all(FLERR,"Invalid python command");
if (length_longstr) {
if (pfuncs[ifunc].otype != STRING)
error->all(FLERR,"Python command length keyword "
"cannot be used unless output is a string");
pfuncs[ifunc].length_longstr = length_longstr;
pfuncs[ifunc].longstr = new char[length_longstr+1];
}
if (strstr(ostr,"v_") != ostr) error->all(FLERR,"Invalid python command");
int n = strlen(&ostr[2]) + 1;
pfuncs[ifunc].ovarname = new char[n];
strcpy(pfuncs[ifunc].ovarname,&ostr[2]);
return ifunc;
}
/* ------------------------------------------------------------------ */
void PythonImpl::deallocate(int i)
{
delete [] pfuncs[i].itype;
delete [] pfuncs[i].ivarflag;
delete [] pfuncs[i].ivalue;
delete [] pfuncs[i].dvalue;
for (int j = 0; j < pfuncs[i].ninput; j++)
delete [] pfuncs[i].svalue[j];
delete [] pfuncs[i].svalue;
delete [] pfuncs[i].ovarname;
delete [] pfuncs[i].longstr;
}

Event Timeline