Merge pull request #2923 from akohlmey/python-finalize-take2
Treat calling Py_Finalize() more like MPI_Finalize() and avoid crashes
This commit is contained in:
@ -10,6 +10,7 @@ This section documents the following functions:
|
|||||||
- :cpp:func:`lammps_mpi_init`
|
- :cpp:func:`lammps_mpi_init`
|
||||||
- :cpp:func:`lammps_mpi_finalize`
|
- :cpp:func:`lammps_mpi_finalize`
|
||||||
- :cpp:func:`lammps_kokkos_finalize`
|
- :cpp:func:`lammps_kokkos_finalize`
|
||||||
|
- :cpp:func:`lammps_python_finalize`
|
||||||
|
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
@ -104,3 +105,13 @@ calling program.
|
|||||||
|
|
||||||
.. doxygenfunction:: lammps_mpi_finalize
|
.. doxygenfunction:: lammps_mpi_finalize
|
||||||
:project: progguide
|
:project: progguide
|
||||||
|
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
.. doxygenfunction:: lammps_kokkos_finalize
|
||||||
|
:project: progguide
|
||||||
|
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
.. doxygenfunction:: lammps_python_finalize
|
||||||
|
:project: progguide
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
// clang-format off
|
|
||||||
/* ----------------------------------------------------------------------
|
/* ----------------------------------------------------------------------
|
||||||
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
|
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
|
||||||
https://www.lammps.org/, Sandia National Laboratories
|
https://www.lammps.org/, Sandia National Laboratories
|
||||||
@ -25,8 +24,8 @@
|
|||||||
#include "python_utils.h"
|
#include "python_utils.h"
|
||||||
#include "variable.h"
|
#include "variable.h"
|
||||||
|
|
||||||
#include <cstring>
|
|
||||||
#include <Python.h> // IWYU pragma: export
|
#include <Python.h> // IWYU pragma: export
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
#ifdef MLIAP_PYTHON
|
#ifdef MLIAP_PYTHON
|
||||||
#include "mliap_model_python.h"
|
#include "mliap_model_python.h"
|
||||||
@ -38,10 +37,7 @@
|
|||||||
|
|
||||||
using namespace LAMMPS_NS;
|
using namespace LAMMPS_NS;
|
||||||
|
|
||||||
enum{NONE,INT,DOUBLE,STRING,PTR};
|
enum { NONE, INT, DOUBLE, STRING, PTR };
|
||||||
|
|
||||||
#define VALUELENGTH 64 // also in variable.cpp
|
|
||||||
|
|
||||||
|
|
||||||
/* ---------------------------------------------------------------------- */
|
/* ---------------------------------------------------------------------- */
|
||||||
|
|
||||||
@ -55,7 +51,7 @@ PythonImpl::PythonImpl(LAMMPS *lmp) : Pointers(lmp)
|
|||||||
#if PY_MAJOR_VERSION >= 3
|
#if PY_MAJOR_VERSION >= 3
|
||||||
#ifndef Py_LIMITED_API
|
#ifndef Py_LIMITED_API
|
||||||
// check for PYTHONUNBUFFERED environment variable
|
// check for PYTHONUNBUFFERED environment variable
|
||||||
const char * PYTHONUNBUFFERED = getenv("PYTHONUNBUFFERED");
|
const char *PYTHONUNBUFFERED = getenv("PYTHONUNBUFFERED");
|
||||||
|
|
||||||
if (PYTHONUNBUFFERED != nullptr && strcmp(PYTHONUNBUFFERED, "1") == 0) {
|
if (PYTHONUNBUFFERED != nullptr && strcmp(PYTHONUNBUFFERED, "1") == 0) {
|
||||||
// Python Global configuration variable
|
// Python Global configuration variable
|
||||||
@ -65,15 +61,11 @@ PythonImpl::PythonImpl(LAMMPS *lmp) : Pointers(lmp)
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// one-time initialization of Python interpreter
|
|
||||||
// pyMain stores pointer to main module
|
|
||||||
external_interpreter = Py_IsInitialized();
|
|
||||||
|
|
||||||
#ifdef MLIAP_PYTHON
|
#ifdef MLIAP_PYTHON
|
||||||
// Inform python intialization scheme of the mliappy module.
|
// Inform python intialization scheme of the mliappy module.
|
||||||
// This -must- happen before python is initialized.
|
// This -must- happen before python is initialized.
|
||||||
int err = PyImport_AppendInittab("mliap_model_python_couple", PyInit_mliap_model_python_couple);
|
int err = PyImport_AppendInittab("mliap_model_python_couple", PyInit_mliap_model_python_couple);
|
||||||
if (err) error->all(FLERR,"Could not register MLIAPPY embedded python module.");
|
if (err) error->all(FLERR, "Could not register MLIAPPY embedded python module.");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Py_Initialize();
|
Py_Initialize();
|
||||||
@ -82,15 +74,13 @@ PythonImpl::PythonImpl(LAMMPS *lmp) : Pointers(lmp)
|
|||||||
// With Python 3.7 this function is now called by Py_Initialize()
|
// With Python 3.7 this function is now called by Py_Initialize()
|
||||||
// Deprecated since version 3.9, will be removed in version 3.11
|
// Deprecated since version 3.9, will be removed in version 3.11
|
||||||
#if PY_MAJOR_VERSION < 3 || PY_MINOR_VERSION < 7
|
#if PY_MAJOR_VERSION < 3 || PY_MINOR_VERSION < 7
|
||||||
if (!PyEval_ThreadsInitialized()) {
|
if (!PyEval_ThreadsInitialized()) { PyEval_InitThreads(); }
|
||||||
PyEval_InitThreads();
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
PyUtils::GIL lock;
|
PyUtils::GIL lock;
|
||||||
|
|
||||||
PyObject *pModule = PyImport_AddModule("__main__");
|
PyObject *pModule = PyImport_AddModule("__main__");
|
||||||
if (!pModule) error->all(FLERR,"Could not initialize embedded Python");
|
if (!pModule) error->all(FLERR, "Could not initialize embedded Python");
|
||||||
|
|
||||||
pyMain = (void *) pModule;
|
pyMain = (void *) pModule;
|
||||||
}
|
}
|
||||||
@ -104,18 +94,12 @@ PythonImpl::~PythonImpl()
|
|||||||
PyUtils::GIL lock;
|
PyUtils::GIL lock;
|
||||||
|
|
||||||
for (int i = 0; i < nfunc; i++) {
|
for (int i = 0; i < nfunc; i++) {
|
||||||
delete [] pfuncs[i].name;
|
delete[] pfuncs[i].name;
|
||||||
deallocate(i);
|
deallocate(i);
|
||||||
Py_CLEAR(pfuncs[i].pFunc);
|
Py_CLEAR(pfuncs[i].pFunc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// shutdown Python interpreter
|
|
||||||
if (!external_interpreter) {
|
|
||||||
PyGILState_Ensure();
|
|
||||||
Py_Finalize();
|
|
||||||
}
|
|
||||||
|
|
||||||
memory->sfree(pfuncs);
|
memory->sfree(pfuncs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,39 +107,37 @@ PythonImpl::~PythonImpl()
|
|||||||
|
|
||||||
void PythonImpl::command(int narg, char **arg)
|
void PythonImpl::command(int narg, char **arg)
|
||||||
{
|
{
|
||||||
if (narg < 2) error->all(FLERR,"Invalid python command");
|
if (narg < 2) error->all(FLERR, "Invalid python command");
|
||||||
|
|
||||||
// if invoke is only keyword, invoke the previously defined function
|
// if invoke is only keyword, invoke the previously defined function
|
||||||
|
|
||||||
if (narg == 2 && strcmp(arg[1],"invoke") == 0) {
|
if (narg == 2 && strcmp(arg[1], "invoke") == 0) {
|
||||||
int ifunc = find(arg[0]);
|
int ifunc = find(arg[0]);
|
||||||
if (ifunc < 0) error->all(FLERR,"Python invoke of undefined function");
|
if (ifunc < 0) error->all(FLERR, "Python invoke of undefined function");
|
||||||
|
|
||||||
char *str = nullptr;
|
char *str = nullptr;
|
||||||
if (pfuncs[ifunc].noutput) {
|
if (pfuncs[ifunc].noutput) {
|
||||||
str = input->variable->pythonstyle(pfuncs[ifunc].ovarname,
|
str = input->variable->pythonstyle(pfuncs[ifunc].ovarname, pfuncs[ifunc].name);
|
||||||
pfuncs[ifunc].name);
|
if (!str) error->all(FLERR, "Python variable does not match Python function");
|
||||||
if (!str)
|
|
||||||
error->all(FLERR,"Python variable does not match Python function");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
invoke_function(ifunc,str);
|
invoke_function(ifunc, str);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if source is only keyword, execute the python code
|
// if source is only keyword, execute the python code
|
||||||
|
|
||||||
if (narg == 3 && strcmp(arg[1],"source") == 0) {
|
if (narg == 3 && strcmp(arg[1], "source") == 0) {
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
FILE *fp = fopen(arg[2],"r");
|
FILE *fp = fopen(arg[2], "r");
|
||||||
if (fp == nullptr)
|
if (fp == nullptr)
|
||||||
err = execute_string(arg[2]);
|
err = execute_string(arg[2]);
|
||||||
else
|
else
|
||||||
err = execute_file(arg[2]);
|
err = execute_file(arg[2]);
|
||||||
|
|
||||||
if (fp) fclose(fp);
|
if (fp) fclose(fp);
|
||||||
if (err) error->all(FLERR,"Could not process Python source command");
|
if (err) error->all(FLERR, "Could not process Python source command");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -174,53 +156,53 @@ void PythonImpl::command(int narg, char **arg)
|
|||||||
|
|
||||||
int iarg = 1;
|
int iarg = 1;
|
||||||
while (iarg < narg) {
|
while (iarg < narg) {
|
||||||
if (strcmp(arg[iarg],"input") == 0) {
|
if (strcmp(arg[iarg], "input") == 0) {
|
||||||
if (iarg+2 > narg) error->all(FLERR,"Invalid python command");
|
if (iarg + 2 > narg) error->all(FLERR, "Invalid python command");
|
||||||
ninput = utils::inumeric(FLERR,arg[iarg+1],false,lmp);
|
ninput = utils::inumeric(FLERR, arg[iarg + 1], false, lmp);
|
||||||
if (ninput < 0) error->all(FLERR,"Invalid python command");
|
if (ninput < 0) error->all(FLERR, "Invalid python command");
|
||||||
iarg += 2;
|
iarg += 2;
|
||||||
delete[] istr;
|
delete[] istr;
|
||||||
istr = new char*[ninput];
|
istr = new char *[ninput];
|
||||||
if (iarg+ninput > narg) error->all(FLERR,"Invalid python command");
|
if (iarg + ninput > narg) error->all(FLERR, "Invalid python command");
|
||||||
for (int i = 0; i < ninput; i++) istr[i] = arg[iarg+i];
|
for (int i = 0; i < ninput; i++) istr[i] = arg[iarg + i];
|
||||||
iarg += ninput;
|
iarg += ninput;
|
||||||
} else if (strcmp(arg[iarg],"return") == 0) {
|
} else if (strcmp(arg[iarg], "return") == 0) {
|
||||||
if (iarg+2 > narg) error->all(FLERR,"Invalid python command");
|
if (iarg + 2 > narg) error->all(FLERR, "Invalid python command");
|
||||||
noutput = 1;
|
noutput = 1;
|
||||||
delete[] ostr;
|
ostr = arg[iarg + 1];
|
||||||
ostr = arg[iarg+1];
|
|
||||||
iarg += 2;
|
iarg += 2;
|
||||||
} else if (strcmp(arg[iarg],"format") == 0) {
|
} else if (strcmp(arg[iarg], "format") == 0) {
|
||||||
if (iarg+2 > narg) error->all(FLERR,"Invalid python command");
|
if (iarg + 2 > narg) error->all(FLERR, "Invalid python command");
|
||||||
format = utils::strdup(arg[iarg+1]);
|
format = utils::strdup(arg[iarg + 1]);
|
||||||
iarg += 2;
|
iarg += 2;
|
||||||
} else if (strcmp(arg[iarg],"length") == 0) {
|
} else if (strcmp(arg[iarg], "length") == 0) {
|
||||||
if (iarg+2 > narg) error->all(FLERR,"Invalid python command");
|
if (iarg + 2 > narg) error->all(FLERR, "Invalid python command");
|
||||||
length_longstr = utils::inumeric(FLERR,arg[iarg+1],false,lmp);
|
length_longstr = utils::inumeric(FLERR, arg[iarg + 1], false, lmp);
|
||||||
if (length_longstr <= 0) error->all(FLERR,"Invalid python command");
|
if (length_longstr <= 0) error->all(FLERR, "Invalid python command");
|
||||||
iarg += 2;
|
iarg += 2;
|
||||||
} else if (strcmp(arg[iarg],"file") == 0) {
|
} else if (strcmp(arg[iarg], "file") == 0) {
|
||||||
if (iarg+2 > narg) error->all(FLERR,"Invalid python command");
|
if (iarg + 2 > narg) error->all(FLERR, "Invalid python command");
|
||||||
delete[] pyfile;
|
delete[] pyfile;
|
||||||
pyfile = utils::strdup(arg[iarg+1]);
|
pyfile = utils::strdup(arg[iarg + 1]);
|
||||||
iarg += 2;
|
iarg += 2;
|
||||||
} else if (strcmp(arg[iarg],"here") == 0) {
|
} else if (strcmp(arg[iarg], "here") == 0) {
|
||||||
if (iarg+2 > narg) error->all(FLERR,"Invalid python command");
|
if (iarg + 2 > narg) error->all(FLERR, "Invalid python command");
|
||||||
herestr = arg[iarg+1];
|
herestr = arg[iarg + 1];
|
||||||
iarg += 2;
|
iarg += 2;
|
||||||
} else if (strcmp(arg[iarg],"exists") == 0) {
|
} else if (strcmp(arg[iarg], "exists") == 0) {
|
||||||
existflag = 1;
|
existflag = 1;
|
||||||
iarg++;
|
iarg++;
|
||||||
} else error->all(FLERR,"Invalid python command");
|
} else
|
||||||
|
error->all(FLERR, "Invalid python command");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pyfile && herestr) 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 (pyfile && existflag) error->all(FLERR, "Invalid python command");
|
||||||
if (herestr && 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]
|
// create or overwrite entry in pfuncs vector with name = arg[0]
|
||||||
|
|
||||||
int ifunc = create_entry(arg[0],ninput,noutput,length_longstr, istr, ostr, format);
|
int ifunc = create_entry(arg[0], ninput, noutput, length_longstr, istr, ostr, format);
|
||||||
|
|
||||||
PyUtils::GIL lock;
|
PyUtils::GIL lock;
|
||||||
|
|
||||||
@ -230,18 +212,18 @@ void PythonImpl::command(int narg, char **arg)
|
|||||||
// exist: do nothing, assume code has already been run
|
// exist: do nothing, assume code has already been run
|
||||||
|
|
||||||
if (pyfile) {
|
if (pyfile) {
|
||||||
FILE *fp = fopen(pyfile,"r");
|
FILE *fp = fopen(pyfile, "r");
|
||||||
|
|
||||||
if (fp == nullptr) {
|
if (fp == nullptr) {
|
||||||
PyUtils::Print_Errors();
|
PyUtils::Print_Errors();
|
||||||
error->all(FLERR,"Could not open Python file");
|
error->all(FLERR, "Could not open Python file");
|
||||||
}
|
}
|
||||||
|
|
||||||
int err = PyRun_SimpleFile(fp,pyfile);
|
int err = PyRun_SimpleFile(fp, pyfile);
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
PyUtils::Print_Errors();
|
PyUtils::Print_Errors();
|
||||||
error->all(FLERR,"Could not process Python file");
|
error->all(FLERR, "Could not process Python file");
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
@ -250,34 +232,32 @@ void PythonImpl::command(int narg, char **arg)
|
|||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
PyUtils::Print_Errors();
|
PyUtils::Print_Errors();
|
||||||
error->all(FLERR,"Could not process Python string");
|
error->all(FLERR, "Could not process Python string");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// pFunc = function object for requested function
|
// pFunc = function object for requested function
|
||||||
|
|
||||||
PyObject *pModule = (PyObject *) pyMain;
|
PyObject *pModule = (PyObject *) pyMain;
|
||||||
PyObject *pFunc = PyObject_GetAttrString(pModule,pfuncs[ifunc].name);
|
PyObject *pFunc = PyObject_GetAttrString(pModule, pfuncs[ifunc].name);
|
||||||
|
|
||||||
if (!pFunc) {
|
if (!pFunc) {
|
||||||
PyUtils::Print_Errors();
|
PyUtils::Print_Errors();
|
||||||
error->all(FLERR,"Could not find Python function {}",
|
error->all(FLERR, "Could not find Python function {}", pfuncs[ifunc].name);
|
||||||
pfuncs[ifunc].name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!PyCallable_Check(pFunc)) {
|
if (!PyCallable_Check(pFunc)) {
|
||||||
PyUtils::Print_Errors();
|
PyUtils::Print_Errors();
|
||||||
error->all(FLERR,"Python function {} is not callable",
|
error->all(FLERR, "Python function {} is not callable", pfuncs[ifunc].name);
|
||||||
pfuncs[ifunc].name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pfuncs[ifunc].pFunc = (void *) pFunc;
|
pfuncs[ifunc].pFunc = (void *) pFunc;
|
||||||
|
|
||||||
// clean-up input storage
|
// clean-up input storage
|
||||||
|
|
||||||
delete [] istr;
|
delete[] istr;
|
||||||
delete [] format;
|
delete[] format;
|
||||||
delete [] pyfile;
|
delete[] pyfile;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------ */
|
||||||
@ -295,20 +275,14 @@ void PythonImpl::invoke_function(int ifunc, char *result)
|
|||||||
int ninput = pfuncs[ifunc].ninput;
|
int ninput = pfuncs[ifunc].ninput;
|
||||||
PyObject *pArgs = PyTuple_New(ninput);
|
PyObject *pArgs = PyTuple_New(ninput);
|
||||||
|
|
||||||
if (!pArgs) {
|
if (!pArgs) { error->all(FLERR, "Could not create Python function arguments"); }
|
||||||
error->all(FLERR,"Could not create Python function arguments");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < ninput; i++) {
|
for (int i = 0; i < ninput; i++) {
|
||||||
int itype = pfuncs[ifunc].itype[i];
|
int itype = pfuncs[ifunc].itype[i];
|
||||||
if (itype == INT) {
|
if (itype == INT) {
|
||||||
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]);
|
||||||
|
if (!str) { error->all(FLERR, "Could not evaluate Python function input variable"); }
|
||||||
if (!str) {
|
|
||||||
error->all(FLERR,"Could not evaluate Python function input variable");
|
|
||||||
}
|
|
||||||
|
|
||||||
pValue = PY_INT_FROM_LONG(atoi(str));
|
pValue = PY_INT_FROM_LONG(atoi(str));
|
||||||
} else {
|
} else {
|
||||||
pValue = PY_INT_FROM_LONG(pfuncs[ifunc].ivalue[i]);
|
pValue = PY_INT_FROM_LONG(pfuncs[ifunc].ivalue[i]);
|
||||||
@ -316,11 +290,7 @@ void PythonImpl::invoke_function(int ifunc, char *result)
|
|||||||
} 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]);
|
||||||
|
if (!str) { error->all(FLERR, "Could not evaluate Python function input variable"); }
|
||||||
if (!str) {
|
|
||||||
error->all(FLERR,"Could not evaluate Python function input variable");
|
|
||||||
}
|
|
||||||
|
|
||||||
pValue = PyFloat_FromDouble(atof(str));
|
pValue = PyFloat_FromDouble(atof(str));
|
||||||
} else {
|
} else {
|
||||||
pValue = PyFloat_FromDouble(pfuncs[ifunc].dvalue[i]);
|
pValue = PyFloat_FromDouble(pfuncs[ifunc].dvalue[i]);
|
||||||
@ -328,10 +298,7 @@ void PythonImpl::invoke_function(int ifunc, char *result)
|
|||||||
} 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]);
|
||||||
if (!str) {
|
if (!str) { error->all(FLERR, "Could not evaluate Python function input variable"); }
|
||||||
error->all(FLERR,"Could not evaluate Python function input variable");
|
|
||||||
}
|
|
||||||
|
|
||||||
pValue = PY_STRING_FROM_STRING(str);
|
pValue = PY_STRING_FROM_STRING(str);
|
||||||
} else {
|
} else {
|
||||||
pValue = PY_STRING_FROM_STRING(pfuncs[ifunc].svalue[i]);
|
pValue = PY_STRING_FROM_STRING(pfuncs[ifunc].svalue[i]);
|
||||||
@ -339,20 +306,20 @@ void PythonImpl::invoke_function(int ifunc, char *result)
|
|||||||
} else if (itype == PTR) {
|
} else if (itype == PTR) {
|
||||||
pValue = PY_VOID_POINTER(lmp);
|
pValue = PY_VOID_POINTER(lmp);
|
||||||
} else {
|
} else {
|
||||||
error->all(FLERR,"Unsupported variable type");
|
error->all(FLERR, "Unsupported variable type");
|
||||||
}
|
}
|
||||||
PyTuple_SetItem(pArgs,i,pValue);
|
PyTuple_SetItem(pArgs, i, pValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
// call the Python function
|
// call the Python function
|
||||||
// error check with one() since only some procs may fail
|
// error check with one() since only some procs may fail
|
||||||
|
|
||||||
pValue = PyObject_CallObject(pFunc,pArgs);
|
pValue = PyObject_CallObject(pFunc, pArgs);
|
||||||
Py_CLEAR(pArgs);
|
Py_CLEAR(pArgs);
|
||||||
|
|
||||||
if (!pValue) {
|
if (!pValue) {
|
||||||
PyUtils::Print_Errors();
|
PyUtils::Print_Errors();
|
||||||
error->one(FLERR,"Python function evaluation failed");
|
error->one(FLERR, "Python function evaluation failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
// function returned a value
|
// function returned a value
|
||||||
@ -362,14 +329,15 @@ void PythonImpl::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",PY_INT_AS_LONG(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) {
|
||||||
const char *pystr = PY_STRING_AS_STRING(pValue);
|
const 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, Variable::VALUELENGTH - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Py_CLEAR(pValue);
|
Py_CLEAR(pValue);
|
||||||
@ -380,19 +348,18 @@ void PythonImpl::invoke_function(int ifunc, char *result)
|
|||||||
int PythonImpl::find(const char *name)
|
int PythonImpl::find(const char *name)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < nfunc; i++)
|
for (int i = 0; i < nfunc; i++)
|
||||||
if (strcmp(name,pfuncs[i].name) == 0) return i;
|
if (strcmp(name, pfuncs[i].name) == 0) return i;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
int PythonImpl::variable_match(const char *name, const char *varname,
|
int PythonImpl::variable_match(const char *name, const char *varname, int numeric)
|
||||||
int numeric)
|
|
||||||
{
|
{
|
||||||
int ifunc = find(name);
|
int ifunc = find(name);
|
||||||
if (ifunc < 0) return -1;
|
if (ifunc < 0) return -1;
|
||||||
if (pfuncs[ifunc].noutput == 0) return -1;
|
if (pfuncs[ifunc].noutput == 0) return -1;
|
||||||
if (strcmp(pfuncs[ifunc].ovarname,varname) != 0) return -1;
|
if (strcmp(pfuncs[ifunc].ovarname, varname) != 0) return -1;
|
||||||
if (numeric && pfuncs[ifunc].otype == STRING) return -1;
|
if (numeric && pfuncs[ifunc].otype == STRING) return -1;
|
||||||
return ifunc;
|
return ifunc;
|
||||||
}
|
}
|
||||||
@ -406,8 +373,8 @@ char *PythonImpl::long_string(int ifunc)
|
|||||||
|
|
||||||
/* ------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
int PythonImpl::create_entry(char *name, int ninput, int noutput, int length_longstr,
|
int PythonImpl::create_entry(char *name, int ninput, int noutput, int length_longstr, char **istr,
|
||||||
char **istr, char *ostr, char *format)
|
char *ostr, char *format)
|
||||||
{
|
{
|
||||||
// ifunc = index to entry by name in pfuncs vector, can be old or new
|
// ifunc = index to entry by name in pfuncs vector, can be old or new
|
||||||
// free old vectors if overwriting old pfunc
|
// free old vectors if overwriting old pfunc
|
||||||
@ -417,18 +384,18 @@ int PythonImpl::create_entry(char *name, int ninput, int noutput, int length_lon
|
|||||||
if (ifunc < 0) {
|
if (ifunc < 0) {
|
||||||
ifunc = nfunc;
|
ifunc = nfunc;
|
||||||
nfunc++;
|
nfunc++;
|
||||||
pfuncs = (PyFunc *)
|
pfuncs = (PyFunc *) memory->srealloc(pfuncs, nfunc * sizeof(struct PyFunc), "python:pfuncs");
|
||||||
memory->srealloc(pfuncs,nfunc*sizeof(struct PyFunc),"python:pfuncs");
|
|
||||||
pfuncs[ifunc].name = utils::strdup(name);
|
pfuncs[ifunc].name = utils::strdup(name);
|
||||||
} else deallocate(ifunc);
|
} else
|
||||||
|
deallocate(ifunc);
|
||||||
|
|
||||||
pfuncs[ifunc].ninput = ninput;
|
pfuncs[ifunc].ninput = ninput;
|
||||||
pfuncs[ifunc].noutput = noutput;
|
pfuncs[ifunc].noutput = noutput;
|
||||||
|
|
||||||
if (!format && ninput+noutput)
|
if (!format && ninput + noutput)
|
||||||
error->all(FLERR,"Invalid python command");
|
error->all(FLERR, "Invalid python command");
|
||||||
else if (format && ((int) strlen(format) != ninput+noutput))
|
else if (format && ((int) strlen(format) != ninput + noutput))
|
||||||
error->all(FLERR,"Invalid python command");
|
error->all(FLERR, "Invalid python command");
|
||||||
|
|
||||||
// process inputs as values or variables
|
// process inputs as values or variables
|
||||||
|
|
||||||
@ -436,34 +403,34 @@ int PythonImpl::create_entry(char *name, int ninput, int noutput, int length_lon
|
|||||||
pfuncs[ifunc].ivarflag = new int[ninput];
|
pfuncs[ifunc].ivarflag = new int[ninput];
|
||||||
pfuncs[ifunc].ivalue = new int[ninput];
|
pfuncs[ifunc].ivalue = new int[ninput];
|
||||||
pfuncs[ifunc].dvalue = new double[ninput];
|
pfuncs[ifunc].dvalue = new double[ninput];
|
||||||
pfuncs[ifunc].svalue = new char*[ninput];
|
pfuncs[ifunc].svalue = new char *[ninput];
|
||||||
|
|
||||||
for (int i = 0; i < ninput; i++) {
|
for (int i = 0; i < ninput; i++) {
|
||||||
pfuncs[ifunc].svalue[i] = nullptr;
|
pfuncs[ifunc].svalue[i] = nullptr;
|
||||||
char type = format[i];
|
char type = format[i];
|
||||||
if (type == 'i') {
|
if (type == 'i') {
|
||||||
pfuncs[ifunc].itype[i] = INT;
|
pfuncs[ifunc].itype[i] = INT;
|
||||||
if (utils::strmatch(istr[i],"^v_")) {
|
if (utils::strmatch(istr[i], "^v_")) {
|
||||||
pfuncs[ifunc].ivarflag[i] = 1;
|
pfuncs[ifunc].ivarflag[i] = 1;
|
||||||
pfuncs[ifunc].svalue[i] = utils::strdup(istr[i]+2);
|
pfuncs[ifunc].svalue[i] = utils::strdup(istr[i] + 2);
|
||||||
} else {
|
} else {
|
||||||
pfuncs[ifunc].ivarflag[i] = 0;
|
pfuncs[ifunc].ivarflag[i] = 0;
|
||||||
pfuncs[ifunc].ivalue[i] = utils::inumeric(FLERR,istr[i],false,lmp);
|
pfuncs[ifunc].ivalue[i] = utils::inumeric(FLERR, istr[i], false, lmp);
|
||||||
}
|
}
|
||||||
} else if (type == 'f') {
|
} else if (type == 'f') {
|
||||||
pfuncs[ifunc].itype[i] = DOUBLE;
|
pfuncs[ifunc].itype[i] = DOUBLE;
|
||||||
if (utils::strmatch(istr[i],"^v_")) {
|
if (utils::strmatch(istr[i], "^v_")) {
|
||||||
pfuncs[ifunc].ivarflag[i] = 1;
|
pfuncs[ifunc].ivarflag[i] = 1;
|
||||||
pfuncs[ifunc].svalue[i] = utils::strdup(istr[i]+2);
|
pfuncs[ifunc].svalue[i] = utils::strdup(istr[i] + 2);
|
||||||
} else {
|
} else {
|
||||||
pfuncs[ifunc].ivarflag[i] = 0;
|
pfuncs[ifunc].ivarflag[i] = 0;
|
||||||
pfuncs[ifunc].dvalue[i] = utils::numeric(FLERR,istr[i],false,lmp);
|
pfuncs[ifunc].dvalue[i] = utils::numeric(FLERR, istr[i], false, lmp);
|
||||||
}
|
}
|
||||||
} else if (type == 's') {
|
} else if (type == 's') {
|
||||||
pfuncs[ifunc].itype[i] = STRING;
|
pfuncs[ifunc].itype[i] = STRING;
|
||||||
if (utils::strmatch(istr[i],"^v_")) {
|
if (utils::strmatch(istr[i], "^v_")) {
|
||||||
pfuncs[ifunc].ivarflag[i] = 1;
|
pfuncs[ifunc].ivarflag[i] = 1;
|
||||||
pfuncs[ifunc].svalue[i] = utils::strdup(istr[i]+2);
|
pfuncs[ifunc].svalue[i] = utils::strdup(istr[i] + 2);
|
||||||
} else {
|
} else {
|
||||||
pfuncs[ifunc].ivarflag[i] = 0;
|
pfuncs[ifunc].ivarflag[i] = 0;
|
||||||
pfuncs[ifunc].svalue[i] = utils::strdup(istr[i]);
|
pfuncs[ifunc].svalue[i] = utils::strdup(istr[i]);
|
||||||
@ -471,10 +438,10 @@ int PythonImpl::create_entry(char *name, int ninput, int noutput, int length_lon
|
|||||||
} else if (type == 'p') {
|
} else if (type == 'p') {
|
||||||
pfuncs[ifunc].ivarflag[i] = 0;
|
pfuncs[ifunc].ivarflag[i] = 0;
|
||||||
pfuncs[ifunc].itype[i] = PTR;
|
pfuncs[ifunc].itype[i] = PTR;
|
||||||
if (strcmp(istr[i],"SELF") != 0)
|
if (strcmp(istr[i], "SELF") != 0) error->all(FLERR, "Invalid python command");
|
||||||
error->all(FLERR,"Invalid python command");
|
|
||||||
|
|
||||||
} else error->all(FLERR,"Invalid python command");
|
} else
|
||||||
|
error->all(FLERR, "Invalid python command");
|
||||||
}
|
}
|
||||||
|
|
||||||
// process output as value or variable
|
// process output as value or variable
|
||||||
@ -484,22 +451,25 @@ int PythonImpl::create_entry(char *name, int ninput, int noutput, int length_lon
|
|||||||
if (!noutput) return ifunc;
|
if (!noutput) return ifunc;
|
||||||
|
|
||||||
char type = format[ninput];
|
char type = format[ninput];
|
||||||
if (type == 'i') pfuncs[ifunc].otype = INT;
|
if (type == 'i')
|
||||||
else if (type == 'f') pfuncs[ifunc].otype = DOUBLE;
|
pfuncs[ifunc].otype = INT;
|
||||||
else if (type == 's') pfuncs[ifunc].otype = STRING;
|
else if (type == 'f')
|
||||||
else error->all(FLERR,"Invalid python command");
|
pfuncs[ifunc].otype = DOUBLE;
|
||||||
|
else if (type == 's')
|
||||||
|
pfuncs[ifunc].otype = STRING;
|
||||||
|
else
|
||||||
|
error->all(FLERR, "Invalid python command");
|
||||||
|
|
||||||
if (length_longstr) {
|
if (length_longstr) {
|
||||||
if (pfuncs[ifunc].otype != STRING)
|
if (pfuncs[ifunc].otype != STRING)
|
||||||
error->all(FLERR,"Python command length keyword "
|
error->all(FLERR, "Python command length keyword cannot be used unless output is a string");
|
||||||
"cannot be used unless output is a string");
|
|
||||||
pfuncs[ifunc].length_longstr = length_longstr;
|
pfuncs[ifunc].length_longstr = length_longstr;
|
||||||
pfuncs[ifunc].longstr = new char[length_longstr+1];
|
pfuncs[ifunc].longstr = new char[length_longstr + 1];
|
||||||
pfuncs[ifunc].longstr[length_longstr] = '\0';
|
pfuncs[ifunc].longstr[length_longstr] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strstr(ostr,"v_") != ostr) error->all(FLERR,"Invalid python command");
|
if (strstr(ostr, "v_") != ostr) error->all(FLERR, "Invalid python command");
|
||||||
pfuncs[ifunc].ovarname = utils::strdup(ostr+2);
|
pfuncs[ifunc].ovarname = utils::strdup(ostr + 2);
|
||||||
|
|
||||||
return ifunc;
|
return ifunc;
|
||||||
}
|
}
|
||||||
@ -516,11 +486,11 @@ int PythonImpl::execute_string(char *cmd)
|
|||||||
|
|
||||||
int PythonImpl::execute_file(char *fname)
|
int PythonImpl::execute_file(char *fname)
|
||||||
{
|
{
|
||||||
FILE *fp = fopen(fname,"r");
|
FILE *fp = fopen(fname, "r");
|
||||||
if (fp == nullptr) return -1;
|
if (fp == nullptr) return -1;
|
||||||
|
|
||||||
PyUtils::GIL lock;
|
PyUtils::GIL lock;
|
||||||
int err = PyRun_SimpleFile(fp,fname);
|
int err = PyRun_SimpleFile(fp, fname);
|
||||||
|
|
||||||
if (fp) fclose(fp);
|
if (fp) fclose(fp);
|
||||||
return err;
|
return err;
|
||||||
@ -530,15 +500,14 @@ int PythonImpl::execute_file(char *fname)
|
|||||||
|
|
||||||
void PythonImpl::deallocate(int i)
|
void PythonImpl::deallocate(int i)
|
||||||
{
|
{
|
||||||
delete [] pfuncs[i].itype;
|
delete[] pfuncs[i].itype;
|
||||||
delete [] pfuncs[i].ivarflag;
|
delete[] pfuncs[i].ivarflag;
|
||||||
delete [] pfuncs[i].ivalue;
|
delete[] pfuncs[i].ivalue;
|
||||||
delete [] pfuncs[i].dvalue;
|
delete[] pfuncs[i].dvalue;
|
||||||
for (int j = 0; j < pfuncs[i].ninput; j++)
|
for (int j = 0; j < pfuncs[i].ninput; j++) delete[] pfuncs[i].svalue[j];
|
||||||
delete [] pfuncs[i].svalue[j];
|
delete[] pfuncs[i].svalue;
|
||||||
delete [] pfuncs[i].svalue;
|
delete[] pfuncs[i].ovarname;
|
||||||
delete [] pfuncs[i].ovarname;
|
delete[] pfuncs[i].longstr;
|
||||||
delete [] pfuncs[i].longstr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------ */
|
||||||
@ -547,3 +516,10 @@ bool PythonImpl::has_minimum_version(int major, int minor)
|
|||||||
{
|
{
|
||||||
return (PY_MAJOR_VERSION == major && PY_MINOR_VERSION >= minor) || (PY_MAJOR_VERSION > major);
|
return (PY_MAJOR_VERSION == major && PY_MINOR_VERSION >= minor) || (PY_MAJOR_VERSION > major);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
void PythonImpl::finalize()
|
||||||
|
{
|
||||||
|
if (Py_IsInitialized()) Py_Finalize();
|
||||||
|
}
|
||||||
|
|||||||
@ -20,9 +20,8 @@
|
|||||||
namespace LAMMPS_NS {
|
namespace LAMMPS_NS {
|
||||||
|
|
||||||
class PythonImpl : protected Pointers, public PythonInterface {
|
class PythonImpl : protected Pointers, public PythonInterface {
|
||||||
public:
|
|
||||||
bool external_interpreter;
|
|
||||||
|
|
||||||
|
public:
|
||||||
PythonImpl(class LAMMPS *);
|
PythonImpl(class LAMMPS *);
|
||||||
~PythonImpl();
|
~PythonImpl();
|
||||||
void command(int, char **);
|
void command(int, char **);
|
||||||
@ -33,6 +32,7 @@ class PythonImpl : protected Pointers, public PythonInterface {
|
|||||||
int execute_string(char *);
|
int execute_string(char *);
|
||||||
int execute_file(char *);
|
int execute_file(char *);
|
||||||
bool has_minimum_version(int major, int minor);
|
bool has_minimum_version(int major, int minor);
|
||||||
|
static void finalize();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void *pyMain;
|
void *pyMain;
|
||||||
|
|||||||
@ -33,17 +33,18 @@
|
|||||||
#include "group.h"
|
#include "group.h"
|
||||||
#include "info.h"
|
#include "info.h"
|
||||||
#include "input.h"
|
#include "input.h"
|
||||||
|
#include "lmppython.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "modify.h"
|
#include "modify.h"
|
||||||
#include "molecule.h"
|
#include "molecule.h"
|
||||||
#include "neigh_list.h"
|
#include "neigh_list.h"
|
||||||
#include "neighbor.h"
|
#include "neighbor.h"
|
||||||
#include "region.h"
|
|
||||||
#include "respa.h"
|
|
||||||
#include "output.h"
|
#include "output.h"
|
||||||
#if defined(LMP_PLUGIN)
|
#if defined(LMP_PLUGIN)
|
||||||
#include "plugin.h"
|
#include "plugin.h"
|
||||||
#endif
|
#endif
|
||||||
|
#include "region.h"
|
||||||
|
#include "respa.h"
|
||||||
#include "thermo.h"
|
#include "thermo.h"
|
||||||
#include "timer.h"
|
#include "timer.h"
|
||||||
#include "universe.h"
|
#include "universe.h"
|
||||||
@ -339,6 +340,8 @@ function no more MPI calls may be made.
|
|||||||
|
|
||||||
.. versionadded:: 18Sep2020
|
.. versionadded:: 18Sep2020
|
||||||
|
|
||||||
|
*See also*
|
||||||
|
:cpp:func:`lammps_kokkos_finalize`, :cpp:func:`lammps_python_finalize`
|
||||||
\endverbatim */
|
\endverbatim */
|
||||||
|
|
||||||
void lammps_mpi_finalize()
|
void lammps_mpi_finalize()
|
||||||
@ -369,6 +372,8 @@ After calling this function no Kokkos functionality may be used.
|
|||||||
|
|
||||||
.. versionadded:: 2Jul2021
|
.. versionadded:: 2Jul2021
|
||||||
|
|
||||||
|
*See also*
|
||||||
|
:cpp:func:`lammps_mpi_finalize`, :cpp:func:`lammps_python_finalize`
|
||||||
\endverbatim */
|
\endverbatim */
|
||||||
|
|
||||||
void lammps_kokkos_finalize()
|
void lammps_kokkos_finalize()
|
||||||
@ -376,6 +381,42 @@ void lammps_kokkos_finalize()
|
|||||||
KokkosLMP::finalize();
|
KokkosLMP::finalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/** Clear the embedded Python environment
|
||||||
|
*
|
||||||
|
\verbatim embed:rst
|
||||||
|
|
||||||
|
This function resets and clears an embedded Python environment
|
||||||
|
by calling the `Py_Finalize() function
|
||||||
|
<https://docs.python.org/3/c-api/init.html#c.Py_FinalizeEx>`_
|
||||||
|
of the embedded Python library, if enabled.
|
||||||
|
This call would free up all allocated resources and release
|
||||||
|
loaded shared objects.
|
||||||
|
|
||||||
|
However, this is **not** done when a LAMMPS instance is deleted because
|
||||||
|
a) LAMMPS may have been used through the Python module and thus
|
||||||
|
the Python interpreter is external and not embedded into LAMMPS
|
||||||
|
and therefore may not be reset by LAMMPS b) some Python modules
|
||||||
|
and extensions, most notably NumPy, are not compatible with being
|
||||||
|
initialized multiple times, which would happen if additional
|
||||||
|
LAMMPS instances using Python would be created *after*
|
||||||
|
after calling Py_Finalize().
|
||||||
|
|
||||||
|
This function can be called to explicitly clear the Python
|
||||||
|
environment in case it is safe to do so.
|
||||||
|
|
||||||
|
.. versionadded:: TBD
|
||||||
|
|
||||||
|
*See also*
|
||||||
|
:cpp:func:`lammps_mpi_finalize`, :cpp:func:`lammps_kokkos_finalize`
|
||||||
|
\endverbatim */
|
||||||
|
|
||||||
|
void lammps_python_finalize()
|
||||||
|
{
|
||||||
|
Python::finalize();
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
// Library functions to process commands
|
// Library functions to process commands
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
|
|||||||
@ -95,6 +95,7 @@ void lammps_close(void *handle);
|
|||||||
void lammps_mpi_init();
|
void lammps_mpi_init();
|
||||||
void lammps_mpi_finalize();
|
void lammps_mpi_finalize();
|
||||||
void lammps_kokkos_finalize();
|
void lammps_kokkos_finalize();
|
||||||
|
void lammps_python_finalize();
|
||||||
|
|
||||||
/* ----------------------------------------------------------------------
|
/* ----------------------------------------------------------------------
|
||||||
* Library functions to process commands
|
* Library functions to process commands
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
// clang-format off
|
|
||||||
/* ----------------------------------------------------------------------
|
/* ----------------------------------------------------------------------
|
||||||
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
|
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
|
||||||
https://www.lammps.org/, Sandia National Laboratories
|
https://www.lammps.org/, Sandia National Laboratories
|
||||||
@ -39,9 +38,7 @@ Python::~Python()
|
|||||||
|
|
||||||
/* ---------------------------------------------------------------------- */
|
/* ---------------------------------------------------------------------- */
|
||||||
|
|
||||||
PythonInterface::~PythonInterface()
|
PythonInterface::~PythonInterface() {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---------------------------------------------------------------------- */
|
/* ---------------------------------------------------------------------- */
|
||||||
|
|
||||||
@ -50,12 +47,13 @@ void Python::init()
|
|||||||
#if defined(LMP_PYTHON)
|
#if defined(LMP_PYTHON)
|
||||||
if (!impl) impl = new PythonImpl(lmp);
|
if (!impl) impl = new PythonImpl(lmp);
|
||||||
#else
|
#else
|
||||||
error->all(FLERR,"Python support missing! Compile with PYTHON package installed!");
|
error->all(FLERR, "Python support missing! Compile with PYTHON package installed!");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---------------------------------------------------------------------- */
|
/* ---------------------------------------------------------------------- */
|
||||||
bool Python::is_enabled() const {
|
bool Python::is_enabled() const
|
||||||
|
{
|
||||||
#if defined(LMP_PYTHON)
|
#if defined(LMP_PYTHON)
|
||||||
return true;
|
return true;
|
||||||
#else
|
#else
|
||||||
@ -97,7 +95,7 @@ int Python::variable_match(const char *name, const char *varname, int numeric)
|
|||||||
|
|
||||||
/* ------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
char * Python::long_string(int ifunc)
|
char *Python::long_string(int ifunc)
|
||||||
{
|
{
|
||||||
init();
|
init();
|
||||||
return impl->long_string(ifunc);
|
return impl->long_string(ifunc);
|
||||||
@ -126,3 +124,12 @@ bool Python::has_minimum_version(int major, int minor)
|
|||||||
init();
|
init();
|
||||||
return impl->has_minimum_version(major, minor);
|
return impl->has_minimum_version(major, minor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
void Python::finalize()
|
||||||
|
{
|
||||||
|
#if defined(LMP_PYTHON)
|
||||||
|
PythonImpl::finalize();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|||||||
@ -47,6 +47,7 @@ class Python : protected Pointers {
|
|||||||
|
|
||||||
bool is_enabled() const;
|
bool is_enabled() const;
|
||||||
void init();
|
void init();
|
||||||
|
static void finalize();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PythonInterface *impl;
|
PythonInterface *impl;
|
||||||
|
|||||||
@ -15,6 +15,8 @@
|
|||||||
|
|
||||||
#include "accelerator_kokkos.h"
|
#include "accelerator_kokkos.h"
|
||||||
#include "input.h"
|
#include "input.h"
|
||||||
|
#include "lmppython.h"
|
||||||
|
|
||||||
#if defined(LAMMPS_EXCEPTIONS)
|
#if defined(LAMMPS_EXCEPTIONS)
|
||||||
#include "exceptions.h"
|
#include "exceptions.h"
|
||||||
#endif
|
#endif
|
||||||
@ -79,15 +81,18 @@ int main(int argc, char **argv)
|
|||||||
delete lammps;
|
delete lammps;
|
||||||
} catch (LAMMPSAbortException &ae) {
|
} catch (LAMMPSAbortException &ae) {
|
||||||
KokkosLMP::finalize();
|
KokkosLMP::finalize();
|
||||||
|
Python::finalize();
|
||||||
MPI_Abort(ae.universe, 1);
|
MPI_Abort(ae.universe, 1);
|
||||||
} catch (LAMMPSException &e) {
|
} catch (LAMMPSException &e) {
|
||||||
KokkosLMP::finalize();
|
KokkosLMP::finalize();
|
||||||
|
Python::finalize();
|
||||||
MPI_Barrier(lammps_comm);
|
MPI_Barrier(lammps_comm);
|
||||||
MPI_Finalize();
|
MPI_Finalize();
|
||||||
exit(1);
|
exit(1);
|
||||||
} catch (fmt::format_error &fe) {
|
} catch (fmt::format_error &fe) {
|
||||||
fprintf(stderr, "fmt::format_error: %s\n", fe.what());
|
fprintf(stderr, "fmt::format_error: %s\n", fe.what());
|
||||||
KokkosLMP::finalize();
|
KokkosLMP::finalize();
|
||||||
|
Python::finalize();
|
||||||
MPI_Abort(MPI_COMM_WORLD, 1);
|
MPI_Abort(MPI_COMM_WORLD, 1);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
@ -99,11 +104,13 @@ int main(int argc, char **argv)
|
|||||||
} catch (fmt::format_error &fe) {
|
} catch (fmt::format_error &fe) {
|
||||||
fprintf(stderr, "fmt::format_error: %s\n", fe.what());
|
fprintf(stderr, "fmt::format_error: %s\n", fe.what());
|
||||||
KokkosLMP::finalize();
|
KokkosLMP::finalize();
|
||||||
|
Python::finalize();
|
||||||
MPI_Abort(MPI_COMM_WORLD, 1);
|
MPI_Abort(MPI_COMM_WORLD, 1);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
KokkosLMP::finalize();
|
KokkosLMP::finalize();
|
||||||
|
Python::finalize();
|
||||||
MPI_Barrier(lammps_comm);
|
MPI_Barrier(lammps_comm);
|
||||||
MPI_Finalize();
|
MPI_Finalize();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -49,7 +49,6 @@ using namespace MathConst;
|
|||||||
#define MAXLEVEL 4
|
#define MAXLEVEL 4
|
||||||
#define MAXLINE 256
|
#define MAXLINE 256
|
||||||
#define CHUNK 1024
|
#define CHUNK 1024
|
||||||
#define VALUELENGTH 64 // also in python.cpp
|
|
||||||
#define MAXFUNCARG 6
|
#define MAXFUNCARG 6
|
||||||
|
|
||||||
#define MYROUND(a) (( a-floor(a) ) >= .5) ? ceil(a) : floor(a)
|
#define MYROUND(a) (( a-floor(a) ) >= .5) ? ceil(a) : floor(a)
|
||||||
|
|||||||
@ -72,6 +72,7 @@ class Variable : protected Pointers {
|
|||||||
PYTHON,
|
PYTHON,
|
||||||
INTERNAL
|
INTERNAL
|
||||||
};
|
};
|
||||||
|
static constexpr int VALUELENGTH = 64;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int me;
|
int me;
|
||||||
|
|||||||
@ -64,6 +64,7 @@ extern void lammps_close(void *handle);
|
|||||||
extern void lammps_mpi_init();
|
extern void lammps_mpi_init();
|
||||||
extern void lammps_mpi_finalize();
|
extern void lammps_mpi_finalize();
|
||||||
extern void lammps_kokkos_finalize();
|
extern void lammps_kokkos_finalize();
|
||||||
|
extern void lammps_python_finalize();
|
||||||
extern void lammps_file(void *handle, const char *file);
|
extern void lammps_file(void *handle, const char *file);
|
||||||
extern char *lammps_command(void *handle, const char *cmd);
|
extern char *lammps_command(void *handle, const char *cmd);
|
||||||
extern void lammps_commands_list(void *handle, int ncmd, const char **cmds);
|
extern void lammps_commands_list(void *handle, int ncmd, const char **cmds);
|
||||||
@ -199,6 +200,7 @@ extern void lammps_close(void *handle);
|
|||||||
extern void lammps_mpi_init();
|
extern void lammps_mpi_init();
|
||||||
extern void lammps_mpi_finalize();
|
extern void lammps_mpi_finalize();
|
||||||
extern void lammps_kokkos_finalize();
|
extern void lammps_kokkos_finalize();
|
||||||
|
extern void lammps_python_finalize();
|
||||||
extern void lammps_file(void *handle, const char *file);
|
extern void lammps_file(void *handle, const char *file);
|
||||||
extern char *lammps_command(void *handle, const char *cmd);
|
extern char *lammps_command(void *handle, const char *cmd);
|
||||||
extern void lammps_commands_list(void *handle, int ncmd, const char **cmds);
|
extern void lammps_commands_list(void *handle, int ncmd, const char **cmds);
|
||||||
|
|||||||
@ -4,11 +4,6 @@
|
|||||||
# availability of the PYTHON package is tested for inside the tester.
|
# availability of the PYTHON package is tested for inside the tester.
|
||||||
|
|
||||||
set(TEST_INPUT_FOLDER ${CMAKE_CURRENT_SOURCE_DIR})
|
set(TEST_INPUT_FOLDER ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
add_executable(test_python_package test_python_package.cpp)
|
|
||||||
target_link_libraries(test_python_package PRIVATE lammps GTest::GMock GTest::GTest)
|
|
||||||
target_compile_definitions(test_python_package PRIVATE -DTEST_INPUT_FOLDER=${TEST_INPUT_FOLDER})
|
|
||||||
add_test(NAME PythonPackage COMMAND test_python_package WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
|
||||||
set_tests_properties(PythonPackage PROPERTIES ENVIRONMENT "LAMMPS_POTENTIALS=${LAMMPS_POTENTIALS_DIR};PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}:${LAMMPS_PYTHON_DIR}:$ENV{PYTHONPATH};PYTHONUNBUFFERED=1")
|
|
||||||
|
|
||||||
# we must have shared libraries enabled for testing the python module
|
# we must have shared libraries enabled for testing the python module
|
||||||
if(NOT BUILD_SHARED_LIBS)
|
if(NOT BUILD_SHARED_LIBS)
|
||||||
@ -22,9 +17,20 @@ if(CMAKE_VERSION VERSION_LESS 3.12)
|
|||||||
set(Python_EXECUTABLE ${PYTHON_EXECUTABLE})
|
set(Python_EXECUTABLE ${PYTHON_EXECUTABLE})
|
||||||
endif()
|
endif()
|
||||||
else()
|
else()
|
||||||
find_package(Python3 COMPONENTS Interpreter)
|
find_package(Python3 COMPONENTS Interpreter Development)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
add_executable(test_python_package test_python_package.cpp)
|
||||||
|
target_link_libraries(test_python_package PRIVATE lammps GTest::GMock GTest::GTest)
|
||||||
|
target_compile_definitions(test_python_package PRIVATE -DTEST_INPUT_FOLDER=${TEST_INPUT_FOLDER})
|
||||||
|
# this requires CMake 3.12. don't care to add backward compatibility for this.
|
||||||
|
if(Python3_Development_FOUND)
|
||||||
|
target_compile_definitions(test_python_package PRIVATE -DTEST_HAVE_PYTHON_DEVELOPMENT=1)
|
||||||
|
target_link_libraries(test_python_package PRIVATE Python::Python)
|
||||||
|
endif()
|
||||||
|
add_test(NAME PythonPackage COMMAND test_python_package WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
set_tests_properties(PythonPackage PROPERTIES ENVIRONMENT "LAMMPS_POTENTIALS=${LAMMPS_POTENTIALS_DIR};PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}:${LAMMPS_PYTHON_DIR}:$ENV{PYTHONPATH};PYTHONUNBUFFERED=1")
|
||||||
|
|
||||||
if(Python_EXECUTABLE)
|
if(Python_EXECUTABLE)
|
||||||
# prepare to augment the environment so that the LAMMPS python module and the shared library is found.
|
# prepare to augment the environment so that the LAMMPS python module and the shared library is found.
|
||||||
set(PYTHON_TEST_ENVIRONMENT PYTHONPATH=${LAMMPS_PYTHON_DIR}:$ENV{PYTHONPATH})
|
set(PYTHON_TEST_ENVIRONMENT PYTHONPATH=${LAMMPS_PYTHON_DIR}:$ENV{PYTHONPATH})
|
||||||
|
|||||||
@ -27,6 +27,10 @@
|
|||||||
#include "../testing/core.h"
|
#include "../testing/core.h"
|
||||||
#include "../testing/systems/melt.h"
|
#include "../testing/systems/melt.h"
|
||||||
|
|
||||||
|
#if defined(TEST_HAVE_PYTHON_DEVELOPMENT)
|
||||||
|
#include <Python.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
// location of '*.py' files required by tests
|
// location of '*.py' files required by tests
|
||||||
#define STRINGIFY(val) XSTR(val)
|
#define STRINGIFY(val) XSTR(val)
|
||||||
#define XSTR(val) #val
|
#define XSTR(val) #val
|
||||||
@ -88,6 +92,23 @@ TEST_F(PythonPackageTest, InvokeFunctionFromFile)
|
|||||||
ASSERT_THAT(output, HasSubstr("2.25\n"));
|
ASSERT_THAT(output, HasSubstr("2.25\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(TEST_HAVE_PYTHON_DEVELOPMENT)
|
||||||
|
TEST_F(PythonPackageTest, InvokeInitialized)
|
||||||
|
{
|
||||||
|
// execute python function from file
|
||||||
|
HIDE_OUTPUT([&] {
|
||||||
|
command("python printnum file ${input_dir}/func.py");
|
||||||
|
});
|
||||||
|
ASSERT_TRUE(Py_IsInitialized());
|
||||||
|
HIDE_OUTPUT([&] {
|
||||||
|
command("clear");
|
||||||
|
});
|
||||||
|
ASSERT_TRUE(Py_IsInitialized());
|
||||||
|
lammps_python_finalize();
|
||||||
|
ASSERT_FALSE(Py_IsInitialized());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
TEST_F(PythonPackageTest, InvokeFunctionPassInt)
|
TEST_F(PythonPackageTest, InvokeFunctionPassInt)
|
||||||
{
|
{
|
||||||
// execute python function, passing integer as argument
|
// execute python function, passing integer as argument
|
||||||
|
|||||||
Reference in New Issue
Block a user