resolve NOTES and add option to print return value to log with python invoke
This commit is contained in:
@ -28,7 +28,8 @@ Syntax
|
|||||||
|
|
||||||
one or more keywords with/without arguments must be appended
|
one or more keywords with/without arguments must be appended
|
||||||
keyword = *invoke* or *input* or *return* or *format* or *length* or *file* or *here* or *exists*
|
keyword = *invoke* or *input* or *return* or *format* or *length* or *file* or *here* or *exists*
|
||||||
*invoke* arg = none = invoke the previously-defined Python function
|
*invoke* arg = invoke the previously-defined Python function
|
||||||
|
logreturn = log return value of the invoked python function, if defined (optional)
|
||||||
*input* args = N i1 i2 ... iN
|
*input* args = N i1 i2 ... iN
|
||||||
N = # of inputs to function
|
N = # of inputs to function
|
||||||
i1,...,iN = value, SELF, or LAMMPS variable name
|
i1,...,iN = value, SELF, or LAMMPS variable name
|
||||||
@ -58,7 +59,7 @@ Examples
|
|||||||
.. code-block:: LAMMPS
|
.. code-block:: LAMMPS
|
||||||
|
|
||||||
python pForce input 2 v_x 20.0 return v_f format fff file force.py
|
python pForce input 2 v_x 20.0 return v_f format fff file force.py
|
||||||
python pForce invoke
|
python pForce invoke logreturn
|
||||||
|
|
||||||
python factorial input 1 myN return v_fac format ii here """
|
python factorial input 1 myN return v_fac format ii here """
|
||||||
def factorial(n):
|
def factorial(n):
|
||||||
@ -165,18 +166,19 @@ that will be registered with LAMMPS for future execution. The function
|
|||||||
may already be defined (see *exists* keyword) or must be defined using
|
may already be defined (see *exists* keyword) or must be defined using
|
||||||
the *file* or *here* keywords as explained below.
|
the *file* or *here* keywords as explained below.
|
||||||
|
|
||||||
If the *invoke* keyword is used, no other keywords can be used, and a
|
If the *invoke* keyword is used, only the optional *logreturn* keyword
|
||||||
previous *python* command must have registered the Python function
|
can be used. A previous *python* command must have registered the
|
||||||
referenced by this command. This invokes the Python function with the
|
Python function referenced by this command. The command invokes the
|
||||||
previously defined arguments and the return value is processed as
|
Python function with the previously defined arguments. A return value
|
||||||
explained below. You can invoke a registered function as many times
|
of the Python function will be ignored unless the Python function is
|
||||||
as you wish in your input script.
|
linked to a :doc:`python style variable <variable>` with the *return*
|
||||||
|
keyword. This return value can be logged to the screen and logfile by
|
||||||
NOTE for Richard: As indicated with a NOTE in python_impl.cpp, I don't
|
adding the *logreturn* keyword to the *invoke* command. In that case a
|
||||||
think there is any access to a value returned by invoking a Py
|
message with the name of the python command and the return value is
|
||||||
function in this way. If that is correct, I think this should be
|
printed. Return values of python functions are otherwise only
|
||||||
clarified in the doc page, with a better explanation of the utility of
|
accessible when the function is invoked indirectly by expanding a
|
||||||
using the *invoke* keyword.
|
:doc:`python style variable <variable>`. You can invoke a registered
|
||||||
|
function as many times as you wish in your input script.
|
||||||
|
|
||||||
The *input* keyword defines how many arguments *N* the Python function
|
The *input* keyword defines how many arguments *N* the Python function
|
||||||
expects. If it takes no arguments, then the *input* keyword should
|
expects. If it takes no arguments, then the *input* keyword should
|
||||||
@ -213,10 +215,9 @@ defined, this command creates an :doc:`internal-style variable
|
|||||||
|
|
||||||
An internal-style variable must be used when an equal-style,
|
An internal-style variable must be used when an equal-style,
|
||||||
vector-style, or atom-style variable triggers the invocation of the
|
vector-style, or atom-style variable triggers the invocation of the
|
||||||
Python function defined by this command, by including a Python
|
Python function defined by this command, by including a Python function
|
||||||
function wrapper with arguments in its formula. Each of the arguments
|
wrapper with arguments in its formula. Each of the arguments must be
|
||||||
must be specified as an internal-style variable via the *input*
|
specified as an internal-style variable via the *input* keyword.
|
||||||
keyword.
|
|
||||||
|
|
||||||
In brief, the syntax for a Python function wrapper in a variable
|
In brief, the syntax for a Python function wrapper in a variable
|
||||||
formula is py_varname(arg1,arg2,...argN), where "varname" is the name
|
formula is py_varname(arg1,arg2,...argN), where "varname" is the name
|
||||||
@ -246,6 +247,9 @@ value. The specified *varReturn* is of the form v_name, where "name"
|
|||||||
is the name of a python-style LAMMPS variable, defined by the
|
is the name of a python-style LAMMPS variable, defined by the
|
||||||
:doc:`variable <variable>` command. The Python function can return a
|
:doc:`variable <variable>` command. The Python function can return a
|
||||||
numeric or string value, as specified by the *format* keyword.
|
numeric or string value, as specified by the *format* keyword.
|
||||||
|
This return value is *only* accessible when expanding the python-style
|
||||||
|
variable. When the *invoke* keyword is used, the return value of
|
||||||
|
the python function is ignored.
|
||||||
|
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
|||||||
@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
#include "python_impl.h"
|
#include "python_impl.h"
|
||||||
|
|
||||||
|
#include "comm.h"
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
#include "input.h"
|
#include "input.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
@ -166,9 +167,9 @@ void PythonImpl::command(int narg, char **arg)
|
|||||||
{
|
{
|
||||||
if (narg < 2) utils::missing_cmd_args(FLERR, "python", error);
|
if (narg < 2) utils::missing_cmd_args(FLERR, "python", error);
|
||||||
|
|
||||||
// if invoke is only keyword, invoke the previously defined function
|
// if invoke keyword is used, invoke the previously defined function
|
||||||
|
|
||||||
if (narg == 2 && strcmp(arg[1], "invoke") == 0) {
|
if (strcmp(arg[1], "invoke") == 0) {
|
||||||
int ifunc = find(arg[0]);
|
int ifunc = find(arg[0]);
|
||||||
if (ifunc < 0)
|
if (ifunc < 0)
|
||||||
error->all(FLERR, Error::ARGZERO, "Python invoke of unknown function: {}", arg[0]);
|
error->all(FLERR, Error::ARGZERO, "Python invoke of unknown function: {}", arg[0]);
|
||||||
@ -183,17 +184,17 @@ void PythonImpl::command(int narg, char **arg)
|
|||||||
arg[0], pfuncs[ifunc].ovarname, pfuncs[ifunc].name);
|
arg[0], pfuncs[ifunc].ovarname, pfuncs[ifunc].name);
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE to Richard - if this function returns a value,
|
bool logreturn = false;
|
||||||
// I don't believe it will be accessible to the input script
|
if (narg == 3 && strcmp(arg[2], "logreturn") == 0) logreturn = true;
|
||||||
// b/c that only occurs if Variable::retreive() or Variable::compute_equal() is called
|
|
||||||
// so if a Python function with a return is invoked this way,
|
char *str = nullptr;
|
||||||
// it might be better to issue an error or warning ?
|
if (logreturn && pfuncs[ifunc].noutput) str = new char[Variable::VALUELENGTH];
|
||||||
// i.e. it only make sense to call a setup-style kind of Python function this way
|
|
||||||
// one with no return value
|
|
||||||
// it also means there is no need to call Variable::pythonstyle() here
|
|
||||||
// or even define the pythonstyle() method in Variable ?
|
|
||||||
|
|
||||||
invoke_function(ifunc, str, nullptr);
|
invoke_function(ifunc, str, nullptr);
|
||||||
|
if (logreturn && str && (comm->me == 0))
|
||||||
|
utils::logmesg(lmp, "Invoked python function {} returned {}\n", arg[0], str);
|
||||||
|
|
||||||
|
delete[] str;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -427,16 +428,14 @@ void PythonImpl::invoke_function(int ifunc, char *result, double *dvalue)
|
|||||||
if (pfuncs[ifunc].noutput) {
|
if (pfuncs[ifunc].noutput) {
|
||||||
int otype = pfuncs[ifunc].otype;
|
int otype = pfuncs[ifunc].otype;
|
||||||
if (otype == INT) {
|
if (otype == INT) {
|
||||||
if (dvalue)
|
if (dvalue) *dvalue = (double) PY_INT_AS_LONG(pValue);
|
||||||
*dvalue = (double) PY_INT_AS_LONG(pValue);
|
if (result) {
|
||||||
else {
|
|
||||||
auto value = fmt::format("{}", PY_INT_AS_LONG(pValue));
|
auto value = fmt::format("{}", PY_INT_AS_LONG(pValue));
|
||||||
strncpy(result, value.c_str(), Variable::VALUELENGTH - 1);
|
strncpy(result, value.c_str(), Variable::VALUELENGTH - 1);
|
||||||
}
|
}
|
||||||
} else if (otype == DOUBLE) {
|
} else if (otype == DOUBLE) {
|
||||||
if (dvalue)
|
if (dvalue) *dvalue = PyFloat_AsDouble(pValue);
|
||||||
*dvalue = PyFloat_AsDouble(pValue);
|
if (result) {
|
||||||
else {
|
|
||||||
auto value = fmt::format("{:.15g}", PyFloat_AsDouble(pValue));
|
auto value = fmt::format("{:.15g}", PyFloat_AsDouble(pValue));
|
||||||
strncpy(result, value.c_str(), Variable::VALUELENGTH - 1);
|
strncpy(result, value.c_str(), Variable::VALUELENGTH - 1);
|
||||||
}
|
}
|
||||||
@ -444,10 +443,10 @@ void PythonImpl::invoke_function(int ifunc, char *result, double *dvalue)
|
|||||||
const char *pystr = PyUnicode_AsUTF8(pValue);
|
const char *pystr = PyUnicode_AsUTF8(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
|
if (result) strncpy(result, pystr, Variable::VALUELENGTH - 1);
|
||||||
strncpy(result, pystr, Variable::VALUELENGTH - 1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Py_CLEAR(pValue);
|
Py_CLEAR(pValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user