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
|
||||
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
|
||||
N = # of inputs to function
|
||||
i1,...,iN = value, SELF, or LAMMPS variable name
|
||||
@ -58,7 +59,7 @@ Examples
|
||||
.. code-block:: LAMMPS
|
||||
|
||||
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 """
|
||||
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
|
||||
the *file* or *here* keywords as explained below.
|
||||
|
||||
If the *invoke* keyword is used, no other keywords can be used, and a
|
||||
previous *python* command must have registered the Python function
|
||||
referenced by this command. This invokes the Python function with the
|
||||
previously defined arguments and the return value is processed as
|
||||
explained below. You can invoke a registered function as many times
|
||||
as you wish in your input script.
|
||||
|
||||
NOTE for Richard: As indicated with a NOTE in python_impl.cpp, I don't
|
||||
think there is any access to a value returned by invoking a Py
|
||||
function in this way. If that is correct, I think this should be
|
||||
clarified in the doc page, with a better explanation of the utility of
|
||||
using the *invoke* keyword.
|
||||
If the *invoke* keyword is used, only the optional *logreturn* keyword
|
||||
can be used. A previous *python* command must have registered the
|
||||
Python function referenced by this command. The command invokes the
|
||||
Python function with the previously defined arguments. A return value
|
||||
of the Python function will be ignored unless the Python function is
|
||||
linked to a :doc:`python style variable <variable>` with the *return*
|
||||
keyword. This return value can be logged to the screen and logfile by
|
||||
adding the *logreturn* keyword to the *invoke* command. In that case a
|
||||
message with the name of the python command and the return value is
|
||||
printed. Return values of python functions are otherwise only
|
||||
accessible when the function is invoked indirectly by expanding a
|
||||
: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
|
||||
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,
|
||||
vector-style, or atom-style variable triggers the invocation of the
|
||||
Python function defined by this command, by including a Python
|
||||
function wrapper with arguments in its formula. Each of the arguments
|
||||
must be specified as an internal-style variable via the *input*
|
||||
keyword.
|
||||
Python function defined by this command, by including a Python function
|
||||
wrapper with arguments in its formula. Each of the arguments must be
|
||||
specified as an internal-style variable via the *input* keyword.
|
||||
|
||||
In brief, the syntax for a Python function wrapper in a variable
|
||||
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
|
||||
:doc:`variable <variable>` command. The Python function can return a
|
||||
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 "comm.h"
|
||||
#include "error.h"
|
||||
#include "input.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 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]);
|
||||
if (ifunc < 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);
|
||||
}
|
||||
|
||||
// NOTE to Richard - if this function returns a value,
|
||||
// I don't believe it will be accessible to the input script
|
||||
// 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,
|
||||
// it might be better to issue an error or warning ?
|
||||
// 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 ?
|
||||
bool logreturn = false;
|
||||
if (narg == 3 && strcmp(arg[2], "logreturn") == 0) logreturn = true;
|
||||
|
||||
char *str = nullptr;
|
||||
if (logreturn && pfuncs[ifunc].noutput) str = new char[Variable::VALUELENGTH];
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -427,16 +428,14 @@ void PythonImpl::invoke_function(int ifunc, char *result, double *dvalue)
|
||||
if (pfuncs[ifunc].noutput) {
|
||||
int otype = pfuncs[ifunc].otype;
|
||||
if (otype == INT) {
|
||||
if (dvalue)
|
||||
*dvalue = (double) PY_INT_AS_LONG(pValue);
|
||||
else {
|
||||
if (dvalue) *dvalue = (double) PY_INT_AS_LONG(pValue);
|
||||
if (result) {
|
||||
auto value = fmt::format("{}", PY_INT_AS_LONG(pValue));
|
||||
strncpy(result, value.c_str(), Variable::VALUELENGTH - 1);
|
||||
}
|
||||
} else if (otype == DOUBLE) {
|
||||
if (dvalue)
|
||||
*dvalue = PyFloat_AsDouble(pValue);
|
||||
else {
|
||||
if (dvalue) *dvalue = PyFloat_AsDouble(pValue);
|
||||
if (result) {
|
||||
auto value = fmt::format("{:.15g}", PyFloat_AsDouble(pValue));
|
||||
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);
|
||||
if (pfuncs[ifunc].longstr)
|
||||
strncpy(pfuncs[ifunc].longstr, pystr, pfuncs[ifunc].length_longstr);
|
||||
else
|
||||
strncpy(result, pystr, Variable::VALUELENGTH - 1);
|
||||
if (result) strncpy(result, pystr, Variable::VALUELENGTH - 1);
|
||||
}
|
||||
}
|
||||
|
||||
Py_CLEAR(pValue);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user