Merge branch 'develop' into plugin-add-run-min-style

This commit is contained in:
Axel Kohlmeyer
2025-06-11 03:24:04 -04:00
28 changed files with 1815 additions and 484 deletions

View File

@ -5,18 +5,28 @@ LAMMPS has several commands which can be used to invoke Python
code directly from an input script: code directly from an input script:
* :doc:`python <python>` * :doc:`python <python>`
* :doc:`variable python <variable>` * :doc:`python-style variables <variable>`
* :doc:`equal-style and atom-style variables with formulas containing Python function wrappers <variable>`
* :doc:`fix python/invoke <fix_python_invoke>` * :doc:`fix python/invoke <fix_python_invoke>`
* :doc:`pair_style python <pair_python>` * :doc:`pair_style python <pair_python>`
The :doc:`python <python>` command which can be used to define and The :doc:`python <python>` command can be used to define and execute a
execute a Python function that you write the code for. The Python Python function that you write the code for. The Python function can
function can also be assigned to a LAMMPS python-style variable via also be assigned to a LAMMPS python-style variable via the
the :doc:`variable <variable>` command. Each time the variable is :doc:`variable <variable>` command. Each time the variable is
evaluated, either in the LAMMPS input script itself, or by another evaluated, either in the LAMMPS input script itself, or by another
LAMMPS command that uses the variable, this will trigger the Python LAMMPS command that uses the variable, this will trigger the Python
function to be invoked. function to be invoked.
The Python function can also be referenced in the formula used to
define an :doc:`equal-style or atom-style variable <variable>`, using
the syntax for a :doc:`Python function wrapper <variable>`. This make
it easy to pass LAMMPS-related arguments to the Python function, as
well as to invoke it whenever the equal- or atom-style variable is
evaluated. For an atom-style variable it means the Python function
can be invoked once per atom, using per-atom properties as arguments
to the function.
The Python code for the function can be included directly in the input The Python code for the function can be included directly in the input
script or in an auxiliary file. The function can have arguments which script or in an auxiliary file. The function can have arguments which
are mapped to LAMMPS variables (also defined in the input script) and are mapped to LAMMPS variables (also defined in the input script) and

View File

@ -53,15 +53,17 @@ The value *eng* is the interaction energy for the angle.
The value *v_name* can be used together with the *set* keyword to The value *v_name* can be used together with the *set* keyword to
compute a user-specified function of the angle theta. The *name* compute a user-specified function of the angle theta. The *name*
specified for the *v_name* value is the name of an :doc:`equal-style variable <variable>` which should evaluate a formula based on a specified for the *v_name* value is the name of an :doc:`equal-style
variable <variable>` which should evaluate a formula based on a
variable which will store the angle theta. This other variable must variable which will store the angle theta. This other variable must
be an :doc:`internal-style variable <variable>` defined in the input be an :doc:`internal-style variable <variable>` specified by the *set*
script; its initial numeric value can be anything. It must be an keyword. It is an internal-style variable, because this command
internal-style variable, because this command resets its value resets its value directly. The internal-style variable does not need
directly. The *set* keyword is used to identify the name of this to be defined in the input script (though it can be); if it is not
other variable associated with theta. defined, then the *set* option creates an :doc:`internal-style
variable <variable>` with the specified name.
Note that the value of theta for each angle which stored in the Note that the value of theta for each angle which is stored in the
internal variable is in radians, not degrees. internal variable is in radians, not degrees.
As an example, these commands can be added to the bench/in.rhodo As an example, these commands can be added to the bench/in.rhodo
@ -70,7 +72,6 @@ system and output the statistics in various ways:
.. code-block:: LAMMPS .. code-block:: LAMMPS
variable t internal 0.0
variable cos equal cos(v_t) variable cos equal cos(v_t)
variable cossq equal cos(v_t)*cos(v_t) variable cossq equal cos(v_t)*cos(v_t)

View File

@ -130,13 +130,15 @@ moving apart.
The value *v_name* can be used together with the *set* keyword to The value *v_name* can be used together with the *set* keyword to
compute a user-specified function of the bond distance. The *name* compute a user-specified function of the bond distance. The *name*
specified for the *v_name* value is the name of an :doc:`equal-style variable <variable>` which should evaluate a formula based on a specified for the *v_name* value is the name of an :doc:`equal-style
variable which will store the bond distance. This other variable must variable <variable>` which should evaluate a formula based on a
be an :doc:`internal-style variable <variable>` defined in the input variable which stores the bond distance. This other variable must be
script; its initial numeric value can be anything. It must be an the :doc:`internal-style variable <variable>` specified by the *set*
internal-style variable, because this command resets its value keyword. It is an internal-style variable, because this command
directly. The *set* keyword is used to identify the name of this resets its value directly. The internal-style variable does not need
other variable associated with theta. to be defined in the input script (though it can be); if it is not
defined, then the *set* option creates an :doc:`internal-style
variable <variable>` with the specified name.
As an example, these commands can be added to the bench/in.rhodo As an example, these commands can be added to the bench/in.rhodo
script to compute the length\ :math:`^2` of every bond in the system and script to compute the length\ :math:`^2` of every bond in the system and
@ -144,7 +146,6 @@ output the statistics in various ways:
.. code-block:: LAMMPS .. code-block:: LAMMPS
variable d internal 0.0
variable dsq equal v_d*v_d variable dsq equal v_d*v_d
compute 1 all property/local batom1 batom2 btype compute 1 all property/local batom1 batom2 btype

View File

@ -45,30 +45,31 @@ interactions. The number of datums generated, aggregated across all
processors, equals the number of dihedral angles in the system, modified processors, equals the number of dihedral angles in the system, modified
by the group parameter as explained below. by the group parameter as explained below.
The value *phi* (:math:`\phi`) is the dihedral angle, as defined in the diagram The value *phi* (:math:`\phi`) is the dihedral angle, as defined in
on the :doc:`dihedral_style <dihedral_style>` doc page. the diagram on the :doc:`dihedral_style <dihedral_style>` doc page.
The value *v_name* can be used together with the *set* keyword to compute a The value *v_name* can be used together with the *set* keyword to
user-specified function of the dihedral angle :math:`\phi`. The *name* compute a user-specified function of the dihedral angle :math:`\phi`.
specified for the *v_name* value is the name of an The *name* specified for the *v_name* value is the name of an
:doc:`equal-style variable <variable>` which should evaluate a formula based on :doc:`equal-style variable <variable>` which should evaluate a formula
a variable which will store the angle :math:`\phi`. This other variable must based on a variable which will store the angle :math:`\phi`. This
be an :doc:`internal-style variable <variable>` defined in the input other variable must be an :doc:`internal-style variable <variable>`
script; its initial numeric value can be anything. It must be an specified by the *set* keyword. It is an internal-style variable,
internal-style variable, because this command resets its value because this command resets its value directly. The internal-style
directly. The *set* keyword is used to identify the name of this variable does not need to be defined in the input script (though it
other variable associated with :math:`\phi`. can be); if it is not defined, then the *set* option creates an
:doc:`internal-style variable <variable>` with the specified name.
Note that the value of :math:`\phi` for each angle which stored in the internal Note that the value of :math:`\phi` for each angle which stored in the
variable is in radians, not degrees. internal variable is in radians, not degrees.
As an example, these commands can be added to the bench/in.rhodo As an example, these commands can be added to the bench/in.rhodo
script to compute the :math:`\cos\phi` and :math:`\cos^2\phi` of every dihedral script to compute the :math:`\cos\phi` and :math:`\cos^2\phi` of every
angle in the system and output the statistics in various ways: dihedral angle in the system and output the statistics in various
ways:
.. code-block:: LAMMPS .. code-block:: LAMMPS
variable p internal 0.0
variable cos equal cos(v_p) variable cos equal cos(v_p)
variable cossq equal cos(v_p)*cos(v_p) variable cossq equal cos(v_p)*cos(v_p)
@ -100,10 +101,10 @@ no consistent ordering of the entries within the local vector or array
from one timestep to the next. The only consistency that is from one timestep to the next. The only consistency that is
guaranteed is that the ordering on a particular timestep will be the guaranteed is that the ordering on a particular timestep will be the
same for local vectors or arrays generated by other compute commands. same for local vectors or arrays generated by other compute commands.
For example, dihedral output from the For example, dihedral output from the :doc:`compute property/local
:doc:`compute property/local <compute_property_local>` command can be combined <compute_property_local>` command can be combined with data from this
with data from this command and output by the :doc:`dump local <dump>` command and output by the :doc:`dump local <dump>` command in a
command in a consistent way. consistent way.
Here is an example of how to do this: Here is an example of how to do this:

View File

@ -416,24 +416,23 @@ atom, based on its coordinates. They apply to all styles except
*single*. The *name* specified for the *var* keyword is the name of *single*. The *name* specified for the *var* keyword is the name of
an :doc:`equal-style variable <variable>` that should evaluate to a an :doc:`equal-style variable <variable>` that should evaluate to a
zero or non-zero value based on one or two or three variables that zero or non-zero value based on one or two or three variables that
will store the *x*, *y*, or *z* coordinates of an atom (one variable per will store the *x*, *y*, or *z* coordinates of an atom (one variable
coordinate). If used, these other variables must be per coordinate). If used, these other variables must be specified by
:doc:`internal-style variables <variable>` defined in the input the *set* keyword. They are internal-style variable, because this
script; their initial numeric value can be anything. They must be command resets their values directly. The internal-style variables do
internal-style variables, because this command resets their values not need to be defined in the input script (though they can be); if
directly. The *set* keyword is used to identify the names of these one (or more) is not defined, then the *set* option creates an
other variables, one variable for the *x*-coordinate of a created atom, :doc:`internal-style variable <variable>` with the specified name.
one for *y*, and one for *z*.
.. figure:: img/sinusoid.jpg .. figure:: img/sinusoid.jpg
:figwidth: 50% :figwidth: 50%
:align: right :align: right
:target: _images/sinusoid.jpg :target: _images/sinusoid.jpg
When an atom is created, its :math:`(x,y,z)` coordinates become the values for When an atom is about to be created, its :math:`(x,y,z)` coordinates
any *set* variable that is defined. The *var* variable is then become the values for any *set* variable that is defined. The *var*
evaluated. If the returned value is 0.0, the atom is not created. If variable is then evaluated. If the returned value is 0.0, the atom is
it is non-zero, the atom is created. not created. If it is non-zero, the atom is created.
As an example, these commands can be used in a 2d simulation, to As an example, these commands can be used in a 2d simulation, to
create a sinusoidal surface. Note that the surface is "rough" due to create a sinusoidal surface. Note that the surface is "rough" due to
@ -456,8 +455,6 @@ converts lattice spacings to distance.
region box block 0 $x 0 $y -0.5 0.5 region box block 0 $x 0 $y -0.5 0.5
create_box 1 box create_box 1 box
variable xx internal 0.0
variable yy internal 0.0
variable v equal "(0.2*v_y*ylat * cos(v_xx/xlat * 2.0*PI*4.0/v_x) + 0.5*v_y*ylat - v_yy) > 0.0" variable v equal "(0.2*v_y*ylat * cos(v_xx/xlat * 2.0*PI*4.0/v_x) + 0.5*v_y*ylat - v_yy) > 0.0"
create_atoms 1 box var v set x xx set y yy create_atoms 1 box var v set x xx set y yy
write_dump all atom sinusoid.lammpstrj write_dump all atom sinusoid.lammpstrj

View File

@ -98,52 +98,53 @@ the following dynamic equation:
\frac{dc}{dt} = -\alpha (K_p e + K_i \int_0^t e \, dt + K_d \frac{de}{dt} ) \frac{dc}{dt} = -\alpha (K_p e + K_i \int_0^t e \, dt + K_d \frac{de}{dt} )
where *c* is the continuous time analog of the control variable, where *c* is the continuous time analog of the control variable, *e*
*e* =\ *pvar*\ -\ *setpoint* is the error in the process variable, and =\ *pvar*\ -\ *setpoint* is the error in the process variable, and
:math:`\alpha`, :math:`K_p`, :math:`K_i` , and :math:`K_d` are constants :math:`\alpha`, :math:`K_p`, :math:`K_i` , and :math:`K_d` are
set by the corresponding constants set by the corresponding keywords described above. The
keywords described above. The discretized version of this equation is: discretized version of this equation is:
.. math:: .. math::
c_n = c_{n-1} -\alpha \left( K_p \tau e_n + K_i \tau^2 \sum_{i=1}^n e_i + K_d (e_n - e_{n-1}) \right) c_n = c_{n-1} -\alpha \left( K_p \tau e_n + K_i \tau^2 \sum_{i=1}^n e_i + K_d (e_n - e_{n-1}) \right)
where :math:`\tau = \mathtt{Nevery} \cdot \mathtt{timestep}` is the time where :math:`\tau = \mathtt{Nevery} \cdot \mathtt{timestep}` is the
interval between updates, time interval between updates, and the subscripted variables indicate
and the subscripted variables indicate the values of *c* and *e* at the values of *c* and *e* at successive updates.
successive updates.
From the first equation, it is clear that if the three gain values From the first equation, it is clear that if the three gain values
:math:`K_p`, :math:`K_i`, :math:`K_d` are dimensionless constants, :math:`K_p`, :math:`K_i`, :math:`K_d` are dimensionless constants,
then :math:`\alpha` must have then :math:`\alpha` must have units of [unit *cvar*\ ]/[unit *pvar*\
units of [unit *cvar*\ ]/[unit *pvar*\ ]/[unit time] e.g. [ eV/K/ps ]/[unit time] e.g. [ eV/K/ps ]. The advantage of this unit scheme is
]. The advantage of this unit scheme is that the value of the that the value of the constants should be invariant under a change of
constants should be invariant under a change of either the MD timestep either the MD timestep size or the value of *Nevery*\ . Similarly, if
size or the value of *Nevery*\ . Similarly, if the LAMMPS :doc:`unit style <units>` is changed, it should only be necessary to change the LAMMPS :doc:`unit style <units>` is changed, it should only be
the value of :math:`\alpha` to reflect this, while leaving :math:`K_p`, necessary to change the value of :math:`\alpha` to reflect this, while
:math:`K_i`, and :math:`K_d` unaltered. leaving :math:`K_p`, :math:`K_i`, and :math:`K_d` unaltered.
When choosing the values of the four constants, it is best to first When choosing the values of the four constants, it is best to first
pick a value and sign for :math:`\alpha` that is consistent with the pick a value and sign for :math:`\alpha` that is consistent with the
magnitudes and signs of *pvar* and *cvar*\ . The magnitude of :math:`K_p` magnitudes and signs of *pvar* and *cvar*\ . The magnitude of
should then be tested over a large positive range keeping :math:`K_i = K_d =0`. :math:`K_p` should then be tested over a large positive range keeping
A good value for :math:`K_p` will produce a fast response in *pvar*, :math:`K_i = K_d =0`. A good value for :math:`K_p` will produce a
without overshooting the *setpoint*\ . For many applications, proportional fast response in *pvar*, without overshooting the *setpoint*\ . For
feedback is sufficient, and so :math:`K_i = K_d =0` can be used. In cases many applications, proportional feedback is sufficient, and so
where there is a substantial lag time in the response of *pvar* to a change :math:`K_i = K_d =0` can be used. In cases where there is a
in *cvar*, this can be counteracted by increasing :math:`K_d`. In situations substantial lag time in the response of *pvar* to a change in *cvar*,
this can be counteracted by increasing :math:`K_d`. In situations
where *pvar* plateaus without reaching *setpoint*, this can be where *pvar* plateaus without reaching *setpoint*, this can be
counteracted by increasing :math:`K_i`. In the language of Charles Dickens, counteracted by increasing :math:`K_i`. In the language of Charles
:math:`K_p` represents the error of the present, :math:`K_i` the error of Dickens, :math:`K_p` represents the error of the present, :math:`K_i`
the past, and :math:`K_d` the error yet to come. the error of the past, and :math:`K_d` the error yet to come.
Because this fix updates *cvar*, but does not initialize its value, Because this fix updates *cvar*, but does not initialize its value,
the initial value :math:`c_0` is that assigned by the user in the input script via the initial value :math:`c_0` is that assigned by the user in the
the :doc:`internal-style variable <variable>` command. This value is input script via the :doc:`internal-style variable <variable>`
used (by every other LAMMPS command that uses the variable) until this command. This value is used (by every other LAMMPS command that uses
fix performs its first update of *cvar* after *Nevery* timesteps. On the variable) until this fix performs its first update of *cvar* after
the first update, the value of the derivative term is set to zero, *Nevery* timesteps. On the first update, the value of the derivative
because the value of :math:`e_{n-1}` is not yet defined. term is set to zero, because the value of :math:`e_{n-1}` is not yet
defined.
---------- ----------
@ -154,21 +155,23 @@ must produce a global quantity, not a per-atom or local quantity.
If *pvar* begins with "c\_", a compute ID must follow which has been If *pvar* begins with "c\_", a compute ID must follow which has been
previously defined in the input script and which generates a global previously defined in the input script and which generates a global
scalar or vector. See the individual :doc:`compute <compute>` doc page scalar or vector. See the individual :doc:`compute <compute>` doc
for details. If no bracketed integer is appended, the scalar page for details. If no bracketed integer is appended, the scalar
calculated by the compute is used. If a bracketed integer is calculated by the compute is used. If a bracketed integer is
appended, the Ith value of the vector calculated by the compute is appended, the Ith value of the vector calculated by the compute is
used. Users can also write code for their own compute styles and :doc:`add them to LAMMPS <Modify>`. used. Users can also write code for their own compute styles and
:doc:`add them to LAMMPS <Modify>`.
If *pvar* begins with "f\_", a fix ID must follow which has been If *pvar* begins with "f\_", a fix ID must follow which has been
previously defined in the input script and which generates a global previously defined in the input script and which generates a global
scalar or vector. See the individual :doc:`fix <fix>` page for scalar or vector. See the individual :doc:`fix <fix>` page for
details. Note that some fixes only produce their values on certain details. Note that some fixes only produce their values on certain
timesteps, which must be compatible with when fix controller timesteps, which must be compatible with when fix controller
references the values, or else an error results. If no bracketed integer references the values, or else an error results. If no bracketed
is appended, the scalar calculated by the fix is used. If a bracketed integer is appended, the scalar calculated by the fix is used. If a
integer is appended, the Ith value of the vector calculated by the fix bracketed integer is appended, the Ith value of the vector calculated
is used. Users can also write code for their own fix style and :doc:`add them to LAMMPS <Modify>`. by the fix is used. Users can also write code for their own fix style
and :doc:`add them to LAMMPS <Modify>`.
If *pvar* begins with "v\_", a variable name must follow which has been If *pvar* begins with "v\_", a variable name must follow which has been
previously defined in the input script. Only equal-style variables previously defined in the input script. Only equal-style variables
@ -182,19 +185,21 @@ variable.
The target value *setpoint* for the process variable must be a numeric The target value *setpoint* for the process variable must be a numeric
value, in whatever units *pvar* is defined for. value, in whatever units *pvar* is defined for.
The control variable *cvar* must be the name of an :doc:`internal-style variable <variable>` previously defined in the input script. Note The control variable *cvar* must be the name of an
that it is not specified with a "v\_" prefix, just the name of the :doc:`internal-style variable <variable>` previously defined in the
variable. It must be an internal-style variable, because this fix input script. Note that it is not specified with a "v\_" prefix, just
updates its value directly. Note that other commands can use an the name of the variable. It must be an internal-style variable,
equal-style versus internal-style variable interchangeably. because this fix updates its value directly. Note that other commands
can use an equal-style versus internal-style variable interchangeably.
---------- ----------
Restart, fix_modify, output, run start/stop, minimize info Restart, fix_modify, output, run start/stop, minimize info
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" """""""""""""""""""""""""""""""""""""""""""""""""""""""""""
Currently, no information about this fix is written to :doc:`binary restart files <restart>`. None of the :doc:`fix_modify <fix_modify>` options Currently, no information about this fix is written to :doc:`binary
are relevant to this fix. restart files <restart>`. None of the :doc:`fix_modify <fix_modify>`
options are relevant to this fix.
This fix produces a global vector with 3 values which can be accessed This fix produces a global vector with 3 values which can be accessed
by various :doc:`output commands <Howto_output>`. The values can be by various :doc:`output commands <Howto_output>`. The values can be
@ -211,7 +216,8 @@ variable is in. The vector values calculated by this fix are
"extensive". "extensive".
No parameter of this fix can be used with the *start/stop* keywords of No parameter of this fix can be used with the *start/stop* keywords of
the :doc:`run <run>` command. This fix is not invoked during :doc:`energy minimization <minimize>`. the :doc:`run <run>` command. This fix is not invoked during
:doc:`energy minimization <minimize>`.
Restrictions Restrictions
"""""""""""" """"""""""""

View File

@ -225,22 +225,25 @@ rotated configuration of the molecule.
.. versionadded:: 21Nov2023 .. versionadded:: 21Nov2023
The *var* and *set* keywords can be used together to provide a criterion The *var* and *set* keywords can be used together to provide a
for accepting or rejecting the addition of an individual atom, based on its criterion for accepting or rejecting the addition of an individual
coordinates. The *name* specified for the *var* keyword is the name of an atom, based on its coordinates. The *name* specified for the *var*
:doc:`equal-style variable <variable>` that should evaluate to a zero or keyword is the name of an :doc:`equal-style variable <variable>` that
non-zero value based on one or two or three variables that will store the should evaluate to a zero or non-zero value based on one or two or
*x*, *y*, or *z* coordinates of an atom (one variable per coordinate). If three variables that will store the *x*, *y*, or *z* coordinates of an
used, these other variables must be :doc:`internal-style variables atom (one variable per coordinate). If used, these other variables
<variable>` defined in the input script; their initial numeric value can be must be :doc:`internal-style variables <variable>` specified by the
anything. They must be internal-style variables, because this command *set* keyword. They must be internal-style variables, because this
resets their values directly. The *set* keyword is used to identify the command resets their values directly. The internal-style variables do
names of these other variables, one variable for the *x*-coordinate of a not need to be defined in the input script (though they can be); if
created atom, one for *y*, and one for *z*. When an atom is created, its one (or more) is not defined, then the *set* option creates an
:math:`(x,y,z)` coordinates become the values for any *set* variable that :doc:`internal-style variable <variable>` with the specified name.
is defined. The *var* variable is then evaluated. If the returned value
is 0.0, the atom is not created. If it is non-zero, the atom is created. When an atom is about to be created, its :math:`(x,y,z)` coordinates
For an example of how to use these keywords, see the become the values for any *set* variable that is defined. The *var*
variable is then evaluated. If the returned value is 0.0, the atom is
not created. If it is non-zero, the atom is created. For an example
of how to use the set/var keywords in a similar context, see the
:doc:`create_atoms <create_atoms>` command. :doc:`create_atoms <create_atoms>` command.
The *rate* option moves the insertion volume in the z direction (3d) The *rate* option moves the insertion volume in the z direction (3d)
@ -304,12 +307,13 @@ units of distance or velocity.
Restart, fix_modify, output, run start/stop, minimize info Restart, fix_modify, output, run start/stop, minimize info
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" """""""""""""""""""""""""""""""""""""""""""""""""""""""""""
This fix writes the state of the deposition to :doc:`binary restart files <restart>`. This includes information about how many This fix writes the state of the deposition to :doc:`binary restart
particles have been deposited, the random number generator seed, the files <restart>`. This includes information about how many particles
next timestep for deposition, etc. See the have been deposited, the random number generator seed, the next
:doc:`read_restart <read_restart>` command for info on how to re-specify timestep for deposition, etc. See the :doc:`read_restart
a fix in an input script that reads a restart file, so that the <read_restart>` command for info on how to re-specify a fix in an
operation of the fix continues in an uninterrupted fashion. input script that reads a restart file, so that the operation of the
fix continues in an uninterrupted fashion.
.. note:: .. note::

View File

@ -10,43 +10,46 @@ Syntax
python mode keyword args ... python mode keyword args ...
* mode = *source* or name of Python function * mode = *source* or *name* of Python function
if mode is *source*: if mode is *source*:
.. parsed-literal:: .. parsed-literal::
keyword = *here* or name of a *Python file* keyword = *here* or name of a *Python file*
*here* arg = inline *here* arg = one or more lines of Python code
inline = one or more lines of Python code which defines func
must be a single argument, typically enclosed between triple quotes must be a single argument, typically enclosed between triple quotes
the in-lined Python code will be executed immediately
*Python file* = name of a file with Python code which will be executed immediately *Python file* = name of a file with Python code which will be executed immediately
* if *mode* is the name of a Python function, one or more keywords with/without arguments must be appended * if *mode* is *name* of a Python function:
.. parsed-literal:: .. parsed-literal::
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
value = integer number, floating point number, or string value = integer number, floating point number, or string
SELF = reference to LAMMPS itself which can be accessed by Python function SELF = reference to LAMMPS itself which can then be accessed by Python function
variable = v_name, where name = name of LAMMPS variable, e.g. v_abc variable = v_name, where name = name of a LAMMPS variable, e.g. v_abc
internal variable = iv_name, where name = name of a LAMMPS internal-style variable, e.g. iv_xyz
*return* arg = varReturn *return* arg = varReturn
varReturn = v_name = LAMMPS variable name which the return value of the Python function will be assigned to varReturn = v_name = LAMMPS variable name which the return value of the Python function will be assigned to
*format* arg = fstring with M characters *format* arg = fstring with M characters
M = N if no return value, where N = # of inputs M = N if no return value, where N = # of inputs
M = N+1 if there is a return value M = N+1 if there is a return value
fstring = each character (i,f,s,p) corresponds in order to an input or return value fstring = each character (i,f,s,p) corresponds (in order) to an input or return value
'i' = integer, 'f' = floating point, 's' = string, 'p' = SELF 'i' = integer, 'f' = floating point, 's' = string, 'p' = SELF
*length* arg = Nlen *length* arg = Nlen
Nlen = max length of string returned from Python function Nlen = max length of string returned from Python function
*file* arg = filename *file* arg = filename
filename = file of Python code, which defines func filename = file of Python code, which defines the Python function
*here* arg = inline *here* arg = inline
inline = one or more lines of Python code which defines func inline = one or more lines of Python code which defines the Python function
must be a single argument, typically enclosed between triple quotes must be a single argument, typically enclosed between triple quotes
*exists* arg = none = Python code has been loaded by previous python command *exists* arg = none = Python code has been loaded by previous python command
@ -56,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):
@ -87,37 +90,42 @@ Examples
Description Description
""""""""""" """""""""""
The *python* command allows interfacing LAMMPS with an embedded Python The *python* command interfaces LAMMPS with an embedded Python
interpreter and enables either executing arbitrary python code in that interpreter and enables executing arbitrary python code in that
interpreter, registering a Python function for future execution (as a interpreter. This can be done immediately, by using *mode* = *source*.
python style variable, from a fix interfaced with python, or for direct Or execution can be deferred, by registering a Python function for later
invocation), or invoking such a previously registered function. execution, by using *mode* = *name* of a Python function.
Arguments, including LAMMPS variables, can be passed to the function Later execution can be triggered in one of two ways. One is to use the
from the LAMMPS input script and a value returned by the Python function python command again with its *invoke* keyword. The other is to trigger
assigned to a LAMMPS variable. The Python code for the function can be included the evaluation of a python-style, equal-style, vector-style, or
directly in the input script or in a separate Python file. The function atom-style variable. A python-style variable invokes its associated
can be standard Python code or it can make "callbacks" to LAMMPS through Python function; its return value becomes the value of the python-style
its library interface to query or set internal values within LAMMPS. variable. Equal-, vector-, and atom-style variables can use a Python
This is a powerful mechanism for performing complex operations in a function wrapper in their formulas which encodes the Python function
LAMMPS input script that are not possible with the simple input script name, and specifies arguments to pass to the function.
and variable syntax which LAMMPS defines. Thus your input script can
operate more like a true programming language. Note that python-style, equal-style, vector-style, and atom-style
variables can be used in many different ways within LAMMPS. They can be
evaluated directly in an input script, effectively replacing the
variable with its value. Or they can be passed to various commands as
arguments, so that the variable is evaluated multiple times during a
simulation run. See the :doc:`variable <variable>` command doc page for
more details on variable styles which enable Python function evaluation.
The Python code for the function can be included directly in the input
script or in a separate Python file. The function can be standard
Python code or it can make "callbacks" to LAMMPS through its library
interface to query or set internal values within LAMMPS. This is a
powerful mechanism for performing complex operations in a LAMMPS input
script that are not possible with the simple input script and variable
syntax which LAMMPS defines. Thus your input script can operate more
like a true programming language.
Use of this command requires building LAMMPS with the PYTHON package Use of this command requires building LAMMPS with the PYTHON package
which links to the Python library so that the Python interpreter is which links to the Python library so that the Python interpreter is
embedded in LAMMPS. More details about this process are given below. embedded in LAMMPS. More details about this process are given below.
There are two ways to invoke a Python function once it has been
registered. One is using the *invoke* keyword. The other is to assign
the function to a :doc:`python-style variable <variable>` defined in
your input script. Whenever the variable is evaluated, it will execute
the Python function to assign a value to the variable. Note that
variables can be evaluated in many different ways within LAMMPS. They
can be substituted with their result directly in an input script, or
they can be passed to various commands as arguments, so that the
variable is evaluated during a simulation run.
A broader overview of how Python can be used with LAMMPS is given in the A broader overview of how Python can be used with LAMMPS is given in the
:doc:`Use Python with LAMMPS <Python_head>` section of the :doc:`Use Python with LAMMPS <Python_head>` section of the
documentation. There also is an ``examples/python`` directory which documentation. There also is an ``examples/python`` directory which
@ -125,69 +133,128 @@ illustrates use of the python command.
---------- ----------
The first argument of the *python* command is either the *source* The first argument is the *mode* setting, which is either *source* or
keyword or the name of a Python function. This defines the mode the *name* of a Python function.
of the python command.
.. versionchanged:: 22Dec2022 .. versionchanged:: 22Dec2022
If the *source* keyword is used, it is followed by either a file name or If *source* is used, it is followed by either the *here* keyword or a
the *here* keyword. No other keywords can be used. The *here* keyword file name containing Python code. The *here* keyword is followed by a
is followed by a string with python commands, either on a single line string containing python commands, either on a single line enclosed in
enclosed in quotes, or as multiple lines enclosed in triple quotes. quotes, or as multiple lines enclosed in triple quotes. In either
These Python commands will be passed to the python interpreter and case, the in-line code or file contents are passed to the python
executed immediately without registering a Python function for future interpreter and executed immediately. The code will be loaded into
execution. The code will be loaded into and run in the "main" module of and run in the "main" module of the Python interpreter. This allows
the Python interpreter. This allows running arbitrary Python code at running arbitrary Python code at any time while processing the SPARTA
any time while processing the LAMMPS input file. This can be used to input file. This can be used to pre-load Python modules, initialize
pre-load Python modules, initialize global variables, define functions global variables, define functions or classes, or perform operations
or classes, or perform operations using the python programming language. using the Python programming language. The Python code will be
The Python code will be executed in parallel on all MPI processes. No executed in parallel on all the MPI processes being used to run
arguments can be passed. LAMMPS. Note that no arguments can be passed to the executed Python
code.
If the *mode* setting is the *name* of a Python function, then it will
be registered with SPARTA for future execution (or already be defined,
see the *exists* keyword). One or more keywords must follow the
*mode* function name. One of the keywords must be *invoke*, *file*,
*here*, or *exists*.
In all other cases, the first argument is the name of a Python function In all other cases, the first argument is the name of a Python function
that will be registered with LAMMPS for future execution. The function 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 the function as many times as you wish of the Python function will be ignored unless the Python function is
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
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 The *input* keyword defines how many arguments *N* the Python function
expects. If it takes no arguments, then the *input* keyword should not expects. If it takes no arguments, then the *input* keyword should
be used. Each argument can be specified directly as a value, e.g. '6' not be used. Each argument can be specified directly as a value,
or '3.14159' or 'abc' (a string of characters). The type of each e.g. '6' or '3.14159' or 'abc' (a string of characters). The type of
argument is specified by the *format* keyword as explained below, so each argument is specified by the *format* keyword as explained below,
that Python will know how to interpret the value. If the word SELF is so that Python will know how to interpret the value. If the word SELF
used for an argument it has a special meaning. A pointer is passed to is used for an argument it has a special meaning. A pointer is passed
the Python function which it can convert into a reference to LAMMPS to the Python function which it can convert into a reference to LAMMPS
itself using the :doc:`LAMMPS Python module <Python_module>`. This itself using the :doc:`LAMMPS Python module <Python_module>`. This
enables the function to call back to LAMMPS through its library enables the function to call back to LAMMPS through its library
interface as explained below. This allows the Python function to query interface as explained below. This allows the Python function to
or set values internal to LAMMPS which can affect the subsequent query or set values internal to LAMMPS which can affect the subsequent
execution of the input script. A LAMMPS variable can also be used as an execution of the input script.
argument, specified as v_name, where "name" is the name of the variable.
Any style of LAMMPS variable returning a scalar or a string can be used, A LAMMPS variable can also be used as an *input* argument, specified
as defined by the :doc:`variable <variable>` command. The *format* as v_name, where "name" is the name of the variable defined in the
keyword must be used to set the type of data that is passed to Python. input script. Any style of LAMMPS variable returning a scalar or a
string can be used, as defined by the :doc:`variable <variable>`
command. The style of variable must be consistent with the *format*
keyword specification for the type of data that is passed to Python.
Each time the Python function is invoked, the LAMMPS variable is Each time the Python function is invoked, the LAMMPS variable is
evaluated and its value is passed to the Python function. evaluated and its value is passed as an argument to the Python
function. Note that a python-style variable can be used as an
argument, which means that the a Python function can use arguments
which invoke other Python functions.
A LAMMPS internal-style variable can also be used as an *input*
argument, specified as iv_name, where "name" is the name of the
internal-style variable. The internal-style variable does not have to
be defined in the input script (though it can be); if it is not
defined, this command creates an :doc:`internal-style variable
<variable>` with the specified name.
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.
In brief, the syntax for a Python function wrapper in a variable formula
is ``py_varname(arg1,arg2,...argN)``, where "varname" is the name of a
python-style variable associated with a Python function defined by this
command. One or more arguments to the function wrapper can themselves
be sub-formulas which the variable command will evaluate and pass as
arguments to the Python function. This is done by assigning the numeric
result for each argument to an internal-style variable; thus the *input*
keyword must specify the arguments as internal-style variables and their
format (see below) as "f" for floating point. This is because LAMMPS
variable formulas are calculated with floating point arithmetic (any
integer values are converted to floating point). Note that the Python
function can also have additional inputs, also specified by the *input*
keyword, which are NOT arguments in the Python function wrapper. See
the example below for the ``mixedargs`` Python function.
See the :doc:`variable <variable>` command doc page for full details
on formula syntax including for Python function wrappers. Examples
using Python function wrappers are shown below. Note that as
explained above with python-style variables, Python function wrappers
can be nested; a sub-formula for an argument can contain its own
Python function wrapper which invokes another Python function.
The *return* keyword is only needed if the Python function returns a The *return* keyword is only needed if the Python function returns a
value. The specified *varReturn* must be of the form v_name, where value. The specified *varReturn* is of the form v_name, where "name"
"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.
As explained on the :doc:`variable <variable>` doc page, the definition ----------
of a python-style variable associates a Python function name with the
variable. This must match the *Python function name* first argument of As explained on the :doc:`variable <variable>` doc page, the
the *python* command. For example these two commands would be definition of a python-style variable associates a Python function
consistent: name with the variable. Its specification must match the *mode*
argument of the *python* command for the Python function name. For
example these two commands would be consistent:
.. code-block:: LAMMPS .. code-block:: LAMMPS
@ -196,43 +263,43 @@ consistent:
The two commands can appear in either order in the input script so The two commands can appear in either order in the input script so
long as both are specified before the Python function is invoked for long as both are specified before the Python function is invoked for
the first time. Afterwards, the variable 'foo' is associated with the first time.
the Python function 'myMultiply'.
The *format* keyword must be used if the *input* or *return* keywords The *format* keyword must be used if the *input* or *return* keywords
are used. It defines an *fstring* with M characters, where M = sum of are used. It defines an *fstring* with M characters, where M = sum of
number of inputs and outputs. The order of characters corresponds to number of inputs and outputs. The order of characters corresponds to
the N inputs, followed by the return value (if it exists). Each the N inputs, followed by the return value (if it exists). Each
character must be one of the following: "i" for integer, "f" for character must be one of the following: "i" for integer, "f" for
floating point, "s" for string, or "p" for SELF. Each character defines floating point, "s" for string, or "p" for SELF. Each character
the type of the corresponding input or output value of the Python defines the type of the corresponding input or output value of the
function and affects the type conversion that is performed internally as Python function and affects the type conversion that is performed
data is passed back and forth between LAMMPS and Python. Note that it internally as data is passed back and forth between LAMMPS and Python.
is permissible to use a :doc:`python-style variable <variable>` in a Note that it is permissible to use a :doc:`python-style variable
LAMMPS command that allows for an equal-style variable as an argument, <variable>` in a LAMMPS command that allows for an equal-style
but only if the output of the Python function is flagged as a numeric variable as an argument, but only if the output of the Python function
value ("i" or "f") via the *format* keyword. is flagged as a numeric value ("i" or "f") via the *format* keyword.
If the *return* keyword is used and the *format* keyword specifies the If the *return* keyword is used and the *format* keyword specifies the
output as a string, then the default maximum length of that string is output as a string, then the default maximum length of that string is
63 characters (64-1 for the string terminator). If you want to return 63 characters (64-1 for the string terminator). If you want to return
a longer string, the *length* keyword can be specified with its *Nlen* a longer string, the *length* keyword can be specified with its *Nlen*
value set to a larger number (the code allocates space for Nlen+1 to value set to a larger number. LAMMPS will then allocate Nlen+1 space
include the string terminator). If the Python function generates a to include the string terminator. If the Python function generates a
string longer than the default 63 or the specified *Nlen*, it will be string longer than the default 63 or the specified *Nlen*, it will be
truncated. truncated.
---------- ----------
Either the *file*, *here*, or *exists* keyword must be used, but only As noted above, either the *invoke*, *file*, *here*, or *exists*
one of them. These keywords specify what Python code to load into the keyword must be used, but only one of them. These keywords specify
Python interpreter. The *file* keyword gives the name of a file what Python code to load into the Python interpreter. The *file*
containing Python code, which should end with a ".py" suffix. The code keyword gives the name of a file containing Python code, which should
will be immediately loaded into and run in the "main" module of the end with a ".py" suffix. The code will be immediately loaded into and
Python interpreter. The Python code will be executed in parallel on all run in the "main" module of the Python interpreter. The Python code
MPI processes. Note that Python code which contains a function will be executed in parallel on all MPI processes. Note that Python
definition does not "execute" the function when it is run; it simply code which contains a function definition does not "execute" the
defines the function so that it can be invoked later. function when it is run; it simply defines the function so that it can
be invoked later.
The *here* keyword does the same thing, except that the Python code The *here* keyword does the same thing, except that the Python code
follows as a single argument to the *here* keyword. This can be done follows as a single argument to the *here* keyword. This can be done
@ -243,15 +310,18 @@ proper indentation, blank lines, and comments, as desired. See the
how triple quotes can be used as part of input script syntax. how triple quotes can be used as part of input script syntax.
The *exists* keyword takes no argument. It means that Python code The *exists* keyword takes no argument. It means that Python code
containing the required Python function with the given name has already containing the required Python function with the given name has
been executed, for example by a *python source* command or in the same already been executed, for example by a *python source* command or in
file that was used previously with the *file* keyword. the same file that was used previously with the *file* keyword. This
allows use of a single file of Python code which contains multiple
functions, any of which can be used in the same (or different) input
scripts (see below).
Note that the Python code that is loaded and run must contain a function Note that the Python code that is loaded and run by the *file* or
with the specified function name. To operate properly when later *here* keyword must contain a function with the specified function
invoked, the function code must match the *input* and *return* and name. To operate properly when later invoked, the function code must
*format* keywords specified by the python command. Otherwise Python match the *input* and *return* and *format* keywords specified by the
will generate an error. python command. Otherwise Python will generate an error.
---------- ----------
@ -308,13 +378,13 @@ previous value is simply returned, without re-computing it. The
"global" statement inside the Python function allows it to overwrite the "global" statement inside the Python function allows it to overwrite the
global variables from within the local context of the function. global variables from within the local context of the function.
Note that if you load Python code multiple times (via multiple python Also note that if you load Python code multiple times (via multiple
commands), you can overwrite previously loaded variables and functions python commands), you can overwrite previously loaded variables and
if you are not careful. E.g. if the code above were loaded twice, the functions if you are not careful. E.g. if the code above were loaded
global variables would be re-initialized, which might not be what you twice, the global variables would be re-initialized, which might not
want. Likewise, if a function with the same name exists in two chunks be what you want. Likewise, if a function with the same name exists
of Python code you load, the function loaded second will override the in two chunks of Python code you load, the function loaded second will
function loaded first. override the function loaded first.
It's important to realize that if you are running LAMMPS in parallel, It's important to realize that if you are running LAMMPS in parallel,
each MPI task will load the Python interpreter and execute a local each MPI task will load the Python interpreter and execute a local
@ -325,15 +395,16 @@ This implies three important things.
First, if you put a print or other statement creating output to the First, if you put a print or other statement creating output to the
screen in your Python function, you will see P copies of the output, screen in your Python function, you will see P copies of the output,
when running on P processors. If the prints occur at (nearly) the same when running on P processors. If the prints occur at (nearly) the same
time, the P copies of the output may be mixed together. When loading time, the P copies of the output may be mixed together.
the LAMMPS Python module into the embedded Python interpreter, it is
possible to pass the pointer to the current LAMMPS class instance and It is possible to avoid this issue, by passing the pointer to the
via the Python interface to the LAMMPS library interface, it is possible current LAMMPS class instance to the Python function via the {input}
to determine the MPI rank of the current process and thus adapt the SELF argument described above. The Python function can then use the
Python code so that output will only appear on MPI rank 0. The Python interface to the LAMMPS library interface, and determine the
following LAMMPS input demonstrates how this could be done. The text MPI rank of the current process. The Python code can then ensure
'Hello, LAMMPS!' should be printed only once, even when running LAMMPS output will only appear on MPI rank 0. The following LAMMPS input
in parallel. demonstrates how this could be done. The text 'Hello, LAMPS!' should
be printed only once, even when running LAMMPS in parallel.
.. code-block:: LAMMPS .. code-block:: LAMMPS
@ -348,13 +419,13 @@ in parallel.
python python_hello invoke python python_hello invoke
If your Python code loads Python modules that are not pre-loaded by the Second, if your Python code loads Python modules that are not
Python library, then it will load the module from disk. This may be a pre-loaded by the Python library, then it will load the module from
bottleneck if 1000s of processors try to load a module at the same time. disk. This may be a bottleneck if 1000s of processors try to load a
On some large supercomputers, loading of modules from disk by Python may module at the same time. On some large supercomputers, loading of
be disabled. In this case you would need to pre-build a Python library modules from disk by Python may be disabled. In this case you would
that has the required modules pre-loaded and link LAMMPS with that need to pre-build a Python library that has the required modules
library. pre-loaded and link LAMMPS with that library.
Third, if your Python code calls back to LAMMPS (discussed in the Third, if your Python code calls back to LAMMPS (discussed in the
next section) and causes LAMMPS to perform an MPI operation requires next section) and causes LAMMPS to perform an MPI operation requires
@ -365,10 +436,10 @@ LAMMPS. Otherwise the code may hang.
---------- ----------
Your Python function can "call back" to LAMMPS through its As mentioned above, a Python function can "call back" to LAMMPS
library interface, if you use the SELF input to pass Python through its library interface, if the SELF input is used to pass
a pointer to LAMMPS. The mechanism for doing this in your Python a pointer to LAMMPS. The mechanism for doing this is as
Python function is as follows: follows:
.. code-block:: python .. code-block:: python
@ -416,7 +487,7 @@ which loads and runs the following function from ``examples/python/funcs.py``:
lmp.set_variable("cut",cut) # set a variable in LAMMPS 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 ${cut}") # LAMMPS command
#lmp.command("pair_style lj/cut %d" % cut) # LAMMPS command option #lmp.command("pair_style lj/cut %d" % cut) # alternate form of LAMMPS command
lmp.command("pair_coeff * * 1.0 1.0") # ditto lmp.command("pair_coeff * * 1.0 1.0") # ditto
lmp.command("run 10") # ditto lmp.command("run 10") # ditto
@ -449,9 +520,9 @@ is a useful way for a Python function to return multiple values to
LAMMPS, more than the single value that can be passed back via a LAMMPS, more than the single value that can be passed back via a
return statement. This cutoff value in the "cut" variable is then return statement. This cutoff value in the "cut" variable is then
substituted (by LAMMPS) in the pair_style command that is executed substituted (by LAMMPS) in the pair_style command that is executed
next. Alternatively, the "LAMMPS command option" line could be used next. Alternatively, the "alternate form of LAMMPS command" line
in place of the 2 preceding lines, to have Python insert the value could be used in place of the 2 preceding lines, to have Python insert
into the LAMMPS command string. the value into the LAMMPS command string.
.. note:: .. note::
@ -463,20 +534,128 @@ into the LAMMPS command string.
file() functions, so long as the command would work if it were file() functions, so long as the command would work if it were
executed in the LAMMPS input script directly at the same point. executed in the LAMMPS input script directly at the same point.
However, a Python function can also be invoked during a run, whenever
an associated LAMMPS variable it is assigned to is evaluated. If the
variable is an input argument to another LAMMPS command (e.g. :doc:`fix setforce <fix_setforce>`), then the Python function will be invoked
inside the class for that command, in one of its methods that is
invoked in the middle of a timestep. You cannot execute arbitrary
input script commands from the Python function (again, via the
command() or file() functions) at that point in the run and expect it
to work. Other library functions such as those that invoke computes
or other variables may have hidden side effects as well. In these
cases, LAMMPS has no simple way to check that something illogical is
being attempted.
The same applies to Python functions called during a simulation run at ----------
each time step using :doc:`fix python/invoke <fix_python_invoke>`.
A Python function can also be invoked during a run, whenever
an associated python-style variable it is assigned to is evaluated.
If the variable is an input argument to another LAMMPS command
(e.g. :doc:`fix setforce <fix_setforce>`), then the Python function
will be invoked inside the class for that command, possibly in one of
its methods that is invoked in the middle of a timestep. You cannot
execute arbitrary input script commands from the Python function
(again, via the command() or file() functions) at that point in the
run and expect it to work. Other library functions such as those that
invoke computes or other variables may have hidden side effects as
well. In these cases, LAMMPS has no simple way to check that
something illogical is being attempted.
The same constraints apply to Python functions called during a
simulation run at each time step using the :doc:`fix python/invoke
<fix_python_invoke>` command.
----------
A Python function can also be invoked within the formula for an
equal-style, vector-style, or atom-style variable. This means the
Python function will be invoked whenever the variable is invoked. In
the case of a vector-style variable, the Python function can be invoked
once per element of the global vector. In the case of an atom-style
variable, the Python function can be invoked once per atom.
Here are three simple examples using equal-, vector-, and atom-style
variables to trigger execution of a Python function:
.. code-block:: LAMMPS
variable foo python truncate
python truncate return v_foo input 1 iv_arg format fi here """
def truncate(x):
return int(x)
"""
variable ptrunc equal py_foo(press)
print "TRUNCATED pressure = ${ptrunc}"
The Python ``truncate`` function simply converts a floating-point value
to an integer value. When the LAMMPS print command evaluates the
equal-style ``ptrunc`` variable, the current thermodynamic pressure is
passed to the Python function. The truncated value is output to the
screen and logfile by the print command. Note that the *input*
keyword for the *python* command, specifies an internal-style variable
named "arg" as iv_arg which is required to invoke the Python function
from a Python function wrapper.
The last 2 lines can be replaced by these to define a vector-style
variable which invokes the same Python ``truncate`` function:
.. code-block:: LAMMPS
compute ke all temp
variable ke vector c_ke
variable ketrunc vector py_foo(v_ke)
thermo_style custom step temp epair v_ketrunc[*6]
The vector-style variable ``ketrunc`` invokes the Python ``truncate``
function on each of the 6 components of the global kinetic energy tensor
calculated by the :doc:`compute ke <compute_ke>` command. The 6
truncated values will be printed with thermo output to the screen and
log file.
Or the last 2 lines of the equal-style variable example can be replaced
by these to define atom-style variables which invoke the same Python
``truncate`` function:
.. code-block:: LAMMPS
variable xtrunc atom py_foo(x)
variable ytrunc atom py_foo(y)
variable ztrunc atom py_foo(z)
dump 1 all custom 100 tmp.dump id x y z v_xtrunc v_ytrunc v_ztrunc
When the dump command invokes the 3 atom-style variables, their
arguments x,y,z to the Python function wrapper are the current per-atom
coordinates of each atom. The Python ``truncate`` function is thus
invoked 3 times for each atom, and the truncated coordinate values for
each atom are written to the dump file.
Note that when using a Python function wrapper in a variable, arguments
can be passed to the Python function either from the variable formula or
by *input* keyword to the :doc:`python command <python>`. For example,
consider these (made up) commands:
.. code-block:: LAMMPS
variable foo python mixedargs
python mixedargs return v_foo input 6 7.5 v_myValue iv_arg1 iv_argy iv_argz v_flag &
format fffffsf here """
def mixedargs(a,b,x,y,z,flag):
...
return result
"""
variable flag string optionABC
variable myValue equal "2.0*temp*c_pe"
compute pe all pe
compute peatom all pe/atom
variable field atom py_foo(x+3.0,sqrt(y),(z-zlo)*c_peatom)
They define a Python ``mixedargs`` function with 6 arguments. Three of
them are internal-style variables, which the variable formula calculates
as numeric values for each atom and passes to the function. In this
example, these arguments are themselves small formulas containing the
x,y,z coordinates of each atom as well as a per-atom compute (c_peratom)
and thermodynamic keyword (zlo).
The other three arguments ``(7.5,v_myValue,v_flag)`` are defined by the
*python* command. The first and last are constant values ("7.5" and the
``optionABC`` string). The second argument (``myValue``) is the result
of an equal-style variable formula which accesses the system temperature
and potential energy.
The "result" returned by each invocation of the Python ``mixedargs``
function becomes the per-atom value in the atom-style "field" variable,
which could be output to a dump file or used elsewhere in the input
script.
---------- ----------
@ -563,27 +742,30 @@ If you use Python code which calls back to LAMMPS, via the SELF input
argument explained above, there is an extra step required when building argument explained above, there is an extra step required when building
LAMMPS. LAMMPS must also be built as a shared library and your Python LAMMPS. LAMMPS must also be built as a shared library and your Python
function must be able to load the :doc:`"lammps" Python module function must be able to load the :doc:`"lammps" Python module
<Python_module>` that wraps the LAMMPS library interface. These are the <Python_module>` that wraps the LAMMPS library interface.
same steps required to use Python by itself to wrap LAMMPS. Details on
these steps are explained on the :doc:`Python <Python_head>` doc page. These are the same steps required to use Python by itself to wrap
Note that it is important that the stand-alone LAMMPS executable and the LAMMPS. Details on these steps are explained on the :doc:`Python
LAMMPS shared library be consistent (built from the same source code <Python_head>` doc page. Note that it is important that the
files) in order for this to work. If the two have been built at stand-alone LAMMPS executable and the LAMMPS shared library be
different times using different source files, problems may occur. consistent (built from the same source code files) in order for this
to work. If the two have been built at different times using
different source files, problems may occur.
Another limitation of calling back to Python from the LAMMPS module Another limitation of calling back to Python from the LAMMPS module
using the *python* command in a LAMMPS input is that both, the Python using the *python* command in a LAMMPS input is that both, the Python
interpreter and LAMMPS, must be linked to the same Python runtime as a interpreter and LAMMPS, must be linked to the same Python runtime as a
shared library. If the Python interpreter is linked to Python shared library. If the Python interpreter is linked to Python
statically (which seems to happen with Conda) then loading the shared statically (which seems to happen with Conda) then loading the shared
LAMMPS library will create a second python "main" module that hides the LAMMPS library will create a second python "main" module that hides
one from the Python interpreter and all previous defined function and the one from the Python interpreter and all previous defined function
global variables will become invisible. and global variables will become invisible.
Related commands Related commands
"""""""""""""""" """"""""""""""""
:doc:`shell <shell>`, :doc:`variable <variable>`, :doc:`fix python/invoke <fix_python_invoke>` :doc:`shell <shell>`, :doc:`variable <variable>`,
:doc:`fix python/invoke <fix_python_invoke>`
Default Default
""""""" """""""

View File

@ -45,7 +45,8 @@ Syntax
*universe* args = one or more strings *universe* args = one or more strings
*world* args = one string for each partition of processors *world* args = one string for each partition of processors
*equal* or *vector* or *atom* args = one formula containing numbers, thermo keywords, math operations, built-in functions, atom values and vectors, compute/fix/variable references *equal* or *vector* or *atom* args = one formula containing numbers, thermo keywords,
math operations, built-in functions, atom values and vectors, compute/fix/variable references
numbers = 0.0, 100, -5.4, 2.8e-4, etc numbers = 0.0, 100, -5.4, 2.8e-4, etc
constants = PI, version, on, off, true, false, yes, no constants = PI, version, on, off, true, false, yes, no
thermo keywords = vol, ke, press, etc from :doc:`thermo_style <thermo_style>` thermo keywords = vol, ke, press, etc from :doc:`thermo_style <thermo_style>`
@ -67,8 +68,12 @@ Syntax
bound(group,dir,region), gyration(group,region), ke(group,reigon), bound(group,dir,region), gyration(group,region), ke(group,reigon),
angmom(group,dim,region), torque(group,dim,region), angmom(group,dim,region), torque(group,dim,region),
inertia(group,dimdim,region), omega(group,dim,region) inertia(group,dimdim,region), omega(group,dim,region)
special functions = sum(x), min(x), max(x), ave(x), trap(x), slope(x), sort(x), rsort(x), gmask(x), rmask(x), grmask(x,y), next(x), is_file(name), is_os(name), extract_setting(name), label2type(kind,label), is_typelabel(kind,label), is_timeout() special functions = sum(x), min(x), max(x), ave(x), trap(x), slope(x), sort(x), rsort(x), \ gmask(x), rmask(x), grmask(x,y), next(x), is_file(name), is_os(name),
feature functions = is_available(category,feature), is_active(category,feature), is_defined(category,id) extract_setting(name), label2type(kind,label),
is_typelabel(kind,label), is_timeout()
feature functions = is_available(category,feature), is_active(category,feature),
is_defined(category,id)
python function wrapper = py_varname(x,y,z,...)
atom value = id[i], mass[i], type[i], mol[i], x[i], y[i], z[i], vx[i], vy[i], vz[i], fx[i], fy[i], fz[i], q[i] atom value = id[i], mass[i], type[i], mol[i], x[i], y[i], z[i], vx[i], vy[i], vz[i], fx[i], fy[i], fz[i], q[i]
atom vector = id, mass, type, mol, radius, q, x, y, z, vx, vy, vz, fx, fy, fz atom vector = id, mass, type, mol, radius, q, x, y, z, vx, vy, vz, fx, fy, fz
custom atom property = i_name, d_name, i_name[i], d_name[i], i2_name[i], d2_name[i], i2_name[i][j], d2_name[i][j] custom atom property = i_name, d_name, i_name[i], d_name[i], i2_name[i], d2_name[i], i2_name[i][j], d2_name[i][j]
@ -127,18 +132,21 @@ command), or used as input to an averaging fix (see the :doc:`fix
ave/time <fix_ave_time>` command). Variables of style *vector* store ave/time <fix_ave_time>` command). Variables of style *vector* store
a formula which produces a vector of such values which can be used as a formula which produces a vector of such values which can be used as
input to various averaging fixes, or elements of which can be part of input to various averaging fixes, or elements of which can be part of
thermodynamic output. Variables of style *atom* store a formula which thermodynamic output.
when evaluated produces one numeric value per atom which can be output
to a dump file (see the :doc:`dump custom <dump>` command) or used as Variables of style *atom* store a formula which when evaluated
input to an averaging fix (see the :doc:`fix ave/chunk produces one numeric value per atom which can be output to a dump file
<fix_ave_chunk>` and :doc:`fix ave/atom <fix_ave_atom>` commands). (see the :doc:`dump custom <dump>` command) or used as input to an
Variables of style *atomfile* can be used anywhere in an input script averaging fix (see the :doc:`fix ave/chunk <fix_ave_chunk>` and
that atom-style variables are used; they get their per-atom values :doc:`fix ave/atom <fix_ave_atom>` commands). Variables of style
from a file rather than from a formula. Variables of style *python* *atomfile* can be used anywhere in an input script that atom-style
can be hooked to Python functions using code you provide, so that the variables are used; they get their per-atom values from a file rather
variable gets its value from the evaluation of the Python code. than from a formula.
Variables of style *internal* are used by a few commands which set
their value directly. Variables of style *python* can be hooked to Python functions using
Python code you provide, so that the variable gets its value from the
evaluation of the Python code. Variables of style *internal* are used
by a few commands which set their value directly.
.. note:: .. note::
@ -166,15 +174,16 @@ simulation.
.. note:: .. note::
When an input script line is encountered that defines a variable When an input script line is encountered that defines a variable of
of style *equal* or *vector* or *atom* or *python* that contains a style *equal* or *vector* or *atom* or *python* that contains a
formula or Python code, the formula is NOT immediately evaluated. It formula or links to Python code, the formula or Python code is NOT
will be evaluated every time when the variable is **used** instead. If immediately evaluated. Instead, it is evaluated each time the
you simply want to evaluate a formula in place you can use as variable is **used**. If you simply want to evaluate a formula in
so-called. See the section below about "Immediate Evaluation of place you can use a so-called immediate variable. as described in
Variables" for more details on the topic. This is also true of a the preceding note. Or see the section below about "Immediate
*format* style variable since it evaluates another variable when it is Evaluation of Variables" for more details on the topic. This is
invoked. also true of a *format* style variable since it evaluates another
variable when it is invoked.
Variables of style *equal* and *vector* and *atom* can be used as Variables of style *equal* and *vector* and *atom* can be used as
inputs to various other commands which evaluate their formulas as inputs to various other commands which evaluate their formulas as
@ -183,12 +192,12 @@ this context, variables of style *timer* or *internal* or *python* can
be used in place of an equal-style variable, with the following two be used in place of an equal-style variable, with the following two
caveats. caveats.
First, internal-style variables can be used except by commands that First, internal-style variables require their values be set by code
set the value stored by the internal variable. When the LAMMPS elsewhere in LAMMPS. When a LAMMPS input script or command evaluates
command evaluates the internal-style variable, it will use the value an internal-style variable, it must have a current value set
set (internally) by another command. Second, python-style variables (internally) via that mechanism. Second, python-style variables can
can be used so long as the associated Python function, as defined by be used so long as the associated Python function, as defined by the
the :doc:`python <python>` command, returns a numeric value. When the :doc:`python <python>` command, returns a numeric value. When the
LAMMPS command evaluates the python-style variable, the Python LAMMPS command evaluates the python-style variable, the Python
function will be executed. function will be executed.
@ -388,13 +397,24 @@ using the :doc:`command-line switch -var <Run_options>`.
For the *internal* style a numeric value is provided. This value will For the *internal* style a numeric value is provided. This value will
be assigned to the variable until a LAMMPS command sets it to a new be assigned to the variable until a LAMMPS command sets it to a new
value. There are currently only two LAMMPS commands that require value.
*internal* variables as inputs, because they reset them:
:doc:`create_atoms <create_atoms>` and :doc:`fix controller Note however, that most commands which use internal-style variables do
<fix_controller>`. As mentioned above, an internal-style variable can not require them to be defined in the input script. They create one or
be used in place of an equal-style variable anywhere else in an input more internal-style variables if they do not already exist. Examples
script, e.g. as an argument to another command that allows for are these commands:
equal-style variables.
* :doc:`create_atoms <create_atoms>`
* :doc:`fix deposit <fix_deposit>`
* :doc:`compute bond/local <compute_bond_local>`
* :doc:`compute angle/local <compute_angle_local>`
* :doc:`compute dihedral/local <compute_dihedral_local>`
* :doc:`python <python>` command in conjunction with Python function wrappers used in equal- and atom-style variable formulas
A command which does require an internal-style variable to be defined in
the input script is the :doc:`fix controller <fix_controller>` command,
because another (arbitrary) command typically also references the
variable.
---------- ----------
@ -439,6 +459,15 @@ python-style variable can be used in place of an equal-style variable
anywhere in an input script, e.g. as an argument to another command anywhere in an input script, e.g. as an argument to another command
that allows for equal-style variables. that allows for equal-style variables.
A python-style variable can also be used within the formula for an
equal-style or atom-style formula in a Python function wrapper, as
explained below for variable formulas. In this context, the usage
syntax is py_varname(arg1,arg2,...), where varname is the name of the
python-style variable. When a Python wrapper function is used in an
atom-style formula, it can be invoked once per atom using arguments
specific to each atom. The resulting values in the atom-style
variable can thus be calculated by Python code.
---------- ----------
For the *string* style, a single string is assigned to the variable. For the *string* style, a single string is assigned to the variable.
@ -528,9 +557,9 @@ is a valid (though strange) variable formula:
Specifically, a formula can contain numbers, constants, thermo Specifically, a formula can contain numbers, constants, thermo
keywords, math operators, math functions, group functions, region keywords, math operators, math functions, group functions, region
functions, special functions, feature functions, atom values, atom functions, special functions, feature functions, Python function
vectors, custom atom properties, compute references, fix references, and references to other wrappers, atom values, atom vectors, custom atom properties, compute
variables. references, fix references, and references to other variables.
+------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Number | 0.2, 100, 1.0e20, -15.4, etc | | Number | 0.2, 100, 1.0e20, -15.4, etc |
@ -551,6 +580,8 @@ variables.
+------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Feature functions | is_available(category,feature), is_active(category,feature), is_defined(category,id) | | Feature functions | is_available(category,feature), is_active(category,feature), is_defined(category,id) |
+------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Python func wrapper | py_varname(x,y,z,...) |
+------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Atom values | id[i], mass[i], type[i], mol[i], x[i], y[i], z[i], vx[i], vy[i], vz[i], fx[i], fy[i], fz[i], q[i] | | Atom values | id[i], mass[i], type[i], mol[i], x[i], y[i], z[i], vx[i], vy[i], vz[i], fx[i], fy[i], fz[i], q[i] |
+------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Atom vectors | id, mass, type, mol, x, y, z, vx, vy, vz, fx, fy, fz, q | | Atom vectors | id, mass, type, mol, x, y, z, vx, vy, vz, fx, fy, fz, q |
@ -1161,6 +1192,84 @@ variable name.
---------- ----------
Python Function wrapper
------------------------
A Python function wrapper enables the formula for an equal-style or
atom-style variable to invoke functions coded in Python. In the case
of an equal-style variable, the Python-coded function will be invoked
once. In the case of an atom-style variable, it can be invoked once
per atom, if one or more of its arguments include a per-atom quantity,
e.g. the position of an atom. As illustrated below, the reason to use
a Python function wrapper is to make it easy to pass LAMMPS-related
arguments to the Python-coded function associated with a python-style
variable.
The syntax for defining a Python function wrapper is
.. code-block:: LAMMPS
py_varname(arg1,arg2,...argN)
where *varname* is the name of a python-style variable which couples
to a Python-coded function. The function will be passed the zero or
more arguments listed in parentheses: *arg1*, *arg2*, ... *argN*. As
with Math Functions, each argument can itself be an arbitrarily
complex formula.
A Python function wrapper can be used in the following manner by an
input script:
.. code-block:: LAMMPS
variable foo python truncate
python truncate return v_foo input 1 v_arg format fi here """
def truncate(x):
return int(x)
"""
variable xtrunc atom py_foo(x)
variable ytrunc atom py_foo(y)
variable ztrunc atom py_foo(z)
dump 1 all custom 100 tmp.dump id x y z v_xtrunc v_ytrunc v_ztrunc
The first two commands define a python-style variable *foo* and couple
it to the Python-coded function *truncate()* which takes a single
floating point argument, and returns its truncated integer value. In
this case, the Python code for truncate() is included in the *python*
command; it could also be contained in a file. See the :doc:`python
<python>` command doc page for details.
The next three commands define atom-style variables *xtrunc*,
*ytrunc*, and *ztrunc*. Each of them include the same Python function
wrapper in their formula, with a different argument. The atom-style
variable *xtrunc* will invoke the python-style variable *foo*, which
will in turn invoke the Python-coded *truncate()* method. Because
*xtrunc* is an atom-style variable, and the argument *x* in the Python
function wrapper is a per-atom quantity (the x-coord of each atom),
each processor will invoke the *truncate()* method once per atom, for
the atoms it owns.
When invoked for the Ith atom, the value of the *arg* internal-style
variable, defined by the *python* command, is set to the x-coord of
the Ith atom. The call via python-style variable *foo* to the Python
*truncate()* function passes the value of the *arg* variable as its
first (and only) argument. Likewise, the return value of the Python
function becomes is stored by the python-style variable *foo* and used
in the *xtrunc* atom-style variable formula for the Ith atom.
The resulting per-atom vector for *xtrunc* will thus contain the
truncated x-coord of every atom in the system. The dump command
includes the truncated xyz coords for each atom in its output.
See the :doc:`python <python>` command for more details on options the
*python* command can specify as well as examples of more complex Python
functions which can be wrapped in this manner. In particular, the
Python function can take a variety of arguments, some generated by the
*python* command, and others by the arguments of the Python function
wrapper.
----------
Atom Values and Vectors Atom Values and Vectors
----------------------- -----------------------

View File

@ -3129,6 +3129,7 @@ Pxy
pxz pxz
py py
Py Py
pyargs
pydir pydir
pylammps pylammps
PyLammps PyLammps
@ -4080,6 +4081,7 @@ Vanduyfhuys
varargs varargs
varavg varavg
variational variational
varname
Varshalovich Varshalovich
Varshney Varshney
vashishta vashishta

View File

@ -0,0 +1,234 @@
ITEM: TIMESTEP
0
ITEM: NUMBER OF ATOMS
108
ITEM: BOX BOUNDS pp pp pp
0.0000000000000000e+00 5.0387885741475218e+00
0.0000000000000000e+00 5.0387885741475218e+00
0.0000000000000000e+00 5.0387885741475218e+00
ITEM: ATOMS id x y z v_xtrunc v_ytrunc v_ztrunc
1 0 0 0 0 0 0
2 0.839798 0.839798 0 0 0 0
3 0.839798 0 0.839798 0 0 0
4 0 0.839798 0.839798 0 0 0
5 1.6796 0 0 1 0 0
6 2.51939 0.839798 0 2 0 0
7 2.51939 0 0.839798 2 0 0
8 1.6796 0.839798 0.839798 1 0 0
9 3.35919 0 0 3 0 0
10 4.19899 0.839798 0 4 0 0
11 4.19899 0 0.839798 4 0 0
12 3.35919 0.839798 0.839798 3 0 0
13 0 1.6796 0 0 1 0
14 0.839798 2.51939 0 0 2 0
15 0.839798 1.6796 0.839798 0 1 0
16 0 2.51939 0.839798 0 2 0
17 1.6796 1.6796 0 1 1 0
18 2.51939 2.51939 0 2 2 0
19 2.51939 1.6796 0.839798 2 1 0
20 1.6796 2.51939 0.839798 1 2 0
21 3.35919 1.6796 0 3 1 0
22 4.19899 2.51939 0 4 2 0
23 4.19899 1.6796 0.839798 4 1 0
24 3.35919 2.51939 0.839798 3 2 0
25 0 3.35919 0 0 3 0
26 0.839798 4.19899 0 0 4 0
27 0.839798 3.35919 0.839798 0 3 0
28 0 4.19899 0.839798 0 4 0
29 1.6796 3.35919 0 1 3 0
30 2.51939 4.19899 0 2 4 0
31 2.51939 3.35919 0.839798 2 3 0
32 1.6796 4.19899 0.839798 1 4 0
33 3.35919 3.35919 0 3 3 0
34 4.19899 4.19899 0 4 4 0
35 4.19899 3.35919 0.839798 4 3 0
36 3.35919 4.19899 0.839798 3 4 0
37 0 0 1.6796 0 0 1
38 0.839798 0.839798 1.6796 0 0 1
39 0.839798 0 2.51939 0 0 2
40 0 0.839798 2.51939 0 0 2
41 1.6796 0 1.6796 1 0 1
42 2.51939 0.839798 1.6796 2 0 1
43 2.51939 0 2.51939 2 0 2
44 1.6796 0.839798 2.51939 1 0 2
45 3.35919 0 1.6796 3 0 1
46 4.19899 0.839798 1.6796 4 0 1
47 4.19899 0 2.51939 4 0 2
48 3.35919 0.839798 2.51939 3 0 2
49 0 1.6796 1.6796 0 1 1
50 0.839798 2.51939 1.6796 0 2 1
51 0.839798 1.6796 2.51939 0 1 2
52 0 2.51939 2.51939 0 2 2
53 1.6796 1.6796 1.6796 1 1 1
54 2.51939 2.51939 1.6796 2 2 1
55 2.51939 1.6796 2.51939 2 1 2
56 1.6796 2.51939 2.51939 1 2 2
57 3.35919 1.6796 1.6796 3 1 1
58 4.19899 2.51939 1.6796 4 2 1
59 4.19899 1.6796 2.51939 4 1 2
60 3.35919 2.51939 2.51939 3 2 2
61 0 3.35919 1.6796 0 3 1
62 0.839798 4.19899 1.6796 0 4 1
63 0.839798 3.35919 2.51939 0 3 2
64 0 4.19899 2.51939 0 4 2
65 1.6796 3.35919 1.6796 1 3 1
66 2.51939 4.19899 1.6796 2 4 1
67 2.51939 3.35919 2.51939 2 3 2
68 1.6796 4.19899 2.51939 1 4 2
69 3.35919 3.35919 1.6796 3 3 1
70 4.19899 4.19899 1.6796 4 4 1
71 4.19899 3.35919 2.51939 4 3 2
72 3.35919 4.19899 2.51939 3 4 2
73 0 0 3.35919 0 0 3
74 0.839798 0.839798 3.35919 0 0 3
75 0.839798 0 4.19899 0 0 4
76 0 0.839798 4.19899 0 0 4
77 1.6796 0 3.35919 1 0 3
78 2.51939 0.839798 3.35919 2 0 3
79 2.51939 0 4.19899 2 0 4
80 1.6796 0.839798 4.19899 1 0 4
81 3.35919 0 3.35919 3 0 3
82 4.19899 0.839798 3.35919 4 0 3
83 4.19899 0 4.19899 4 0 4
84 3.35919 0.839798 4.19899 3 0 4
85 0 1.6796 3.35919 0 1 3
86 0.839798 2.51939 3.35919 0 2 3
87 0.839798 1.6796 4.19899 0 1 4
88 0 2.51939 4.19899 0 2 4
89 1.6796 1.6796 3.35919 1 1 3
90 2.51939 2.51939 3.35919 2 2 3
91 2.51939 1.6796 4.19899 2 1 4
92 1.6796 2.51939 4.19899 1 2 4
93 3.35919 1.6796 3.35919 3 1 3
94 4.19899 2.51939 3.35919 4 2 3
95 4.19899 1.6796 4.19899 4 1 4
96 3.35919 2.51939 4.19899 3 2 4
97 0 3.35919 3.35919 0 3 3
98 0.839798 4.19899 3.35919 0 4 3
99 0.839798 3.35919 4.19899 0 3 4
100 0 4.19899 4.19899 0 4 4
101 1.6796 3.35919 3.35919 1 3 3
102 2.51939 4.19899 3.35919 2 4 3
103 2.51939 3.35919 4.19899 2 3 4
104 1.6796 4.19899 4.19899 1 4 4
105 3.35919 3.35919 3.35919 3 3 3
106 4.19899 4.19899 3.35919 4 4 3
107 4.19899 3.35919 4.19899 4 3 4
108 3.35919 4.19899 4.19899 3 4 4
ITEM: TIMESTEP
100
ITEM: NUMBER OF ATOMS
108
ITEM: BOX BOUNDS pp pp pp
0.0000000000000000e+00 5.0387885741475218e+00
0.0000000000000000e+00 5.0387885741475218e+00
0.0000000000000000e+00 5.0387885741475218e+00
ITEM: ATOMS id x y z v_xtrunc v_ytrunc v_ztrunc
1 4.97801 0.0605812 4.88474 4 0 4
2 0.800305 0.813213 0.0576603 0 0 0
3 0.6485 5.00484 0.836947 0 5 0
4 5.01236 0.786108 0.77507 5 0 0
5 1.50261 4.9277 4.9755 1 4 4
6 2.46756 0.653742 5.0003 2 0 5
7 2.55699 4.96491 0.926671 2 4 0
8 1.62464 0.676882 0.777822 1 0 0
9 3.57018 0.00324557 4.76943 3 0 4
10 4.24909 0.985667 5.01613 4 0 5
11 4.13124 0.178555 0.700886 4 0 0
12 3.22537 0.963341 0.639686 3 0 0
13 4.90815 1.75715 0.0826318 4 1 0
14 1.00239 2.45421 0.0111309 1 2 0
15 0.88902 1.62097 0.874547 0 1 0
16 0.269512 2.41383 0.844638 0 2 0
17 1.54513 1.51174 0.0869798 1 1 0
18 2.0637 2.5991 0.0826392 2 2 0
19 2.44634 1.71869 0.918144 2 1 0
20 1.57997 2.52559 1.04361 1 2 1
21 3.26063 2.10027 0.0160498 3 2 0
22 4.21774 2.61172 0.0367453 4 2 0
23 4.32215 1.8333 0.925663 4 1 0
24 3.38151 2.6788 0.906258 3 2 0
25 0.275459 3.26838 0.21932 0 3 0
26 0.675354 4.14887 4.94054 0 4 4
27 0.867201 3.35405 1.17655 0 3 1
28 4.78788 4.34273 0.478103 4 4 0
29 1.42014 3.50181 0.191388 1 3 0
30 2.39935 4.14279 0.0726977 2 4 0
31 2.68157 3.57418 0.921633 2 3 0
32 1.77965 4.13041 0.925765 1 4 0
33 3.10555 3.22911 5.00703 3 3 5
34 4.24908 3.67507 4.83053 4 3 4
35 4.2444 3.59428 0.837193 4 3 0
36 3.52155 4.21032 0.423884 3 4 0
37 0.00881412 4.90616 1.74242 0 4 1
38 0.800033 0.812556 1.72553 0 0 1
39 0.929754 0.0251285 2.53734 0 0 2
40 0.0189933 0.897729 2.4272 0 0 2
41 1.77025 0.0864618 1.69637 1 0 1
42 2.6077 0.984882 1.7897 2 0 1
43 2.55819 4.95626 2.45373 2 4 2
44 1.77963 0.875522 2.5056 1 0 2
45 3.50499 0.0189748 1.5186 3 0 1
46 4.47856 0.849521 1.59138 4 0 1
47 3.99 0.309367 2.42507 3 0 2
48 3.06373 0.748905 2.87477 3 0 2
49 0.149175 1.72252 1.81302 0 1 1
50 0.963279 2.42412 1.76899 0 2 1
51 0.879984 1.74899 2.55389 0 1 2
52 0.211736 2.57456 2.48446 0 2 2
53 1.75238 1.7433 1.79028 1 1 1
54 2.61023 2.6802 1.55816 2 2 1
55 2.55259 1.72904 2.60295 2 1 2
56 1.7903 2.56204 2.41895 1 2 2
57 3.43857 1.71819 1.54054 3 1 1
58 4.20455 2.5331 1.87607 4 2 1
59 4.12961 1.48438 2.40107 4 1 2
60 3.36606 2.52455 2.67203 3 2 2
61 0.0138563 3.23504 1.76147 0 3 1
62 0.968224 4.42746 1.67526 0 4 1
63 0.965745 3.43479 2.52674 0 3 2
64 0.144542 4.27264 2.61793 0 4 2
65 1.7741 3.32184 1.66819 1 3 1
66 2.4878 4.29352 1.69069 2 4 1
67 2.56992 3.44579 2.4534 2 3 2
68 1.72356 4.2344 2.49376 1 4 2
69 3.43363 3.36417 1.87451 3 3 1
70 4.32623 4.1046 1.69102 4 4 1
71 4.22927 3.23505 2.64208 4 3 2
72 3.41086 4.28362 2.42553 3 4 2
73 4.85349 0.0953155 3.14546 4 0 3
74 0.833593 0.840282 3.27238 0 0 3
75 0.578712 4.99093 3.85234 0 4 3
76 0.119704 1.00404 4.09942 0 1 4
77 1.78347 4.95586 3.31184 1 4 3
78 2.34318 0.736887 3.54475 2 0 3
79 2.34858 4.82316 4.28272 2 4 4
80 1.55893 0.765128 4.31112 1 0 4
81 3.13128 0.0257347 3.70936 3 0 3
82 4.10851 0.877375 3.36388 4 0 3
83 4.30691 4.69041 4.14081 4 4 4
84 3.48471 1.07347 4.35888 3 1 4
85 0.0668209 1.77975 3.29313 0 1 3
86 0.894798 2.5043 3.34172 0 2 3
87 0.743432 1.71961 4.3483 0 1 4
88 0.135168 2.69084 4.12098 0 2 4
89 1.73749 1.5259 3.46692 1 1 3
90 2.47391 2.57508 3.27574 2 2 3
91 2.4825 1.46064 4.38907 2 1 4
92 1.67786 2.47309 4.03082 1 2 4
93 3.34694 1.70248 3.44784 3 1 3
94 4.20676 2.29697 3.23228 4 2 3
95 4.31748 1.76411 4.20898 4 1 4
96 3.1135 2.59262 4.08996 3 2 4
97 0.0727406 3.44797 3.30247 0 3 3
98 1.0752 4.05296 3.38036 1 4 3
99 1.0085 3.35058 4.3598 1 3 4
100 0.054939 4.00087 4.11831 0 4 4
101 1.72259 3.24691 3.19685 1 3 3
102 2.54324 4.21582 3.29642 2 4 3
103 2.33008 3.38603 4.27885 2 3 4
104 1.65617 4.10852 4.3491 1 4 4
105 3.14646 3.43756 3.28105 3 3 3
106 4.09781 4.10691 3.24439 4 4 3
107 4.1529 3.22538 3.79605 4 3 3
108 3.16394 3.96553 4.31023 3 3 4

View File

@ -0,0 +1,234 @@
ITEM: TIMESTEP
0
ITEM: NUMBER OF ATOMS
108
ITEM: BOX BOUNDS pp pp pp
0.0000000000000000e+00 5.0387885741475218e+00
0.0000000000000000e+00 5.0387885741475218e+00
0.0000000000000000e+00 5.0387885741475218e+00
ITEM: ATOMS id x y z v_xtrunc v_ytrunc v_ztrunc
1 0 0 0 0 0 0
2 0.839798 0.839798 0 0 0 0
3 0.839798 0 0.839798 0 0 0
4 0 0.839798 0.839798 0 0 0
13 0 1.6796 0 0 1 0
14 0.839798 1.6796 0.839798 0 1 0
19 0 0 1.6796 0 0 1
20 0.839798 0.839798 1.6796 0 0 1
25 0 1.6796 1.6796 0 1 1
5 1.6796 0 0 1 0 0
6 2.51939 0.839798 0 2 0 0
7 2.51939 0 0.839798 2 0 0
8 1.6796 0.839798 0.839798 1 0 0
15 1.6796 1.6796 0 1 1 0
16 2.51939 1.6796 0.839798 2 1 0
21 1.6796 0 1.6796 1 0 1
22 2.51939 0.839798 1.6796 2 0 1
26 1.6796 1.6796 1.6796 1 1 1
9 3.35919 0 0 3 0 0
10 4.19899 0.839798 0 4 0 0
11 4.19899 0 0.839798 4 0 0
12 3.35919 0.839798 0.839798 3 0 0
17 3.35919 1.6796 0 3 1 0
18 4.19899 1.6796 0.839798 4 1 0
23 3.35919 0 1.6796 3 0 1
24 4.19899 0.839798 1.6796 4 0 1
27 3.35919 1.6796 1.6796 3 1 1
28 0.839798 0 2.51939 0 0 2
29 0 0.839798 2.51939 0 0 2
34 0.839798 1.6796 2.51939 0 1 2
37 0 0 3.35919 0 0 3
38 0.839798 0.839798 3.35919 0 0 3
39 0.839798 0 4.19899 0 0 4
40 0 0.839798 4.19899 0 0 4
49 0 1.6796 3.35919 0 1 3
50 0.839798 1.6796 4.19899 0 1 4
30 2.51939 0 2.51939 2 0 2
31 1.6796 0.839798 2.51939 1 0 2
35 2.51939 1.6796 2.51939 2 1 2
41 1.6796 0 3.35919 1 0 3
42 2.51939 0.839798 3.35919 2 0 3
43 2.51939 0 4.19899 2 0 4
44 1.6796 0.839798 4.19899 1 0 4
51 1.6796 1.6796 3.35919 1 1 3
52 2.51939 1.6796 4.19899 2 1 4
32 4.19899 0 2.51939 4 0 2
33 3.35919 0.839798 2.51939 3 0 2
36 4.19899 1.6796 2.51939 4 1 2
45 3.35919 0 3.35919 3 0 3
46 4.19899 0.839798 3.35919 4 0 3
47 4.19899 0 4.19899 4 0 4
48 3.35919 0.839798 4.19899 3 0 4
53 3.35919 1.6796 3.35919 3 1 3
54 4.19899 1.6796 4.19899 4 1 4
55 0.839798 2.51939 0 0 2 0
56 0 2.51939 0.839798 0 2 0
61 0 3.35919 0 0 3 0
62 0.839798 4.19899 0 0 4 0
63 0.839798 3.35919 0.839798 0 3 0
64 0 4.19899 0.839798 0 4 0
73 0.839798 2.51939 1.6796 0 2 1
76 0 3.35919 1.6796 0 3 1
77 0.839798 4.19899 1.6796 0 4 1
57 2.51939 2.51939 0 2 2 0
58 1.6796 2.51939 0.839798 1 2 0
65 1.6796 3.35919 0 1 3 0
66 2.51939 4.19899 0 2 4 0
67 2.51939 3.35919 0.839798 2 3 0
68 1.6796 4.19899 0.839798 1 4 0
74 2.51939 2.51939 1.6796 2 2 1
78 1.6796 3.35919 1.6796 1 3 1
79 2.51939 4.19899 1.6796 2 4 1
59 4.19899 2.51939 0 4 2 0
60 3.35919 2.51939 0.839798 3 2 0
69 3.35919 3.35919 0 3 3 0
70 4.19899 4.19899 0 4 4 0
71 4.19899 3.35919 0.839798 4 3 0
72 3.35919 4.19899 0.839798 3 4 0
75 4.19899 2.51939 1.6796 4 2 1
80 3.35919 3.35919 1.6796 3 3 1
81 4.19899 4.19899 1.6796 4 4 1
82 0 2.51939 2.51939 0 2 2
85 0.839798 3.35919 2.51939 0 3 2
86 0 4.19899 2.51939 0 4 2
91 0.839798 2.51939 3.35919 0 2 3
92 0 2.51939 4.19899 0 2 4
97 0 3.35919 3.35919 0 3 3
98 0.839798 4.19899 3.35919 0 4 3
99 0.839798 3.35919 4.19899 0 3 4
100 0 4.19899 4.19899 0 4 4
83 1.6796 2.51939 2.51939 1 2 2
87 2.51939 3.35919 2.51939 2 3 2
88 1.6796 4.19899 2.51939 1 4 2
93 2.51939 2.51939 3.35919 2 2 3
94 1.6796 2.51939 4.19899 1 2 4
101 1.6796 3.35919 3.35919 1 3 3
102 2.51939 4.19899 3.35919 2 4 3
103 2.51939 3.35919 4.19899 2 3 4
104 1.6796 4.19899 4.19899 1 4 4
84 3.35919 2.51939 2.51939 3 2 2
89 4.19899 3.35919 2.51939 4 3 2
90 3.35919 4.19899 2.51939 3 4 2
95 4.19899 2.51939 3.35919 4 2 3
96 3.35919 2.51939 4.19899 3 2 4
105 3.35919 3.35919 3.35919 3 3 3
106 4.19899 4.19899 3.35919 4 4 3
107 4.19899 3.35919 4.19899 4 3 4
108 3.35919 4.19899 4.19899 3 4 4
ITEM: TIMESTEP
100
ITEM: NUMBER OF ATOMS
108
ITEM: BOX BOUNDS pp pp pp
0.0000000000000000e+00 5.0387885741475218e+00
0.0000000000000000e+00 5.0387885741475218e+00
0.0000000000000000e+00 5.0387885741475218e+00
ITEM: ATOMS id x y z v_xtrunc v_ytrunc v_ztrunc
27 3.43857 1.71819 1.54054 3 1 1
55 1.00239 2.45421 0.0111309 1 2 0
24 4.47856 0.849521 1.59138 4 0 1
4 5.01236 0.786108 0.77507 5 0 0
13 4.90815 1.75715 0.0826318 4 1 0
14 0.88902 1.62097 0.874547 0 1 0
23 3.50499 0.0189748 1.5186 3 0 1
20 0.800033 0.812556 1.72553 0 0 1
25 0.149175 1.72252 1.81302 0 1 1
18 4.32215 1.8333 0.925663 4 1 0
2 0.800305 0.813213 0.0576603 0 0 0
11 4.13124 0.178555 0.700886 4 0 0
8 1.62464 0.676882 0.777822 1 0 0
15 1.54513 1.51174 0.0869798 1 1 0
16 2.44634 1.71869 0.918144 2 1 0
21 1.77025 0.0864618 1.69637 1 0 1
22 2.6077 0.984882 1.7897 2 0 1
26 1.75238 1.7433 1.79028 1 1 1
56 0.269512 2.41383 0.844638 0 2 0
73 0.963279 2.42412 1.76899 0 2 1
12 3.22537 0.963341 0.639686 3 0 0
29 0.0189933 0.897729 2.4272 0 0 2
36 4.12961 1.48438 2.40107 4 1 2
31 1.77963 0.875522 2.5056 1 0 2
17 3.26063 2.10027 0.0160498 3 2 0
32 3.99 0.309367 2.42507 3 0 2
10 4.24909 0.985667 5.01613 4 0 5
9 3.57018 0.00324557 4.76943 3 0 4
34 0.879984 1.74899 2.55389 0 1 2
54 4.31748 1.76411 4.20898 4 1 4
38 0.833593 0.840282 3.27238 0 0 3
53 3.34694 1.70248 3.44784 3 1 3
40 0.119704 1.00404 4.09942 0 1 4
49 0.0668209 1.77975 3.29313 0 1 3
50 0.743432 1.71961 4.3483 0 1 4
1 4.97801 0.0605812 4.88474 4 0 4
28 0.929754 0.0251285 2.53734 0 0 2
94 1.67786 2.47309 4.03082 1 2 4
48 3.48471 1.07347 4.35888 3 1 4
42 2.34318 0.736887 3.54475 2 0 3
46 4.10851 0.877375 3.36388 4 0 3
44 1.55893 0.765128 4.31112 1 0 4
51 1.73749 1.5259 3.46692 1 1 3
52 2.4825 1.46064 4.38907 2 1 4
45 3.13128 0.0257347 3.70936 3 0 3
33 3.06373 0.748905 2.87477 3 0 2
37 4.85349 0.0953155 3.14546 4 0 3
95 4.20676 2.29697 3.23228 4 2 3
91 0.894798 2.5043 3.34172 0 2 3
6 2.46756 0.653742 5.0003 2 0 5
35 2.55259 1.72904 2.60295 2 1 2
75 4.20455 2.5331 1.87607 4 2 1
83 1.7903 2.56204 2.41895 1 2 2
90 3.41086 4.28362 2.42553 3 4 2
7 2.55699 4.96491 0.926671 2 4 0
63 0.867201 3.35405 1.17655 0 3 1
64 4.78788 4.34273 0.478103 4 4 0
81 4.32623 4.1046 1.69102 4 4 1
76 0.0138563 3.23504 1.76147 0 3 1
77 0.968224 4.42746 1.67526 0 4 1
57 2.0637 2.5991 0.0826392 2 2 0
80 3.43363 3.36417 1.87451 3 3 1
65 1.42014 3.50181 0.191388 1 3 0
58 1.57997 2.52559 1.04361 1 2 1
67 2.68157 3.57418 0.921633 2 3 0
68 1.77965 4.13041 0.925765 1 4 0
74 2.61023 2.6802 1.55816 2 2 1
78 1.7741 3.32184 1.66819 1 3 1
79 2.4878 4.29352 1.69069 2 4 1
59 4.21774 2.61172 0.0367453 4 2 0
72 3.52155 4.21032 0.423884 3 4 0
19 0.00881412 4.90616 1.74242 0 4 1
71 4.2444 3.59428 0.837193 4 3 0
60 3.38151 2.6788 0.906258 3 2 0
88 1.72356 4.2344 2.49376 1 4 2
87 2.56992 3.44579 2.4534 2 3 2
61 0.275459 3.26838 0.21932 0 3 0
3 0.6485 5.00484 0.836947 0 5 0
66 2.39935 4.14279 0.0726977 2 4 0
82 0.211736 2.57456 2.48446 0 2 2
30 2.55819 4.95626 2.45373 2 4 2
108 3.16394 3.96553 4.31023 3 3 4
62 0.675354 4.14887 4.94054 0 4 4
47 4.30691 4.69041 4.14081 4 4 4
107 4.1529 3.22538 3.79605 4 3 3
92 0.135168 2.69084 4.12098 0 2 4
97 0.0727406 3.44797 3.30247 0 3 3
98 1.0752 4.05296 3.38036 1 4 3
99 1.0085 3.35058 4.3598 1 3 4
100 0.054939 4.00087 4.11831 0 4 4
106 4.09781 4.10691 3.24439 4 4 3
70 4.24908 3.67507 4.83053 4 3 4
43 2.34858 4.82316 4.28272 2 4 4
105 3.14646 3.43756 3.28105 3 3 3
96 3.1135 2.59262 4.08996 3 2 4
101 1.72259 3.24691 3.19685 1 3 3
102 2.54324 4.21582 3.29642 2 4 3
103 2.33008 3.38603 4.27885 2 3 4
104 1.65617 4.10852 4.3491 1 4 4
84 3.36606 2.52455 2.67203 3 2 2
89 4.22927 3.23505 2.64208 4 3 2
41 1.78347 4.95586 3.31184 1 4 3
93 2.47391 2.57508 3.27574 2 2 3
39 0.578712 4.99093 3.85234 0 4 3
86 0.144542 4.27264 2.61793 0 4 2
5 1.50261 4.9277 4.9755 1 4 4
69 3.10555 3.22911 5.00703 3 3 5
85 0.965745 3.43479 2.52674 0 3 2

View File

@ -0,0 +1,60 @@
# 3d Lennard-Jones melt with equal- and atom-style variables which
# use a Python function wrapper in their formulas
variable x index 3
variable y index 3
variable z index 3
units lj
atom_style atomic
lattice fcc 0.8442
region box block 0 $x 0 $y 0 $z
create_box 1 box
create_atoms 1 box
mass 1 1.0
velocity all create 1.44 87287 loop geom
pair_style lj/cut 2.5
pair_coeff 1 1 1.0 1.0 2.5
neighbor 0.3 bin
neigh_modify delay 0 every 20 check no
fix 1 all nve
# define Python truncate() function
variable foo python truncate
python truncate return v_foo input 1 iv_arg format fi here """
def truncate(x):
return int(x)
"""
# use in equal-style variable
variable scalar equal py_foo(4.5)
print "TRUNCATE ${scalar}"
# use in atom-style variable
# examine dump file to see truncated xyz coords of each atom
variable xtrunc atom py_foo(x)
variable ytrunc atom py_foo(y)
variable ztrunc atom py_foo(z)
dump 1 all custom 100 tmp.dump id x y z v_xtrunc v_ytrunc v_ztrunc
# use in vector-style variable
compute ke all temp
variable ke vector c_ke
variable ketrunc vector py_foo(v_ke)
thermo_style custom step temp epair v_ketrunc[*6]
run 100
print "KE TENSOR ${ketrunc}"

View File

@ -0,0 +1,116 @@
LAMMPS (2 Apr 2025 - Development - patch_2Apr2025-270-g2351418c94-modified)
# 3d Lennard-Jones melt with equal- and atom-style variables which
# use a Python function wrapper in their formulas
variable x index 3
variable y index 3
variable z index 3
units lj
atom_style atomic
lattice fcc 0.8442
Lattice spacing in x,y,z = 1.6795962 1.6795962 1.6795962
region box block 0 $x 0 $y 0 $z
region box block 0 3 0 $y 0 $z
region box block 0 3 0 3 0 $z
region box block 0 3 0 3 0 3
create_box 1 box
Created orthogonal box = (0 0 0) to (5.0387886 5.0387886 5.0387886)
1 by 1 by 1 MPI processor grid
create_atoms 1 box
Created 108 atoms
using lattice units in orthogonal box = (0 0 0) to (5.0387886 5.0387886 5.0387886)
create_atoms CPU = 0.000 seconds
mass 1 1.0
velocity all create 1.44 87287 loop geom
pair_style lj/cut 2.5
pair_coeff 1 1 1.0 1.0 2.5
neighbor 0.3 bin
neigh_modify delay 0 every 20 check no
fix 1 all nve
# define Python truncate() function
variable foo python truncate
python truncate return v_foo input 1 iv_arg format fi here """
def truncate(x):
return int(x)
"""
# use in equal-style variable
variable scalar equal py_foo(4.5)
print "TRUNCATE ${scalar}"
TRUNCATE 4
# use in atom-style variable
# examine dump file to see truncated xyz coords of each atom
variable xtrunc atom py_foo(x)
variable ytrunc atom py_foo(y)
variable ztrunc atom py_foo(z)
dump 1 all custom 100 tmp.dump id x y z v_xtrunc v_ytrunc v_ztrunc
# use in vector-style variable
compute ke all temp
variable ke vector c_ke
variable ketrunc vector py_foo(v_ke)
thermo_style custom step temp epair v_ketrunc[*6]
run 100
Generated 0 of 0 mixed pair_coeff terms from geometric mixing rule
Neighbor list info ...
update: every = 20 steps, delay = 0 steps, check = no
max neighbors/atom: 2000, page size: 100000
master list distance cutoff = 2.8
ghost atom cutoff = 2.8
binsize = 1.4, bins = 4 4 4
1 neighbor lists, perpetual/occasional/extra = 1 0 0
(1) pair lj/cut, perpetual
attributes: half, newton on
pair build: half/bin/atomonly/newton
stencil: half/bin/3d
bin: standard
Per MPI rank memory allocation (min/avg/max) = 2.598 | 2.598 | 2.598 Mbytes
Step Temp E_pair v_ketrunc[1] v_ketrunc[2] v_ketrunc[3] v_ketrunc[4] v_ketrunc[5] v_ketrunc[6]
0 1.44 -6.7733681 155 152 154 -10 -4 -6
100 0.82217015 -5.8614684 113 65 84 7 -1 -12
Loop time of 0.00278186 on 1 procs for 100 steps with 108 atoms
Performance: 15529161.573 tau/day, 35947.133 timesteps/s, 3.882 Matom-step/s
100.0% CPU use with 1 MPI tasks x no OpenMP threads
MPI task timing breakdown:
Section | min time | avg time | max time |%varavg| %total
---------------------------------------------------------------
Pair | 0.0018161 | 0.0018161 | 0.0018161 | 0.0 | 65.29
Neigh | 0.00057543 | 0.00057543 | 0.00057543 | 0.0 | 20.68
Comm | 0.00019634 | 0.00019634 | 0.00019634 | 0.0 | 7.06
Output | 0.00012056 | 0.00012056 | 0.00012056 | 0.0 | 4.33
Modify | 4.8221e-05 | 4.8221e-05 | 4.8221e-05 | 0.0 | 1.73
Other | | 2.516e-05 | | | 0.90
Nlocal: 108 ave 108 max 108 min
Histogram: 1 0 0 0 0 0 0 0 0 0
Nghost: 980 ave 980 max 980 min
Histogram: 1 0 0 0 0 0 0 0 0 0
Neighs: 4071 ave 4071 max 4071 min
Histogram: 1 0 0 0 0 0 0 0 0 0
Total # of neighbors = 4071
Ave neighs/atom = 37.694444
Neighbor list builds = 5
Dangerous builds not checked
print "KE TENSOR ${ketrunc}"
KE TENSOR [113,65,84,7,-1,-12]
Total wall time: 0:00:00

View File

@ -0,0 +1,116 @@
LAMMPS (2 Apr 2025 - Development - patch_2Apr2025-270-g2351418c94-modified)
# 3d Lennard-Jones melt with equal- and atom-style variables which
# use a Python function wrapper in their formulas
variable x index 3
variable y index 3
variable z index 3
units lj
atom_style atomic
lattice fcc 0.8442
Lattice spacing in x,y,z = 1.6795962 1.6795962 1.6795962
region box block 0 $x 0 $y 0 $z
region box block 0 3 0 $y 0 $z
region box block 0 3 0 3 0 $z
region box block 0 3 0 3 0 3
create_box 1 box
Created orthogonal box = (0 0 0) to (5.0387886 5.0387886 5.0387886)
1 by 2 by 2 MPI processor grid
create_atoms 1 box
Created 108 atoms
using lattice units in orthogonal box = (0 0 0) to (5.0387886 5.0387886 5.0387886)
create_atoms CPU = 0.000 seconds
mass 1 1.0
velocity all create 1.44 87287 loop geom
pair_style lj/cut 2.5
pair_coeff 1 1 1.0 1.0 2.5
neighbor 0.3 bin
neigh_modify delay 0 every 20 check no
fix 1 all nve
# define Python truncate() function
variable foo python truncate
python truncate return v_foo input 1 iv_arg format fi here """
def truncate(x):
return int(x)
"""
# use in equal-style variable
variable scalar equal py_foo(4.5)
print "TRUNCATE ${scalar}"
TRUNCATE 4
# use in atom-style variable
# examine dump file to see truncated xyz coords of each atom
variable xtrunc atom py_foo(x)
variable ytrunc atom py_foo(y)
variable ztrunc atom py_foo(z)
dump 1 all custom 100 tmp.dump id x y z v_xtrunc v_ytrunc v_ztrunc
# use in vector-style variable
compute ke all temp
variable ke vector c_ke
variable ketrunc vector py_foo(v_ke)
thermo_style custom step temp epair v_ketrunc[*6]
run 100
Generated 0 of 0 mixed pair_coeff terms from geometric mixing rule
Neighbor list info ...
update: every = 20 steps, delay = 0 steps, check = no
max neighbors/atom: 2000, page size: 100000
master list distance cutoff = 2.8
ghost atom cutoff = 2.8
binsize = 1.4, bins = 4 4 4
1 neighbor lists, perpetual/occasional/extra = 1 0 0
(1) pair lj/cut, perpetual
attributes: half, newton on
pair build: half/bin/atomonly/newton
stencil: half/bin/3d
bin: standard
Per MPI rank memory allocation (min/avg/max) = 2.59 | 2.59 | 2.59 Mbytes
Step Temp E_pair v_ketrunc[1] v_ketrunc[2] v_ketrunc[3] v_ketrunc[4] v_ketrunc[5] v_ketrunc[6]
0 1.44 -6.7733681 155 152 154 -10 -4 -6
100 0.82217015 -5.8614684 113 65 84 7 -1 -12
Loop time of 0.00268845 on 4 procs for 100 steps with 108 atoms
Performance: 16068745.964 tau/day, 37196.171 timesteps/s, 4.017 Matom-step/s
66.7% CPU use with 4 MPI tasks x no OpenMP threads
MPI task timing breakdown:
Section | min time | avg time | max time |%varavg| %total
---------------------------------------------------------------
Pair | 0.00043389 | 0.00051695 | 0.00061255 | 0.0 | 19.23
Neigh | 0.00017121 | 0.00018976 | 0.00019891 | 0.0 | 7.06
Comm | 0.0017423 | 0.0018487 | 0.0019509 | 0.2 | 68.76
Output | 6.7449e-05 | 6.9998e-05 | 7.5195e-05 | 0.0 | 2.60
Modify | 2.1329e-05 | 2.2855e-05 | 2.4821e-05 | 0.0 | 0.85
Other | | 4.018e-05 | | | 1.49
Nlocal: 27 ave 30 max 25 min
Histogram: 1 0 1 0 1 0 0 0 0 1
Nghost: 614 ave 616 max 612 min
Histogram: 1 0 1 0 0 0 0 1 0 1
Neighs: 1017.75 ave 1149 max 894 min
Histogram: 1 1 0 0 0 0 0 1 0 1
Total # of neighbors = 4071
Ave neighs/atom = 37.694444
Neighbor list builds = 5
Dangerous builds not checked
print "KE TENSOR ${ketrunc}"
KE TENSOR [113,65,84,7,-1,-12]
Total wall time: 0:00:00

View File

@ -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"
@ -62,6 +63,7 @@
using namespace LAMMPS_NS; using namespace LAMMPS_NS;
enum { NONE, INT, DOUBLE, STRING, PTR }; enum { NONE, INT, DOUBLE, STRING, PTR };
enum { VALUE, VARIABLE, INTERNALVAR };
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
@ -94,20 +96,27 @@ PythonImpl::PythonImpl(LAMMPS *lmp) : Pointers(lmp)
// 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, Error::NOLASTLINE, "Could not register MLIAPPY embedded python module.");
err = PyImport_AppendInittab("mliap_unified_couple", PyInit_mliap_unified_couple); err = PyImport_AppendInittab("mliap_unified_couple", PyInit_mliap_unified_couple);
if (err) error->all(FLERR, "Could not register MLIAPPY unified embedded python module."); if (err)
error->all(FLERR, Error::NOLASTLINE,
"Could not register MLIAPPY unified embedded python module.");
#ifdef LMP_KOKKOS #ifdef LMP_KOKKOS
// 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.
err = PyImport_AppendInittab("mliap_model_python_couple_kokkos", err = PyImport_AppendInittab("mliap_model_python_couple_kokkos",
PyInit_mliap_model_python_couple_kokkos); PyInit_mliap_model_python_couple_kokkos);
if (err) error->all(FLERR, "Could not register MLIAPPY embedded python KOKKOS module."); if (err)
error->all(FLERR, Error::NOLASTLINE,
"Could not register MLIAPPY embedded python KOKKOS module.");
err = PyImport_AppendInittab("mliap_unified_couple_kokkos", PyInit_mliap_unified_couple_kokkos); err = PyImport_AppendInittab("mliap_unified_couple_kokkos", PyInit_mliap_unified_couple_kokkos);
if (err) error->all(FLERR, "Could not register MLIAPPY unified embedded python KOKKOS module."); if (err)
error->all(FLERR, Error::NOLASTLINE,
"Could not register MLIAPPY unified embedded python KOKKOS module.");
#endif #endif
} }
#endif #endif
@ -129,7 +138,7 @@ PythonImpl::PythonImpl(LAMMPS *lmp) : Pointers(lmp)
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, Error::NOLASTLINE, "Could not initialize embedded Python");
pyMain = (void *) pModule; pyMain = (void *) pModule;
} }
@ -158,23 +167,31 @@ 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) error->all(FLERR, "Python invoke of unknown function: {}", arg[0]); if (ifunc < 0)
error->all(FLERR, Error::ARGZERO, "Python invoke of unknown function: {}", arg[0]);
char *str = nullptr; char *str = nullptr;
if (pfuncs[ifunc].noutput) { if (pfuncs[ifunc].noutput) {
str = input->variable->pythonstyle(pfuncs[ifunc].ovarname, pfuncs[ifunc].name); str = input->variable->pythonstyle(pfuncs[ifunc].ovarname, pfuncs[ifunc].name);
if (!str) if (!str)
error->all(FLERR, error->all(
"Python variable {} does not match variable {} " FLERR, Error::ARGZERO,
"registered with Python function {}", "Python variable {} does not match variable {} registered with Python function {}",
arg[0], pfuncs[ifunc].ovarname, pfuncs[ifunc].name); arg[0], pfuncs[ifunc].ovarname, pfuncs[ifunc].name);
} }
invoke_function(ifunc, str); bool logreturn = false;
if (narg == 3 && strcmp(arg[2], "logreturn") == 0) logreturn = true;
invoke_function(ifunc, str, nullptr);
if (logreturn && str && (comm->me == 0))
utils::logmesg(lmp, "Invoked python function {} returned {}\n", arg[0], str);
return; return;
} }
@ -189,7 +206,7 @@ void PythonImpl::command(int narg, char **arg)
if (platform::file_is_readable(arg[1])) if (platform::file_is_readable(arg[1]))
err = execute_file(arg[1]); err = execute_file(arg[1]);
else else
error->all(FLERR, "Could not open python source file {} for processing", arg[1]); error->all(FLERR, 1, "Could not open python source file {} for processing", arg[1]);
} }
if (err) error->all(FLERR, "Failure in python source command"); if (err) error->all(FLERR, "Failure in python source command");
@ -213,7 +230,8 @@ void PythonImpl::command(int narg, char **arg)
if (strcmp(arg[iarg], "input") == 0) { if (strcmp(arg[iarg], "input") == 0) {
if (iarg + 2 > narg) utils::missing_cmd_args(FLERR, "python input", error); if (iarg + 2 > narg) utils::missing_cmd_args(FLERR, "python input", error);
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 number of python input arguments: {}", ninput); if (ninput < 0)
error->all(FLERR, iarg + 1, "Invalid number of python input arguments: {}", ninput);
iarg += 2; iarg += 2;
delete[] istr; delete[] istr;
istr = new char *[ninput]; istr = new char *[ninput];
@ -232,7 +250,8 @@ void PythonImpl::command(int narg, char **arg)
} else if (strcmp(arg[iarg], "length") == 0) { } else if (strcmp(arg[iarg], "length") == 0) {
if (iarg + 2 > narg) utils::missing_cmd_args(FLERR, "python length", error); if (iarg + 2 > narg) utils::missing_cmd_args(FLERR, "python length", error);
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 return value length"); if (length_longstr <= 0)
error->all(FLERR, iarg + 1, "Invalid python return value length {}", length_longstr);
iarg += 2; iarg += 2;
} else if (strcmp(arg[iarg], "file") == 0) { } else if (strcmp(arg[iarg], "file") == 0) {
if (iarg + 2 > narg) utils::missing_cmd_args(FLERR, "python file", error); if (iarg + 2 > narg) utils::missing_cmd_args(FLERR, "python file", error);
@ -247,15 +266,18 @@ void PythonImpl::command(int narg, char **arg)
existflag = 1; existflag = 1;
iarg++; iarg++;
} else } else
error->all(FLERR, "Unknown python command keyword: {}", arg[iarg]); error->all(FLERR, iarg, "Unknown python command keyword: {}", arg[iarg]);
} }
if (pyfile && herestr) if (pyfile && herestr)
error->all(FLERR, "Must not use python 'file' and 'here' keywords at the same time"); error->all(FLERR, Error::NOLASTLINE,
"Must not use python 'file' and 'here' keywords at the same time");
if (pyfile && existflag) if (pyfile && existflag)
error->all(FLERR, "Must not use python 'file' and 'exists' keywords at the same time"); error->all(FLERR, Error::NOLASTLINE,
"Must not use python 'file' and 'exists' keywords at the same time");
if (herestr && existflag) if (herestr && existflag)
error->all(FLERR, "Must not use python 'here' and 'exists' keywords at the same time"); error->all(FLERR, Error::NOLASTLINE,
"Must not use python 'here' and 'exists' keywords at the same time");
// create or overwrite entry in pfuncs vector with name = arg[0] // create or overwrite entry in pfuncs vector with name = arg[0]
@ -273,13 +295,13 @@ void PythonImpl::command(int narg, char **arg)
if (fp == nullptr) { if (fp == nullptr) {
PyUtils::Print_Errors(); PyUtils::Print_Errors();
error->all(FLERR, "Could not open Python file: {}", pyfile); error->all(FLERR, Error::NOLASTLINE, "Could not open Python file: {}", pyfile);
} }
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: {}", pyfile); error->all(FLERR, Error::NOLASTLINE, "Could not process Python file: {}", pyfile);
} }
fclose(fp); fclose(fp);
@ -287,7 +309,7 @@ void PythonImpl::command(int narg, char **arg)
int err = PyRun_SimpleString(herestr); int err = PyRun_SimpleString(herestr);
if (err) { if (err) {
PyUtils::Print_Errors(); PyUtils::Print_Errors();
error->all(FLERR, "Could not process Python string: {}", herestr); error->all(FLERR, Error::NOLASTLINE, "Could not process Python string: {}", herestr);
} }
} }
@ -298,12 +320,12 @@ void PythonImpl::command(int narg, char **arg)
if (!pFunc) { if (!pFunc) {
PyUtils::Print_Errors(); PyUtils::Print_Errors();
error->all(FLERR, "Could not find Python function {}", pfuncs[ifunc].name); error->all(FLERR, Error::NOLASTLINE, "Could not find Python function {}", 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", pfuncs[ifunc].name); error->all(FLERR, Error::NOLASTLINE, "Python function {} is not callable", pfuncs[ifunc].name);
} }
pfuncs[ifunc].pFunc = (void *) pFunc; pfuncs[ifunc].pFunc = (void *) pFunc;
@ -317,7 +339,7 @@ void PythonImpl::command(int narg, char **arg)
/* ------------------------------------------------------------------ */ /* ------------------------------------------------------------------ */
void PythonImpl::invoke_function(int ifunc, char *result) void PythonImpl::invoke_function(int ifunc, char *result, double *dvalue)
{ {
PyUtils::GIL lock; PyUtils::GIL lock;
PyObject *pValue; PyObject *pValue;
@ -331,36 +353,46 @@ void PythonImpl::invoke_function(int ifunc, char *result)
PyObject *pArgs = PyTuple_New(ninput); PyObject *pArgs = PyTuple_New(ninput);
if (!pArgs) if (!pArgs)
error->all(FLERR, "Could not prepare arguments for Python function {}", pfuncs[ifunc].name); error->all(FLERR, Error::NOLASTLINE, "Could not prepare arguments for Python function {}",
pfuncs[ifunc].name);
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] == VARIABLE) {
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, Error::NOLASTLINE,
pfuncs[ifunc].name, pfuncs[ifunc].svalue[i]); "Could not evaluate Python function {} input variable: {}", pfuncs[ifunc].name,
pfuncs[ifunc].svalue[i]);
pValue = PY_INT_FROM_LONG(PY_LONG_FROM_STRING(str)); pValue = PY_INT_FROM_LONG(PY_LONG_FROM_STRING(str));
} else if (pfuncs[ifunc].ivarflag[i] == INTERNALVAR) {
double value = input->variable->compute_equal(pfuncs[ifunc].internal_var[i]);
pValue = PyLong_FromDouble(value);
} else { } else {
pValue = PY_INT_FROM_LONG(pfuncs[ifunc].ivalue[i]); pValue = PY_INT_FROM_LONG(pfuncs[ifunc].ivalue[i]);
} }
} else if (itype == DOUBLE) { } else if (itype == DOUBLE) {
if (pfuncs[ifunc].ivarflag[i]) { if (pfuncs[ifunc].ivarflag[i] == VARIABLE) {
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, Error::NOLASTLINE,
pfuncs[ifunc].name, pfuncs[ifunc].svalue[i]); "Could not evaluate Python function {} input variable: {}", pfuncs[ifunc].name,
pfuncs[ifunc].svalue[i]);
pValue = PyFloat_FromDouble(std::stod(str)); pValue = PyFloat_FromDouble(std::stod(str));
} else if (pfuncs[ifunc].ivarflag[i] == INTERNALVAR) {
double value = input->variable->compute_equal(pfuncs[ifunc].internal_var[i]);
pValue = PyFloat_FromDouble(value);
} else { } else {
pValue = PyFloat_FromDouble(pfuncs[ifunc].dvalue[i]); pValue = PyFloat_FromDouble(pfuncs[ifunc].dvalue[i]);
} }
} else if (itype == STRING) { } else if (itype == STRING) {
if (pfuncs[ifunc].ivarflag[i]) { if (pfuncs[ifunc].ivarflag[i] == VARIABLE) {
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, Error::NOLASTLINE,
pfuncs[ifunc].name, pfuncs[ifunc].svalue[i]); "Could not evaluate Python function {} input variable: {}", pfuncs[ifunc].name,
pfuncs[ifunc].svalue[i]);
pValue = PyUnicode_FromString(str); pValue = PyUnicode_FromString(str);
} else { } else {
pValue = PyUnicode_FromString(pfuncs[ifunc].svalue[i]); pValue = PyUnicode_FromString(pfuncs[ifunc].svalue[i]);
@ -368,7 +400,7 @@ void PythonImpl::invoke_function(int ifunc, char *result)
} else if (itype == PTR) { } else if (itype == PTR) {
pValue = PyCapsule_New((void *) lmp, nullptr, nullptr); pValue = PyCapsule_New((void *) lmp, nullptr, nullptr);
} else { } else {
error->all(FLERR, "Unsupported variable type: {}", itype); error->all(FLERR, Error::NOLASTLINE, "Unsupported variable type: {}", itype);
} }
PyTuple_SetItem(pArgs, i, pValue); PyTuple_SetItem(pArgs, i, pValue);
} }
@ -381,29 +413,37 @@ void PythonImpl::invoke_function(int ifunc, char *result)
if (!pValue) { if (!pValue) {
PyUtils::Print_Errors(); PyUtils::Print_Errors();
error->one(FLERR, "Python evaluation of function {} failed", pfuncs[ifunc].name); error->one(FLERR, Error::NOLASTLINE, "Python evaluation of function {} failed",
pfuncs[ifunc].name);
} }
// function returned a value // function returned a value
// assign it to result string stored by python-style variable // if result is non-NULL, assign to result string stored by python-style variable
// or if user specified a length, assign it to longstr // or if value is string and user specified a length, assign it to longstr
// if dvalue is non-NULL, assign numeric value directly to 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) *dvalue = (double) PY_INT_AS_LONG(pValue);
if (result) {
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) *dvalue = PyFloat_AsDouble(pValue);
if (result) {
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);
}
} else if (otype == STRING) { } else if (otype == STRING) {
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);
} }
@ -416,15 +456,80 @@ int PythonImpl::find(const char *name)
return -1; return -1;
} }
/* ------------------------------------------------------------------ */ /* ---------------------------------------------------------------------
called by Variable class when a python-style variable is evaluated
this will call invoke_function() in this class
either via Variable::retrieve() or Variable::equalstyle
retrieve calls with numeric = 0, equalstyle with numeric = 1
ensure name matches a Python function
ensure the Python function produces an output
ensure the Python function outputs to the matching python-style variable
ensure a string is returned only if retrieve() is the caller
--------------------------------------------------------------------- */
int PythonImpl::variable_match(const char *name, const char *varname, int numeric) int PythonImpl::function_match(const char *name, const char *varname, int numeric, Error *error)
{ {
int ifunc = find(name); int ifunc = find(name);
if (ifunc < 0) return -1;
if (pfuncs[ifunc].noutput == 0) return -2; if (ifunc < 0)
if (strcmp(pfuncs[ifunc].ovarname, varname) != 0) return -3; error->all(FLERR, Error::NOLASTLINE, "Python function {} specified by variable {} not found",
if (numeric && pfuncs[ifunc].otype == STRING) return -4; name, varname);
if (pfuncs[ifunc].noutput == 0)
error->all(FLERR, Error::NOLASTLINE,
"Python function {} for variable {} does not return a value", name, varname);
if (strcmp(pfuncs[ifunc].ovarname, varname) != 0)
error->all(FLERR, Error::NOLASTLINE,
"Python function {} and variable {} do not link to each other", name, varname);
if (numeric && pfuncs[ifunc].otype == STRING)
error->all(FLERR, Error::NOLASTLINE, "Python function {} for variable {} returns a string",
name, varname);
return ifunc;
}
/* ---------------------------------------------------------------------
called by Variable class when evaluating a Python wrapper function
which will call invoke_function()
either via equal-style or atom-style variable formula
the latter calls invoke_function() once per atom
same error checks as function_match() plus 2 new ones
ensure match of number of Python function args mapped to internal-style variables
ensure each internal-style variable still exists
must check now in case user input script deleted variables between runs
which could invalidate indices set in create_event()
other classes avoid this issue by setting variable indices in their init() method
--------------------------------------------------------------------- */
int PythonImpl::wrapper_match(const char *name, const char *varname, int narg, int *argvars,
Error *error)
{
int ifunc = function_match(name, varname, 1, error);
if (ifunc < 0) return ifunc;
int internal_count = 0;
for (int i = 0; i < pfuncs[ifunc].ninput; i++)
if (pfuncs[ifunc].ivarflag[i] == INTERNALVAR) internal_count++;
if (internal_count != narg)
error->all(FLERR, Error::NOLASTLINE,
"Python function {} does not use {} internal variable args", name, narg);
// set argvars of internal-style variables for use by Variable class
// in Python wrapper functions
// also set internal_var for use by invoke_function()
// so that invoke_function() is as fast as possible for args which are internal-style vars
int j = 0;
for (int i = 0; i < pfuncs[ifunc].ninput; i++) {
if (pfuncs[ifunc].ivarflag[i] == INTERNALVAR) {
int ivar = input->variable->find(pfuncs[ifunc].svalue[i]);
if (ivar < 0)
error->all(FLERR, Error::NOLASTLINE, "Python function {} cannot find internal variable {}",
name, pfuncs[ifunc].svalue[i]);
pfuncs[ifunc].internal_var[i] = ivar;
argvars[j++] = ivar;
}
}
return ifunc; return ifunc;
} }
@ -457,9 +562,10 @@ int PythonImpl::create_entry(char *name, int ninput, int noutput, int length_lon
pfuncs[ifunc].noutput = noutput; pfuncs[ifunc].noutput = noutput;
if (!format && ninput + noutput) if (!format && ninput + noutput)
error->all(FLERR, "Missing python format keyword"); error->all(FLERR, Error::NOLASTLINE, "Missing python format keyword");
else if (format && ((int) strlen(format) != ninput + noutput)) else if (format && ((int) strlen(format) != ninput + noutput))
error->all(FLERR, "Input/output arguments ({}) and format characters ({}) are inconsistent", error->all(FLERR, Error::NOLASTLINE,
"Input/output arguments ({}) and format characters ({}) are inconsistent",
(ninput + noutput), strlen(format)); (ninput + noutput), strlen(format));
// process inputs as values or variables // process inputs as values or variables
@ -469,6 +575,7 @@ int PythonImpl::create_entry(char *name, int ninput, int noutput, int length_lon
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];
pfuncs[ifunc].internal_var = new int[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;
@ -476,37 +583,64 @@ int PythonImpl::create_entry(char *name, int ninput, int noutput, int length_lon
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] = VARIABLE;
pfuncs[ifunc].svalue[i] = utils::strdup(istr[i] + 2); pfuncs[ifunc].svalue[i] = utils::strdup(istr[i] + 2);
} else if (utils::strmatch(istr[i], "^iv_")) {
pfuncs[ifunc].ivarflag[i] = INTERNALVAR;
pfuncs[ifunc].svalue[i] = utils::strdup(istr[i] + 3);
char *vname = pfuncs[ifunc].svalue[i];
int ivar = input->variable->find(vname);
if (ivar < 0) { // create internal variable if does not exist
input->variable->internal_create(vname, 0.0);
ivar = input->variable->find(vname);
}
if (!input->variable->internalstyle(ivar))
error->all(FLERR, Error::NOLASTLINE, "Variable {} for python command is invalid style",
vname);
} else { } else {
pfuncs[ifunc].ivarflag[i] = 0; pfuncs[ifunc].ivarflag[i] = VALUE;
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] = VARIABLE;
pfuncs[ifunc].svalue[i] = utils::strdup(istr[i] + 2); pfuncs[ifunc].svalue[i] = utils::strdup(istr[i] + 2);
} else if (utils::strmatch(istr[i], "^iv_")) {
pfuncs[ifunc].ivarflag[i] = INTERNALVAR;
pfuncs[ifunc].svalue[i] = utils::strdup(istr[i] + 3);
char *vname = pfuncs[ifunc].svalue[i];
int ivar = input->variable->find(vname);
if (ivar < 0) { // create internal variable if does not exist
input->variable->internal_create(vname, 0.0);
ivar = input->variable->find(vname);
}
if (!input->variable->internalstyle(ivar))
error->all(FLERR, Error::NOLASTLINE, "Variable {} for python command is invalid style",
vname);
} else { } else {
pfuncs[ifunc].ivarflag[i] = 0; pfuncs[ifunc].ivarflag[i] = VALUE;
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] = VARIABLE;
pfuncs[ifunc].svalue[i] = utils::strdup(istr[i] + 2); pfuncs[ifunc].svalue[i] = utils::strdup(istr[i] + 2);
} else if (utils::strmatch(istr[i], "^iv_")) {
error->all(FLERR, Error::NOLASTLINE,
"Input argument {} cannot be internal variable with string format", istr[i]);
} else { } else {
pfuncs[ifunc].ivarflag[i] = 0; pfuncs[ifunc].ivarflag[i] = VALUE;
pfuncs[ifunc].svalue[i] = utils::strdup(istr[i]); pfuncs[ifunc].svalue[i] = utils::strdup(istr[i]);
} }
} else if (type == 'p') { } else if (type == 'p') {
pfuncs[ifunc].ivarflag[i] = 0; pfuncs[ifunc].ivarflag[i] = VALUE;
pfuncs[ifunc].itype[i] = PTR; pfuncs[ifunc].itype[i] = PTR;
if (strcmp(istr[i], "SELF") != 0) error->all(FLERR, "Invalid python command"); if (strcmp(istr[i], "SELF") != 0) error->all(FLERR, "Invalid python command");
} else } else
error->all(FLERR, "Invalid python format character: {}", type); error->all(FLERR, Error::NOLASTLINE, "Invalid python format character: {}", type);
} }
// process output as value or variable // process output as value or variable
@ -523,17 +657,19 @@ int PythonImpl::create_entry(char *name, int ninput, int noutput, int length_lon
else if (type == 's') else if (type == 's')
pfuncs[ifunc].otype = STRING; pfuncs[ifunc].otype = STRING;
else else
error->all(FLERR, "Invalid python return format character: {}", type); error->all(FLERR, Error::NOLASTLINE, "Invalid python return format character: {}", type);
if (length_longstr) { if (length_longstr) {
if (pfuncs[ifunc].otype != STRING) if (pfuncs[ifunc].otype != STRING)
error->all(FLERR, "Python command length keyword cannot be used unless output is a string"); error->all(FLERR, Error::NOLASTLINE,
"Python command length keyword 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, Error::NOLASTLINE, "Invalid python output variable name {}", ostr);
pfuncs[ifunc].ovarname = utils::strdup(ostr + 2); pfuncs[ifunc].ovarname = utils::strdup(ostr + 2);
return ifunc; return ifunc;
@ -574,6 +710,7 @@ void PythonImpl::deallocate(int i)
delete[] pfuncs[i].dvalue; delete[] pfuncs[i].dvalue;
for (int j = 0; j < pfuncs[i].ninput; j++) delete[] pfuncs[i].svalue[j]; for (int j = 0; j < pfuncs[i].ninput; j++) delete[] pfuncs[i].svalue[j];
delete[] pfuncs[i].svalue; delete[] pfuncs[i].svalue;
delete[] pfuncs[i].internal_var;
delete[] pfuncs[i].ovarname; delete[] pfuncs[i].ovarname;
delete[] pfuncs[i].longstr; delete[] pfuncs[i].longstr;
} }

View File

@ -25,9 +25,10 @@ class PythonImpl : protected Pointers, public PythonInterface {
PythonImpl(class LAMMPS *); PythonImpl(class LAMMPS *);
~PythonImpl() override; ~PythonImpl() override;
void command(int, char **) override; void command(int, char **) override;
void invoke_function(int, char *) override; void invoke_function(int, char *, double *) override;
int find(const char *) override; int find(const char *) override;
int variable_match(const char *, const char *, int) override; int function_match(const char *, const char *, int, Error *) override;
int wrapper_match(const char *, const char *, int, int *, Error *) override;
char *long_string(int) override; char *long_string(int) override;
int execute_string(char *) override; int execute_string(char *) override;
int execute_file(char *) override; int execute_file(char *) override;
@ -44,6 +45,7 @@ class PythonImpl : protected Pointers, public PythonInterface {
int *ivalue; int *ivalue;
double *dvalue; double *dvalue;
char **svalue; char **svalue;
int *internal_var; // stores per-arg index of internal variable
int otype; int otype;
char *ovarname; char *ovarname;
char *longstr; char *longstr;

View File

@ -3109,7 +3109,7 @@ void FixBondReact::update_everything()
update_num_mega++; update_num_mega++;
} }
MPI_Allreduce(MPI_IN_PLACE, &noccur[0], nreacts, MPI_INT, MPI_SUM, world); MPI_Allreduce(MPI_IN_PLACE, &noccur[0], nreacts, MPI_INT, MPI_SUM, world);
reaction_count_total[rxnID] += noccur[rxnID]; for (rxnID = 0; rxnID < nreacts; rxnID++) reaction_count_total[rxnID] += noccur[rxnID];
} else if (pass == 1) { } else if (pass == 1) {
for (int i = 0; i < global_megasize; i++) { for (int i = 0; i < global_megasize; i++) {
rxnID = (int) global_mega_glove[0][i]; rxnID = (int) global_mega_glove[0][i];

View File

@ -108,7 +108,10 @@ ComputeAngleLocal::ComputeAngleLocal(LAMMPS *lmp, int narg, char **arg) :
if (tstr) { if (tstr) {
tvar = input->variable->find(tstr); tvar = input->variable->find(tstr);
if (tvar < 0) error->all(FLERR, "Variable name for compute angle/local does not exist"); if (tvar < 0) {
input->variable->internal_create(tstr,0.0);
tvar = input->variable->find(tstr);
}
if (!input->variable->internalstyle(tvar)) if (!input->variable->internalstyle(tvar))
error->all(FLERR, "Variable for compute angle/local is invalid style"); error->all(FLERR, "Variable for compute angle/local is invalid style");
} }

View File

@ -154,7 +154,10 @@ ComputeBondLocal::ComputeBondLocal(LAMMPS *lmp, int narg, char **arg) :
if (dstr) { if (dstr) {
dvar = input->variable->find(dstr); dvar = input->variable->find(dstr);
if (dvar < 0) error->all(FLERR, "Variable name for compute bond/local does not exist"); if (dvar < 0) {
input->variable->internal_create(dstr,0.0);
dvar = input->variable->find(dstr);
}
if (!input->variable->internalstyle(dvar)) if (!input->variable->internalstyle(dvar))
error->all(FLERR, "Variable for compute bond/local is invalid style"); error->all(FLERR, "Variable for compute bond/local is invalid style");
} }

View File

@ -102,7 +102,10 @@ ComputeDihedralLocal::ComputeDihedralLocal(LAMMPS *lmp, int narg, char **arg) :
if (pstr) { if (pstr) {
pvar = input->variable->find(pstr); pvar = input->variable->find(pstr);
if (pvar < 0) error->all(FLERR, "Variable name for compute dihedral/local does not exist"); if (pvar < 0) {
input->variable->internal_create(pstr,0.0);
pvar = input->variable->find(pstr);
}
if (!input->variable->internalstyle(pvar)) if (!input->variable->internalstyle(pvar))
error->all(FLERR, "Variable for compute dihedral/local is invalid style"); error->all(FLERR, "Variable for compute dihedral/local is invalid style");
} }

View File

@ -390,28 +390,23 @@ void CreateAtoms::command(int narg, char **arg)
if (!input->variable->equalstyle(vvar)) if (!input->variable->equalstyle(vvar))
error->all(FLERR, Error::NOLASTLINE, "Variable {} for create_atoms is invalid style", vstr); error->all(FLERR, Error::NOLASTLINE, "Variable {} for create_atoms is invalid style", vstr);
if (xstr) { #define SETUP_XYZ_VAR(str,var) \
xvar = input->variable->find(xstr); if (str) { \
if (xvar < 0) var = input->variable->find(str); \
error->all(FLERR, Error::NOLASTLINE, "Variable {} for create_atoms does not exist", xstr); if (var < 0) { \
if (!input->variable->internalstyle(xvar)) input->variable->internal_create(str, 0.0); \
error->all(FLERR, Error::NOLASTLINE, "Variable {} for create_atoms is invalid style", xstr); var = input->variable->find(str); \
} } \
if (ystr) { if (!input->variable->internalstyle(var)) \
yvar = input->variable->find(ystr); error->all(FLERR, Error::NOLASTLINE, \
if (yvar < 0) "Variable {} for create_atoms is invalid style", str); \
error->all(FLERR, Error::NOLASTLINE, "Variable {} for create_atoms does not exist", ystr);
if (!input->variable->internalstyle(yvar))
error->all(FLERR, Error::NOLASTLINE, "Variable {} for create_atoms is invalid style", ystr);
}
if (zstr) {
zvar = input->variable->find(zstr);
if (zvar < 0)
error->all(FLERR, Error::NOLASTLINE, "Variable {} for create_atoms does not exist", zstr);
if (!input->variable->internalstyle(zvar))
error->all(FLERR, Error::NOLASTLINE, "Variable {} for create_atoms is invalid style", zstr);
} }
SETUP_XYZ_VAR(xstr, xvar);
SETUP_XYZ_VAR(ystr, yvar);
SETUP_XYZ_VAR(zstr, zvar);
} }
#undef SETUP_XYZ_VAR
// require non-none lattice be defined for BOX or REGION styles // require non-none lattice be defined for BOX or REGION styles

View File

@ -868,19 +868,28 @@ void FixDeposit::options(int narg, char **arg)
if (xstr) { if (xstr) {
xvar = input->variable->find(xstr); xvar = input->variable->find(xstr);
if (xvar < 0) error->all(FLERR, "Variable {} for fix deposit does not exist", xstr); if (xvar < 0) {
input->variable->internal_create(xstr,0.0);
xvar = input->variable->find(xstr);
}
if (!input->variable->internalstyle(xvar)) if (!input->variable->internalstyle(xvar))
error->all(FLERR, "Variable for fix deposit is invalid style"); error->all(FLERR, "Variable for fix deposit is invalid style");
} }
if (ystr) { if (ystr) {
yvar = input->variable->find(ystr); yvar = input->variable->find(ystr);
if (yvar < 0) error->all(FLERR, "Variable {} for fix deposit does not exist", ystr); if (yvar < 0) {
input->variable->internal_create(ystr,0.0);
yvar = input->variable->find(ystr);
}
if (!input->variable->internalstyle(yvar)) if (!input->variable->internalstyle(yvar))
error->all(FLERR, "Variable for fix deposit is invalid style"); error->all(FLERR, "Variable for fix deposit is invalid style");
} }
if (zstr) { if (zstr) {
zvar = input->variable->find(zstr); zvar = input->variable->find(zstr);
if (zvar < 0) error->all(FLERR, "Variable {} for fix deposit does not exist", zstr); if (zvar < 0) {
input->variable->internal_create(zstr,0.0);
zvar = input->variable->find(zstr);
}
if (!input->variable->internalstyle(zvar)) if (!input->variable->internalstyle(zvar))
error->all(FLERR, "Variable for fix deposit is invalid style"); error->all(FLERR, "Variable for fix deposit is invalid style");
} }

View File

@ -14,9 +14,8 @@
#include "lmppython.h" #include "lmppython.h"
#if defined(LMP_PYTHON) #if defined(LMP_PYTHON)
#include "python_impl.h" #include "python_impl.h"
#else
#include "error.h"
#endif #endif
#include "error.h"
using namespace LAMMPS_NS; using namespace LAMMPS_NS;
@ -43,7 +42,7 @@ 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, Error::NOLASTLINE, "Python support missing! Compile with PYTHON package installed!");
#endif #endif
} }
@ -67,10 +66,10 @@ void Python::command(int narg, char **arg)
/* ------------------------------------------------------------------ */ /* ------------------------------------------------------------------ */
void Python::invoke_function(int ifunc, char *result) void Python::invoke_function(int ifunc, char *result, double *dvalue)
{ {
init(); init();
impl->invoke_function(ifunc, result); impl->invoke_function(ifunc, result, dvalue);
} }
/* ------------------------------------------------------------------ */ /* ------------------------------------------------------------------ */
@ -83,10 +82,19 @@ int Python::find(const char *name)
/* ------------------------------------------------------------------ */ /* ------------------------------------------------------------------ */
int Python::variable_match(const char *name, const char *varname, int numeric) int Python::function_match(const char *name, const char *varname, int numeric, Error *errptr)
{ {
init(); init();
return impl->variable_match(name, varname, numeric); return impl->function_match(name, varname, numeric, errptr);
}
/* ------------------------------------------------------------------ */
int Python::wrapper_match(const char *name, const char *varname, int narg, int *argvars,
Error *errptr)
{
init();
return impl->wrapper_match(name, varname, narg, argvars, errptr);
} }
/* ------------------------------------------------------------------ */ /* ------------------------------------------------------------------ */

View File

@ -22,9 +22,10 @@ class PythonInterface {
public: public:
virtual ~PythonInterface() noexcept(false) {} virtual ~PythonInterface() noexcept(false) {}
virtual void command(int, char **) = 0; virtual void command(int, char **) = 0;
virtual void invoke_function(int, char *) = 0; virtual void invoke_function(int, char *, double *) = 0;
virtual int find(const char *) = 0; virtual int find(const char *) = 0;
virtual int variable_match(const char *, const char *, int) = 0; virtual int function_match(const char *, const char *, int, Error *) = 0;
virtual int wrapper_match(const char *, const char *, int, int *, Error *) = 0;
virtual char *long_string(int ifunc) = 0; virtual char *long_string(int ifunc) = 0;
virtual int execute_string(char *) = 0; virtual int execute_string(char *) = 0;
virtual int execute_file(char *) = 0; virtual int execute_file(char *) = 0;
@ -37,9 +38,10 @@ class Python : protected Pointers {
~Python() override; ~Python() override;
void command(int, char **); void command(int, char **);
void invoke_function(int, char *); void invoke_function(int, char *, double *);
int find(const char *); int find(const char *);
int variable_match(const char *, const char *, int); int function_match(const char *, const char *, int, Error *);
int wrapper_match(const char *, const char *, int, int *, Error *);
char *long_string(int ifunc); char *long_string(int ifunc);
int execute_string(char *); int execute_string(char *);
int execute_file(char *); int execute_file(char *);

View File

@ -81,6 +81,7 @@ enum{DONE,ADD,SUBTRACT,MULTIPLY,DIVIDE,CARAT,MODULO,UNARY,
RAMP,STAGGER,LOGFREQ,LOGFREQ2,LOGFREQ3,STRIDE,STRIDE2, RAMP,STAGGER,LOGFREQ,LOGFREQ2,LOGFREQ3,STRIDE,STRIDE2,
VDISPLACE,SWIGGLE,CWIGGLE,SIGN,GMASK,RMASK, VDISPLACE,SWIGGLE,CWIGGLE,SIGN,GMASK,RMASK,
GRMASK,IS_ACTIVE,IS_DEFINED,IS_AVAILABLE,IS_FILE,EXTRACT_SETTING, GRMASK,IS_ACTIVE,IS_DEFINED,IS_AVAILABLE,IS_FILE,EXTRACT_SETTING,
PYWRAPPER,
VALUE,ATOMARRAY,TYPEARRAY,INTARRAY,BIGINTARRAY,VECTORARRAY}; VALUE,ATOMARRAY,TYPEARRAY,INTARRAY,BIGINTARRAY,VECTORARRAY};
// customize by adding a special function // customize by adding a special function
@ -116,6 +117,7 @@ Variable::Variable(LAMMPS *lmp) : Pointers(lmp)
num = nullptr; num = nullptr;
which = nullptr; which = nullptr;
pad = nullptr; pad = nullptr;
pyindex = nullptr;
reader = nullptr; reader = nullptr;
data = nullptr; data = nullptr;
dvalue = nullptr; dvalue = nullptr;
@ -162,6 +164,7 @@ Variable::~Variable()
memory->destroy(num); memory->destroy(num);
memory->destroy(which); memory->destroy(which);
memory->destroy(pad); memory->destroy(pad);
memory->destroy(pyindex);
memory->sfree(reader); memory->sfree(reader);
memory->sfree(data); memory->sfree(data);
memory->sfree(dvalue); memory->sfree(dvalue);
@ -594,6 +597,7 @@ void Variable::set(int narg, char **arg)
num[nvar] = 2; num[nvar] = 2;
which[nvar] = 1; which[nvar] = 1;
pad[nvar] = 0; pad[nvar] = 0;
pyindex[nvar] = -1;
data[nvar] = new char *[num[nvar]]; data[nvar] = new char *[num[nvar]];
data[nvar][0] = utils::strdup(arg[2]); data[nvar][0] = utils::strdup(arg[2]);
data[nvar][1] = new char[VALUELENGTH]; data[nvar][1] = new char[VALUELENGTH];
@ -956,13 +960,12 @@ void Variable::python_command(int narg, char **arg)
int Variable::equalstyle(int ivar) int Variable::equalstyle(int ivar)
{ {
if (style[ivar] == EQUAL || style[ivar] == TIMER || if (style[ivar] == EQUAL || style[ivar] == TIMER || style[ivar] == INTERNAL) return 1;
style[ivar] == INTERNAL) return 1;
if (style[ivar] == PYTHON) { if (style[ivar] == PYTHON) {
int ifunc = python->variable_match(data[ivar][0],names[ivar],1); pyindex[ivar] = python->function_match(data[ivar][0], names[ivar], 1, error);
if (ifunc < 0) return 0; if (pyindex[ivar] >= 0) return 1;
else return 1;
} }
return 0; return 0;
} }
@ -989,7 +992,7 @@ int Variable::vectorstyle(int ivar)
} }
/* ---------------------------------------------------------------------- /* ----------------------------------------------------------------------
check if variable with name is PYTHON and matches funcname check if variable with name is PYTHON style and matches funcname
called by Python class before it invokes a Python function called by Python class before it invokes a Python function
return data storage so Python function can return a value for this variable return data storage so Python function can return a value for this variable
return nullptr if not a match return nullptr if not a match
@ -1006,7 +1009,7 @@ char *Variable::pythonstyle(char *name, char *funcname)
/* ---------------------------------------------------------------------- /* ----------------------------------------------------------------------
return 1 if variable is INTERNAL style, 0 if not return 1 if variable is INTERNAL style, 0 if not
this is checked before call to set_internal() to assure it can be set this is checked before call to internal_set() to ensure it can be set
------------------------------------------------------------------------- */ ------------------------------------------------------------------------- */
int Variable::internalstyle(int ivar) int Variable::internalstyle(int ivar)
@ -1082,23 +1085,8 @@ char *Variable::retrieve(const char *name)
str = data[ivar][1] = utils::strdup(result); str = data[ivar][1] = utils::strdup(result);
} else if (style[ivar] == PYTHON) { } else if (style[ivar] == PYTHON) {
int ifunc = python->variable_match(data[ivar][0],name,0); int ifunc = python->function_match(data[ivar][0],name,0,error);
if (ifunc < 0) { python->invoke_function(ifunc,data[ivar][1],nullptr);
if (ifunc == -1) {
error->all(FLERR, "Could not find Python function {} linked to variable {}",
data[ivar][0], name);
} else if (ifunc == -2) {
error->all(FLERR, "Python function {} for variable {} does not have a return value",
data[ivar][0], name);
} else if (ifunc == -3) {
error->all(FLERR,"Python variable {} does not match variable name registered with "
"Python function {}", name, data[ivar][0]);
} else {
error->all(FLERR, "Unknown error verifying function {} linked to python style variable {}",
data[ivar][0],name);
}
}
python->invoke_function(ifunc,data[ivar][1]);
str = data[ivar][1]; str = data[ivar][1];
// if Python func returns a string longer than VALUELENGTH // if Python func returns a string longer than VALUELENGTH
@ -1157,17 +1145,7 @@ double Variable::compute_equal(int ivar)
if (style[ivar] == EQUAL) value = evaluate(data[ivar][0],nullptr,ivar); if (style[ivar] == EQUAL) value = evaluate(data[ivar][0],nullptr,ivar);
else if (style[ivar] == TIMER) value = dvalue[ivar]; else if (style[ivar] == TIMER) value = dvalue[ivar];
else if (style[ivar] == INTERNAL) value = dvalue[ivar]; else if (style[ivar] == INTERNAL) value = dvalue[ivar];
else if (style[ivar] == PYTHON) { else if (style[ivar] == PYTHON) python->invoke_function(pyindex[ivar],nullptr,&value);
int ifunc = python->find(data[ivar][0]);
if (ifunc < 0)
print_var_error(FLERR,fmt::format("cannot find python function {}",data[ivar][0]),ivar);
python->invoke_function(ifunc,data[ivar][1]);
try {
value = std::stod(data[ivar][1]);
} catch (std::exception &e) {
print_var_error(FLERR,"has an invalid value", ivar);
}
}
// round to zero on underflow // round to zero on underflow
if (fabs(value) < std::numeric_limits<double>::min()) value = 0.0; if (fabs(value) < std::numeric_limits<double>::min()) value = 0.0;
@ -1334,6 +1312,30 @@ void Variable::internal_set(int ivar, double value)
dvalue[ivar] = value; dvalue[ivar] = value;
} }
/* ----------------------------------------------------------------------
create an INTERNAL style variable with name, set to value
------------------------------------------------------------------------- */
void Variable::internal_create(char *name, double value)
{
if (find(name) >= 0)
error->all(FLERR,"Creation of internal-style variable {} which already exists", name);
if (nvar == maxvar) grow();
style[nvar] = INTERNAL;
num[nvar] = 1;
which[nvar] = 0;
pad[nvar] = 0;
data[nvar] = new char *[num[nvar]];
data[nvar][0] = new char[VALUELENGTH];
dvalue[nvar] = value;
if (!utils::is_id(name))
error->all(FLERR, "Variable name '{}' must have only letters, numbers, or underscores", name);
names[nvar] = utils::strdup(name);
nvar++;
}
/* ---------------------------------------------------------------------- /* ----------------------------------------------------------------------
remove Nth variable from list and compact list remove Nth variable from list and compact list
delete reader explicitly if it exists delete reader explicitly if it exists
@ -1381,6 +1383,7 @@ void Variable::grow()
memory->grow(num,maxvar,"var:num"); memory->grow(num,maxvar,"var:num");
memory->grow(which,maxvar,"var:which"); memory->grow(which,maxvar,"var:which");
memory->grow(pad,maxvar,"var:pad"); memory->grow(pad,maxvar,"var:pad");
memory->grow(pyindex,maxvar,"var:pyindex");
reader = (VarReader **) reader = (VarReader **)
memory->srealloc(reader,maxvar*sizeof(VarReader *),"var:reader"); memory->srealloc(reader,maxvar*sizeof(VarReader *),"var:reader");
@ -1424,6 +1427,7 @@ void Variable::copy(int narg, char **from, char **to)
sin(x),cos(x),tan(x),asin(x),atan2(y,x),... sin(x),cos(x),tan(x),asin(x),atan2(y,x),...
group function = count(group), mass(group), xcm(group,x), ... group function = count(group), mass(group), xcm(group,x), ...
special function = sum(x),min(x), ... special function = sum(x),min(x), ...
python function wrapper = py_varname(x,y,z,...) (up to MAXFUNCARG)
atom value = x[i], y[i], vx[i], ... atom value = x[i], y[i], vx[i], ...
atom vector = x, y, vx, ... atom vector = x, y, vx, ...
custom atom property = i/d_name, i/d_name[i], i/d2_name[i], i/d2_name[i][j] custom atom property = i/d_name, i/d_name[i], i/d2_name[i], i/d2_name[i][j]
@ -2361,13 +2365,14 @@ double Variable::evaluate(char *str, Tree **tree, int ivar)
} }
// ---------------- // ----------------
// math/group/special/labelmap function or atom value/vector or constant or thermo keyword // math/group/region/special/feature function or atom value/vector or constant or thermo keyword
// ---------------- // ----------------
} else { } else {
// ---------------- // ----------------
// math or group or special function // math or group/region or special or feature function
// math_function() includes Python function wrapper
// ---------------- // ----------------
if (str[i] == '(') { if (str[i] == '(') {
@ -2625,7 +2630,8 @@ double Variable::evaluate(char *str, Tree **tree, int ivar)
atan2(y,x),random(x,y,z),normal(x,y,z),ceil(),floor(),round(),ternary(x,y,z), atan2(y,x),random(x,y,z),normal(x,y,z),ceil(),floor(),round(),ternary(x,y,z),
ramp(x,y),stagger(x,y),logfreq(x,y,z),logfreq2(x,y,z), ramp(x,y),stagger(x,y),logfreq(x,y,z),logfreq2(x,y,z),
logfreq3(x,y,z),stride(x,y,z),stride2(x,y,z,a,b,c),vdisplace(x,y),swiggle(x,y,z), logfreq3(x,y,z),stride(x,y,z),stride2(x,y,z,a,b,c),vdisplace(x,y),swiggle(x,y,z),
cwiggle(x,y,z),sign(x),gmask(x),rmask(x),grmask(x,y) cwiggle(x,y,z),sign(x),py_varname(x,y,z,...),
gmask(x),rmask(x),grmask(x,y)
---------------------------------------------------------------------- */ ---------------------------------------------------------------------- */
double Variable::collapse_tree(Tree *tree) double Variable::collapse_tree(Tree *tree)
@ -3183,6 +3189,30 @@ double Variable::collapse_tree(Tree *tree)
return tree->value; return tree->value;
} }
if (tree->type == PYWRAPPER) {
int narg = tree->argcount;
int *argvars = tree->argvars;
double arg;
for (int iarg = 0; iarg < narg; iarg++) {
if (iarg == 0) arg = collapse_tree(tree->first);
else if (iarg == 1) arg = collapse_tree(tree->second);
else arg = collapse_tree(tree->extra[iarg-2]);
internal_set(argvars[iarg],arg);
}
for (int iarg = 0; iarg < narg; iarg++) {
if (iarg == 0) {
if (tree->first->type != VALUE) return 0.0;
} else if (iarg == 1) {
if (tree->second->type != VALUE) return 0.0;
} else {
if (tree->extra[iarg-2]->type != VALUE) return 0.0;
}
}
tree->type = VALUE;
tree->value = compute_equal(tree->pyvar);
return tree->value;
}
// mask functions do not become a single collapsed value // mask functions do not become a single collapsed value
if (tree->type == GMASK) return 0.0; if (tree->type == GMASK) return 0.0;
@ -3196,12 +3226,14 @@ double Variable::collapse_tree(Tree *tree)
evaluate an atom-style or vector-style variable parse tree evaluate an atom-style or vector-style variable parse tree
index I = atom I or vector index I index I = atom I or vector index I
tree was created by one-time parsing of formula string via evaluate() tree was created by one-time parsing of formula string via evaluate()
followed by collapse_tree() operation to streamline tree as much as possible
customize by adding a function: customize by adding a function:
sqrt(),exp(),ln(),log(),sin(),cos(),tan(),asin(),acos(),atan(), sqrt(),exp(),ln(),log(),sin(),cos(),tan(),asin(),acos(),atan(),
atan2(y,x),random(x,y,z),normal(x,y,z),ceil(),floor(),round(),ternary(x,y,z), atan2(y,x),random(x,y,z),normal(x,y,z),ceil(),floor(),round(),ternary(x,y,z),
ramp(x,y),stagger(x,y),logfreq(x,y,z),logfreq2(x,y,z), ramp(x,y),stagger(x,y),logfreq(x,y,z),logfreq2(x,y,z),
logfreq3(x,y,z),stride(x,y,z),stride2(x,y,z,a,b,c),vdisplace(x,y), logfreq3(x,y,z),stride(x,y,z),stride2(x,y,z,a,b,c),vdisplace(x,y),
swiggle(x,y,z),cwiggle(x,y,z),sign(x),gmask(x),rmask(x),grmask(x,y) swiggle(x,y,z),cwiggle(x,y,z),sign(x),py_varname(x,y,z,...),
gmask(x),rmask(x),grmask(x,y)
---------------------------------------------------------------------- */ ---------------------------------------------------------------------- */
double Variable::eval_tree(Tree *tree, int i) double Variable::eval_tree(Tree *tree, int i)
@ -3520,6 +3552,18 @@ double Variable::eval_tree(Tree *tree, int i)
if (tree->type == SIGN) if (tree->type == SIGN)
return (eval_tree(tree->first,i) >= 0.0) ? 1.0 : -1.0; // sign(eval_tree(tree->first,i)); return (eval_tree(tree->first,i) >= 0.0) ? 1.0 : -1.0; // sign(eval_tree(tree->first,i));
if (tree->type == PYWRAPPER) {
int narg = tree->argcount;
for (int iarg = 0; iarg < narg; iarg++) {
if (iarg == 0) arg = eval_tree(tree->first,i);
else if (iarg == 1) arg = eval_tree(tree->second,i);
else arg = eval_tree(tree->extra[iarg-2],i);
internal_set(tree->argvars[iarg],arg);
}
arg = compute_equal(tree->pyvar);
return arg;
}
if (tree->type == GMASK) { if (tree->type == GMASK) {
if (atom->mask[i] & tree->ivalue) return 1.0; if (atom->mask[i] & tree->ivalue) return 1.0;
else return 0.0; else return 0.0;
@ -3583,6 +3627,7 @@ void Variable::free_tree(Tree *tree)
for (int i = 0; i < tree->nextra; i++) free_tree(tree->extra[i]); for (int i = 0; i < tree->nextra; i++) free_tree(tree->extra[i]);
delete[] tree->extra; delete[] tree->extra;
} }
if (tree->argvars) delete[] tree->argvars;
if (tree->selfalloc) memory->destroy(tree->array); if (tree->selfalloc) memory->destroy(tree->array);
delete tree; delete tree;
@ -3685,7 +3730,7 @@ tagint Variable::int_between_brackets(char *&ptr, int varallow)
/* ---------------------------------------------------------------------- /* ----------------------------------------------------------------------
process a math function in formula process a math function in formula
push result onto tree or arg stack push result onto tree or arg stack
word = math function word = math function name
contents = str between parentheses with comma-separated args contents = str between parentheses with comma-separated args
return 0 if not a match, 1 if successfully processed return 0 if not a match, 1 if successfully processed
customize by adding a math function: customize by adding a math function:
@ -3693,7 +3738,7 @@ tagint Variable::int_between_brackets(char *&ptr, int varallow)
atan2(y,x),random(x,y,z),normal(x,y,z),ceil(),floor(),round(),ternary(), atan2(y,x),random(x,y,z),normal(x,y,z),ceil(),floor(),round(),ternary(),
ramp(x,y),stagger(x,y),logfreq(x,y,z),logfreq2(x,y,z), ramp(x,y),stagger(x,y),logfreq(x,y,z),logfreq2(x,y,z),
logfreq3(x,y,z),stride(x,y,z),stride2(x,y,z,a,b,c),vdisplace(x,y), logfreq3(x,y,z),stride(x,y,z),stride2(x,y,z,a,b,c),vdisplace(x,y),
swiggle(x,y,z),cwiggle(x,y,z),sign(x) swiggle(x,y,z),cwiggle(x,y,z),sign(x),py_varname(x,y,z,...)
------------------------------------------------------------------------- */ ------------------------------------------------------------------------- */
int Variable::math_function(char *word, char *contents, Tree **tree, Tree **treestack, int Variable::math_function(char *word, char *contents, Tree **tree, Tree **treestack,
@ -3711,7 +3756,8 @@ int Variable::math_function(char *word, char *contents, Tree **tree, Tree **tree
strcmp(word,"logfreq") != 0 && strcmp(word,"logfreq2") != 0 && strcmp(word,"logfreq") != 0 && strcmp(word,"logfreq2") != 0 &&
strcmp(word,"logfreq3") != 0 && strcmp(word,"stride") != 0 && strcmp(word,"logfreq3") != 0 && strcmp(word,"stride") != 0 &&
strcmp(word,"stride2") != 0 && strcmp(word,"vdisplace") != 0 && strcmp(word,"stride2") != 0 && strcmp(word,"vdisplace") != 0 &&
strcmp(word,"swiggle") != 0 && strcmp(word,"cwiggle") != 0 && strcmp(word,"sign") != 0) strcmp(word,"swiggle") != 0 && strcmp(word,"cwiggle") != 0 && strcmp(word,"sign") != 0 &&
strstr(word,"py_") != word)
return 0; return 0;
// parse contents for comma-separated args // parse contents for comma-separated args
@ -4106,11 +4152,51 @@ int Variable::math_function(char *word, char *contents, Tree **tree, Tree **tree
double value = value1 + value2*(1.0-cos(omega*delta*update->dt)); double value = value1 + value2*(1.0-cos(omega*delta*update->dt));
argstack[nargstack++] = value; argstack[nargstack++] = value;
} }
} else if (strcmp(word,"sign") == 0) { } else if (strcmp(word,"sign") == 0) {
if (narg != 1) if (narg != 1)
print_var_error(FLERR,"Invalid math function in variable formula",ivar); print_var_error(FLERR,"Invalid math function in variable formula",ivar);
if (tree) newtree->type = SIGN; if (tree) newtree->type = SIGN;
else argstack[nargstack++] = (value1 >= 0.0) ? 1.0 : -1.0; // sign(value1); else argstack[nargstack++] = (value1 >= 0.0) ? 1.0 : -1.0; // sign(value1);
// Python wrapper function tied to python-style variable
// text following py_ = python-style variable name tied to Python function
// narg arguments are tied to internal variables defined by python command
} else if (strstr(word,"py_") == word) {
// pyvar = index of python-style variable which invokes Python function
int pyvar = find(&word[3]);
if (style[pyvar] != PYTHON)
print_var_error(FLERR,"Invalid python function variable name",ivar);
// check that wrapper matches Python function
// jvars = returned indices of narg internal variables used by Python function
int *jvars = new int[narg];
pyindex[pyvar] = python->wrapper_match(data[pyvar][0],names[pyvar],narg,jvars,error);
// if tree: store python variable and arg info in tree for later eval
// else: one-time eval of python-coded function now via python variable
if (tree) {
newtree->type = PYWRAPPER;
newtree->pyvar = pyvar;
newtree->argcount = narg;
newtree->argvars = new int[narg];
for (int iarg = 0; iarg < narg; iarg++)
newtree->argvars[iarg] = jvars[iarg];
} else {
for (int iarg = 0; iarg < narg; iarg++) {
if (iarg == 0) internal_set(jvars[iarg],value1);
else if (iarg == 1) internal_set(jvars[iarg],value2);
else internal_set(jvars[iarg],values[iarg-2]);
}
argstack[nargstack++] = compute_equal(pyvar);
}
delete[] jvars;
} }
// delete stored args // delete stored args
@ -4377,7 +4463,7 @@ Region *Variable::region_function(char *id, int ivar)
customize by adding a special function: customize by adding a special function:
sum(x),min(x),max(x),ave(x),trap(x),slope(x), sum(x),min(x),max(x),ave(x),trap(x),slope(x),
gmask(x),rmask(x),grmask(x,y),next(x),is_file(x),is_os(x), gmask(x),rmask(x),grmask(x,y),next(x),is_file(x),is_os(x),
extract_setting(x),label2type(x,y),is_tpelabel(x,y) extract_setting(x),label2type(x,y),is_typelabel(x,y)
is_timeout() is_timeout()
------------------------------------------------------------------------- */ ------------------------------------------------------------------------- */
@ -5296,7 +5382,11 @@ void Variable::print_var_error(const std::string &srcfile, const int lineno,
void Variable::print_tree(Tree *tree, int level) void Variable::print_tree(Tree *tree, int level)
{ {
if (tree->type == VALUE) {
printf("TREE %d: %d %g\n",level,tree->type,tree->value); printf("TREE %d: %d %g\n",level,tree->type,tree->value);
return;
}
printf("TREE %d: %d\n",level,tree->type);
if (tree->first) print_tree(tree->first,level+1); if (tree->first) print_tree(tree->first,level+1);
if (tree->second) print_tree(tree->second,level+1); if (tree->second) print_tree(tree->second,level+1);
if (tree->nextra) if (tree->nextra)

View File

@ -49,6 +49,7 @@ class Variable : protected Pointers {
void compute_atom(int, int, double *, int, int); void compute_atom(int, int, double *, int, int);
int compute_vector(int, double **); int compute_vector(int, double **);
void internal_set(int, double); void internal_set(int, double);
void internal_create(char *, double);
tagint int_between_brackets(char *&, int); tagint int_between_brackets(char *&, int);
double evaluate_boolean(char *); double evaluate_boolean(char *);
@ -87,6 +88,7 @@ class Variable : protected Pointers {
int *num; // # of values for each variable int *num; // # of values for each variable
int *which; // next available value for each variable int *which; // next available value for each variable
int *pad; // 1 = pad loop/uloop variables with 0s, 0 = no pad int *pad; // 1 = pad loop/uloop variables with 0s, 0 = no pad
int *pyindex; // indices to Python funcs for python-style vars
class VarReader **reader; // variable that reads from file class VarReader **reader; // variable that reads from file
char ***data; // str value of each variable's values char ***data; // str value of each variable's values
double *dvalue; // single numeric value for internal variables double *dvalue; // single numeric value for internal variables
@ -123,9 +125,13 @@ class Variable : protected Pointers {
Tree *first, *second; // ptrs further down tree for first 2 args Tree *first, *second; // ptrs further down tree for first 2 args
Tree **extra; // ptrs further down tree for nextra args Tree **extra; // ptrs further down tree for nextra args
int pyvar; // index of Python variable invoked as py_name()
int argcount; // # of args to associated Python function
int *argvars; // indices of internal variables for each arg
Tree() : Tree() :
array(nullptr), iarray(nullptr), barray(nullptr), selfalloc(0), ivalue(0), nextra(0), array(nullptr), iarray(nullptr), barray(nullptr), selfalloc(0), ivalue(0), nextra(0),
region(nullptr), first(nullptr), second(nullptr), extra(nullptr) region(nullptr), first(nullptr), second(nullptr), extra(nullptr), argvars(nullptr)
{ {
} }
}; };

View File

@ -85,7 +85,7 @@ void WriteDump::command(int narg, char **arg)
if (strcmp(arg[1], "image") == 0) (dynamic_cast<DumpImage *>(dump))->multifile_override = 1; if (strcmp(arg[1], "image") == 0) (dynamic_cast<DumpImage *>(dump))->multifile_override = 1;
if (strcmp(arg[1], "cfg") == 0) (dynamic_cast<DumpCFG *>(dump))->multifile_override = 1; if (strcmp(arg[1], "cfg") == 0) (dynamic_cast<DumpCFG *>(dump))->multifile_override = 1;
if ((update->first_update == 0) && (comm->me == 0) && (noinitwarn == 0)) if ((update->first_update == 0) && (comm->me == 0) && (noinitwarn == 0))
error->warning(FLERR, "Calling write_dump before a full system init."); error->warning(FLERR, "Calling write_dump before a full system init");
dump->init(); dump->init();
dump->write(); dump->write();