Merge pull request #3767 from lammps/vector-variable

Vector-style variable enhancements
This commit is contained in:
Axel Kohlmeyer
2023-05-12 15:18:08 -04:00
committed by GitHub
6 changed files with 617 additions and 390 deletions

View File

@ -44,6 +44,20 @@ one word. If it contains variables it must be enclosed in double
quotes to ensure they are not evaluated when the input script line is
read, but will instead be evaluated each time the string is printed.
.. versionadded:: TBD
support for vector style variables
See the :doc:`variable <variable>` command for a description of
*equal* and *vector* style variables which are typically the most
useful ones to use with the print command. Equal- and vector-style
variables can calculate formulas involving mathematical operations,
atom properties, group properties, thermodynamic properties, global
values calculated by a :doc:`compute <compute>` or :doc:`fix <fix>`,
or references to other :doc:`variables <variable>`. Vector-style
variables are printed in a bracketed, comma-separated format,
e.g. [1,2,3,4] or [12.5,2,4.6,10.1].
.. note::
As discussed on the :doc:`Commands parse <Commands_parse>` doc
@ -77,15 +91,6 @@ timesteps 10,20,30,100,200,300,1000,2000,etc:
The specified group-ID is ignored by this fix.
See the :doc:`variable <variable>` command for a description of
*equal* style variables which are the most useful ones to use with the
fix print command, since they are evaluated afresh each timestep that
the fix print line is output. Equal-style variables calculate
formulas involving mathematical operations, atom properties, group
properties, thermodynamic properties, global values calculated by a
:doc:`compute <compute>` or :doc:`fix <fix>`, or references to other
:doc:`variables <variable>`.
If the *file* or *append* keyword is used, a filename is specified to
which the output generated by this fix will be written. If *file* is
used, then the filename is overwritten if it already exists. If

View File

@ -46,6 +46,20 @@ lines of output, the string can be enclosed in triple quotes, as in
the last example above. If the text string contains variables, they
will be evaluated and their current values printed.
.. versionadded:: TBD
support for vector style variables
See the :doc:`variable <variable>` command for a description of
*equal* and *vector* style variables which are typically the most
useful ones to use with the print command. Equal- and vector-style
variables can calculate formulas involving mathematical operations,
atom properties, group properties, thermodynamic properties, global
values calculated by a :doc:`compute <compute>` or :doc:`fix <fix>`,
or references to other :doc:`variables <variable>`. Vector-style
variables are printed in a bracketed, comma-separated format,
e.g. [1,2,3,4] or [12.5,2,4.6,10.1].
.. note::
As discussed on the :doc:`Commands parse <Commands_parse>` doc
@ -60,6 +74,15 @@ will be evaluated and their current values printed.
This is also explained on the :doc:`Commands parse
<Commands_parse>` doc page.
If you want the print command to be executed multiple times (with
changing variable values), there are 3 options. First, consider using
the :doc:`fix print <fix_print>` command, which will print a string
periodically during a simulation. Second, the print command can be
used as an argument to the *every* option of the :doc:`run <run>`
command. Third, the print command could appear in a section of the
input script that is looped over (see the :doc:`jump <jump>` and
:doc:`next <next>` commands).
If the *file* or *append* keyword is used, a filename is specified to
which the output will be written. If *file* is used, then the
filename is overwritten if it already exists. If *append* is used,
@ -74,23 +97,6 @@ logfile can be turned on or off as desired. In multi-partition
calculations, the *screen* option and the corresponding output only
apply to the screen and logfile of the individual partition.
If you want the print command to be executed multiple times (with
changing variable values), there are 3 options. First, consider using
the :doc:`fix print <fix_print>` command, which will print a string
periodically during a simulation. Second, the print command can be
used as an argument to the *every* option of the :doc:`run <run>`
command. Third, the print command could appear in a section of the
input script that is looped over (see the :doc:`jump <jump>` and
:doc:`next <next>` commands).
See the :doc:`variable <variable>` command for a description of *equal*
style variables which are typically the most useful ones to use with
the print command. Equal-style variables can calculate formulas
involving mathematical operations, atom properties, group properties,
thermodynamic properties, global values calculated by a
:doc:`compute <compute>` or :doc:`fix <fix>`, or references to other
:doc:`variables <variable>`.
Restrictions
""""""""""""
none

View File

@ -11,12 +11,19 @@ Syntax
variable name style args ...
* name = name of variable to define
* style = *delete* or *index* or *loop* or *world* or *universe* or *uloop* or *string* or *format* or *getenv* or *file* or *atomfile* or *python* or *timer* or *internal* or *equal* or *vector* or *atom*
* style = *delete* or *atomfile* or *file* or *format* or *getenv* or *index* or *internal* or *loop* or *python* or *string* or *timer* or *uloop* or *universe* or *world* or *equal* or *vector* or *atom*
.. parsed-literal::
*delete* = no args
*atomfile* arg = filename
*file* arg = filename
*format* args = vname fstr
vname = name of equal-style variable to evaluate
fstr = C-style format string
*getenv* arg = one string
*index* args = one or more strings
*internal* arg = numeric value
*loop* args = N
N = integer size of loop, loop from 1 to N inclusive
*loop* args = N pad
@ -27,24 +34,18 @@ Syntax
*loop* args = N1 N2 pad
N1,N2 = loop from N1 to N2 inclusive
pad = all values will be same length, e.g. 050, 051, ..., 100
*world* args = one string for each partition of processors
*universe* args = one or more strings
*python* arg = function
*string* arg = one string
*timer* arg = no arguments
*uloop* args = N
N = integer size of loop
*uloop* args = N pad
N = integer size of loop
pad = all values will be same length, e.g. 001, 002, ..., 100
*string* arg = one string
*format* args = vname fstr
vname = name of equal-style variable to evaluate
fstr = C-style format string
*getenv* arg = one string
*file* arg = filename
*atomfile* arg = filename
*python* arg = function
*timer* arg = no arguments
*internal* arg = numeric value
*equal* or *vector* or *atom* args = one formula containing numbers, thermo keywords, math operations, group functions, atom values and vectors, compute/fix/variable references
*universe* args = one or more strings
*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
numbers = 0.0, 100, -5.4, 2.8e-4, etc
constants = PI, version, on, off, true, false, yes, no
thermo keywords = vol, ke, press, etc from :doc:`thermo_style <thermo_style>`
@ -67,12 +68,13 @@ Syntax
angmom(group,dim,region), torque(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), gmask(x), rmask(x), grmask(x,y), next(x), is_file(name), is_os(name), extract_setting(name), label2type(kind,label)
feature functions = is_active(category,feature), is_available(category,feature), is_defined(category,id)
feature functions = is_available(category,feature), is_active(category,feature), is_defined(category,id)
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
compute references = c_ID, c_ID[i], c_ID[i][j], C_ID, C_ID[i]
fix references = f_ID, f_ID[i], f_ID[i][j], F_ID, F_ID[i]
variable references = v_name, v_name[i]
vector initialization = [1,3,7,10] (for *vector* variables only)
Examples
""""""""
@ -95,6 +97,7 @@ Examples
variable x universe 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
variable x uloop 15 pad
variable str format x %.6g
variable myvec vector [1,3,7,10]
variable x delete
.. code-block:: LAMMPS
@ -252,9 +255,10 @@ commands before the variable would become exhausted. For example,
----------
This section describes how all the various variable styles are defined
and what they store. Except for the *equal* and *vector* and *atom*
styles, which are explained in the next section.
The next sections describe in how all the various variable styles are
defined and what they store. The styles are listed alphabetically,
except for the *equal* and *vector* and *atom* styles, which are
explained together after all the others.
Many of the styles store one or more strings. Note that a single
string can contain spaces (multiple words), if it is enclosed in
@ -262,111 +266,7 @@ quotes in the variable command. When the variable is substituted for
in another input script command, its returned string will then be
interpreted as multiple arguments in the expanded command.
For the *index* style, one or more strings are specified. Initially,
the first string is assigned to the variable. Each time a
:doc:`next <next>` command is used with the variable name, the next
string is assigned. All processors assign the same string to the
variable.
Index-style variables with a single string value can also be set by
using the :doc:`command-line switch -var <Run_options>`.
The *loop* style is identical to the *index* style except that the
strings are the integers from 1 to N inclusive, if only one argument N
is specified. This allows generation of a long list of runs
(e.g. 1000) without having to list N strings in the input script.
Initially, the string "1" is assigned to the variable. Each time a
:doc:`next <next>` command is used with the variable name, the next
string ("2", "3", etc) is assigned. All processors assign the same
string to the variable. The *loop* style can also be specified with
two arguments N1 and N2. In this case the loop runs from N1 to N2
inclusive, and the string N1 is initially assigned to the variable.
N1 <= N2 and N2 >= 0 is required.
For the *world* style, one or more strings are specified. There must
be one string for each processor partition or "world". LAMMPS can be
run with multiple partitions via the :doc:`-partition command-line
switch <Run_options>`. This variable command assigns one string to
each world. All processors in the world are assigned the same string.
The next command cannot be used with equal-style variables, since
there is only one value per world. This style of variable is useful
when you wish to run different simulations on different partitions, or
when performing a parallel tempering simulation (see the :doc:`temper
<temper>` command), to assign different temperatures to different
partitions.
For the *universe* style, one or more strings are specified. There
must be at least as many strings as there are processor partitions or
"worlds". LAMMPS can be run with multiple partitions via the
:doc:`-partition command-line switch <Run_options>`. This variable
command initially assigns one string to each world. When a
:doc:`next <next>` command is encountered using this variable, the first
processor partition to encounter it, is assigned the next available
string. This continues until all the variable strings are consumed.
Thus, this command can be used to run 50 simulations on 8 processor
partitions. The simulations will be run one after the other on
whatever partition becomes available, until they are all finished.
Universe-style variables are incremented using the files
"tmp.lammps.variable" and "tmp.lammps.variable.lock" which you will
see in your directory during such a LAMMPS run.
The *uloop* style is identical to the *universe* style except that the
strings are the integers from 1 to N. This allows generation of long
list of runs (e.g. 1000) without having to list N strings in the input
script.
For the *string* style, a single string is assigned to the variable.
Two differences between this style and using the *index* style exist:
a variable with *string* style can be redefined, e.g. by another command later
in the input script, or if the script is read again in a loop. The other
difference is that *string* performs variable substitution even if the
string parameter is quoted.
For the *format* style, an equal-style or compatible variable is
specified along with a C-style format string, e.g. "%f" or "%.10g",
which must be appropriate for formatting a double-precision
floating-point value and may not have extra characters. The default
format is "%.15g". This variable style allows an equal-style variable
to be formatted precisely when it is evaluated.
Note that if you simply wish to print a variable value with desired
precision to the screen or logfile via the :doc:`print <print>` or
:doc:`fix print <fix_print>` commands, you can also do this by
specifying an "immediate" variable with a trailing colon and format
string, as part of the string argument of those commands. This is
explained on the :doc:`Commands parse <Commands_parse>` doc page.
For the *getenv* style, a single string is assigned to the variable
which should be the name of an environment variable. When the
variable is evaluated, it returns the value of the environment
variable, or an empty string if it not defined. This style of
variable can be used to adapt the behavior of LAMMPS input scripts via
environment variable settings, or to retrieve information that has
been previously stored with the :doc:`shell putenv <shell>` command.
Note that because environment variable settings are stored by the
operating systems, they persist even if the corresponding *getenv*
style variable is deleted, and also are set for sub-shells executed
by the :doc:`shell <shell>` command.
For the *file* style, a filename is provided which contains a list of
strings to assign to the variable, one per line. The strings can be
numeric values if desired. See the discussion of the next() function
below for equal-style variables, which will convert the string of a
file-style variable into a numeric value in a formula.
When a file-style variable is defined, the file is opened and the
string on the first line is read and stored with the variable. This
means the variable can then be evaluated as many times as desired and
will return that string. There are two ways to cause the next string
from the file to be read: use the :doc:`next <next>` command or the
next() function in an equal- or atom-style variable, as discussed
below.
The rules for formatting the file are as follows. A comment character
"#" can be used anywhere on a line; text starting with the comment
character is stripped. Blank lines are skipped. The first "word" of
a non-blank line, delimited by white-space, is the "string" assigned
to the variable.
----------
For the *atomfile* style, a filename is provided which contains one or
more sets of values, to assign on a per-atom basis to the variable.
@ -406,6 +306,97 @@ will be assigned to that atom. IDs can be listed in any order.
atoms is first set to 0.0. Thus values for atoms whose ID does not
appear in the set, will remain 0.0.
----------
For the *file* style, a filename is provided which contains a list of
strings to assign to the variable, one per line. The strings can be
numeric values if desired. See the discussion of the next() function
below for equal-style variables, which will convert the string of a
file-style variable into a numeric value in a formula.
When a file-style variable is defined, the file is opened and the
string on the first line is read and stored with the variable. This
means the variable can then be evaluated as many times as desired and
will return that string. There are two ways to cause the next string
from the file to be read: use the :doc:`next <next>` command or the
next() function in an equal- or atom-style variable, as discussed
below.
The rules for formatting the file are as follows. A comment character
"#" can be used anywhere on a line; text starting with the comment
character is stripped. Blank lines are skipped. The first "word" of
a non-blank line, delimited by white-space, is the "string" assigned
to the variable.
----------
For the *format* style, an equal-style or compatible variable is
specified along with a C-style format string, e.g. "%f" or "%.10g",
which must be appropriate for formatting a double-precision
floating-point value and may not have extra characters. The default
format is "%.15g". This variable style allows an equal-style variable
to be formatted precisely when it is evaluated.
Note that if you simply wish to print a variable value with desired
precision to the screen or logfile via the :doc:`print <print>` or
:doc:`fix print <fix_print>` commands, you can also do this by
specifying an "immediate" variable with a trailing colon and format
string, as part of the string argument of those commands. This is
explained on the :doc:`Commands parse <Commands_parse>` doc page.
----------
For the *getenv* style, a single string is assigned to the variable
which should be the name of an environment variable. When the
variable is evaluated, it returns the value of the environment
variable, or an empty string if it not defined. This style of
variable can be used to adapt the behavior of LAMMPS input scripts via
environment variable settings, or to retrieve information that has
been previously stored with the :doc:`shell putenv <shell>` command.
Note that because environment variable settings are stored by the
operating systems, they persist even if the corresponding *getenv*
style variable is deleted, and also are set for sub-shells executed
by the :doc:`shell <shell>` command.
----------
For the *index* style, one or more strings are specified. Initially,
the first string is assigned to the variable. Each time a
:doc:`next <next>` command is used with the variable name, the next
string is assigned. All processors assign the same string to the
variable.
Index-style variables with a single string value can also be set by
using the :doc:`command-line switch -var <Run_options>`.
----------
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
value. There are currently only two LAMMPS commands that require
*internal* variables as inputs, because they reset them:
:doc:`create_atoms <create_atoms>` and :doc:`fix controller
<fix_controller>`. As mentioned above, an internal-style variable can
be used in place of an equal-style variable anywhere else in an input
script, e.g. as an argument to another command that allows for
equal-style variables.
----------
The *loop* style is identical to the *index* style except that the
strings are the integers from 1 to N inclusive, if only one argument N
is specified. This allows generation of a long list of runs
(e.g. 1000) without having to list N strings in the input script.
Initially, the string "1" is assigned to the variable. Each time a
:doc:`next <next>` command is used with the variable name, the next
string ("2", "3", etc) is assigned. All processors assign the same
string to the variable. The *loop* style can also be specified with
two arguments N1 and N2. In this case the loop runs from N1 to N2
inclusive, and the string N1 is initially assigned to the variable.
N1 <= N2 and N2 >= 0 is required.
----------
For the *python* style a Python function name is provided. This needs
to match a function name specified in a :doc:`python <python>` command
which returns a value to this variable as defined by its *return*
@ -433,25 +424,52 @@ 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
that allows for equal-style variables.
For the *timer* style no additional argument is specified. The value of
the variable is set by querying the current elapsed wall time of the
simulation. This is done at the point in time when the variable is
defined in the input script. If a second timer-style variable is also
defined, then a simple formula can be used to calculate the elapsed time
between the two timers, as in the example at the top of this manual
entry. As mentioned above, timer-style variables can be redefined
elsewhere in the input script, so the same pair of variables can be used
in a loop or to time a series of operations.
----------
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
value. There are currently only two LAMMPS commands that require
*internal* variables as inputs, because they reset them:
:doc:`create_atoms <create_atoms>` and :doc:`fix controller
<fix_controller>`. As mentioned above, an internal-style variable can
be used in place of an equal-style variable anywhere else in an input
script, e.g. as an argument to another command that allows for
equal-style variables.
For the *string* style, a single string is assigned to the variable.
Two differences between this style and using the *index* style exist:
a variable with *string* style can be redefined, e.g. by another command later
in the input script, or if the script is read again in a loop. The other
difference is that *string* performs variable substitution even if the
string parameter is quoted.
----------
The *uloop* style is identical to the *universe* style except that the
strings are the integers from 1 to N. This allows generation of long
list of runs (e.g. 1000) without having to list N strings in the input
script.
----------
For the *universe* style, one or more strings are specified. There
must be at least as many strings as there are processor partitions or
"worlds". LAMMPS can be run with multiple partitions via the
:doc:`-partition command-line switch <Run_options>`. This variable
command initially assigns one string to each world. When a
:doc:`next <next>` command is encountered using this variable, the first
processor partition to encounter it, is assigned the next available
string. This continues until all the variable strings are consumed.
Thus, this command can be used to run 50 simulations on 8 processor
partitions. The simulations will be run one after the other on
whatever partition becomes available, until they are all finished.
Universe-style variables are incremented using the files
"tmp.lammps.variable" and "tmp.lammps.variable.lock" which you will
see in your directory during such a LAMMPS run.
----------
For the *world* style, one or more strings are specified. There must
be one string for each processor partition or "world". LAMMPS can be
run with multiple partitions via the :doc:`-partition command-line
switch <Run_options>`. This variable command assigns one string to
each world. All processors in the world are assigned the same string.
The next command cannot be used with equal-style variables, since
there is only one value per world. This style of variable is useful
when you wish to run different simulations on different partitions, or
when performing a parallel tempering simulation (see the :doc:`temper
<temper>` command), to assign different temperatures to different
partitions.
----------
@ -495,36 +513,39 @@ is a valid (though strange) variable formula:
Specifically, a formula can contain numbers, constants, thermo
keywords, math operators, math functions, group functions, region
functions, atom values, atom vectors, compute references, fix
references, and references to other variables.
functions, special functions, feature functions, atom values, atom
vectors, compute references, fix references, and references to other
variables.
+--------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Number | 0.2, 100, 1.0e20, -15.4, etc |
+--------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Constant | PI, version, on, off, true, false, yes, no |
+--------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Thermo keywords | vol, pe, ebond, etc |
+--------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Math operators | (), -x, x+y, x-y, x\*y, x/y, x\^y, x%y, x == y, x != y, x < y, x <= y, x > y, x >= y, x && y, x \|\| y, x \|\^ y, !x |
+--------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Math functions | sqrt(x), exp(x), ln(x), log(x), abs(x), sin(x), cos(x), tan(x), asin(x), acos(x), atan(x), atan2(y,x), random(x,y,z), normal(x,y,z), ceil(x), floor(x), round(x), 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), cwiggle(x,y,z) |
+--------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Group functions | count(ID), mass(ID), charge(ID), xcm(ID,dim), vcm(ID,dim), fcm(ID,dim), bound(ID,dir), gyration(ID), ke(ID), angmom(ID,dim), torque(ID,dim), inertia(ID,dimdim), omega(ID,dim) |
+--------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Region functions | count(ID,IDR), mass(ID,IDR), charge(ID,IDR), xcm(ID,dim,IDR), vcm(ID,dim,IDR), fcm(ID,dim,IDR), bound(ID,dir,IDR), gyration(ID,IDR), ke(ID,IDR), angmom(ID,dim,IDR), torque(ID,dim,IDR), inertia(ID,dimdim,IDR), omega(ID,dim,IDR) |
+--------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Special functions | sum(x), min(x), max(x), ave(x), trap(x), slope(x), gmask(x), rmask(x), grmask(x,y), next(x), label2type(kind,label) |
+--------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| 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 |
+--------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Compute references | c_ID, c_ID[i], c_ID[i][j], C_ID, C_ID[i] |
+--------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Fix references | f_ID, f_ID[i], f_ID[i][j], F_ID, F_ID[i] |
+--------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Other variables | v_name, v_name[i] |
+--------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+--------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Number | 0.2, 100, 1.0e20, -15.4, etc |
+--------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Constant | PI, version, on, off, true, false, yes, no |
+--------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Thermo keywords | vol, pe, ebond, etc |
+--------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Math operators | (), -x, x+y, x-y, x\*y, x/y, x\^y, x%y, x == y, x != y, x < y, x <= y, x > y, x >= y, x && y, x \|\| y, x \|\^ y, !x |
+--------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Math functions | sqrt(x), exp(x), ln(x), log(x), abs(x), sin(x), cos(x), tan(x), asin(x), acos(x), atan(x), atan2(y,x), random(x,y,z), normal(x,y,z), ceil(x), floor(x), round(x), 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), cwiggle(x,y,z) |
+--------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Group functions | count(ID), mass(ID), charge(ID), xcm(ID,dim), vcm(ID,dim), fcm(ID,dim), bound(ID,dir), gyration(ID), ke(ID), angmom(ID,dim), torque(ID,dim), inertia(ID,dimdim), omega(ID,dim) |
+--------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Region functions | count(ID,IDR), mass(ID,IDR), charge(ID,IDR), xcm(ID,dim,IDR), vcm(ID,dim,IDR), fcm(ID,dim,IDR), bound(ID,dir,IDR), gyration(ID,IDR), ke(ID,IDR), angmom(ID,dim,IDR), torque(ID,dim,IDR), inertia(ID,dimdim,IDR), omega(ID,dim,IDR) |
+--------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Special functions | sum(x), min(x), max(x), ave(x), trap(x), slope(x), gmask(x), rmask(x), grmask(x,y), next(x), is_file(name), is_os(name), extract_setting(name), label2type(kind,label) |
+--------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Feature functions | is_available(category,feature), is_active(category,feature), is_defined(category,id) |
+--------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| 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 |
+--------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Compute references | c_ID, c_ID[i], c_ID[i][j], C_ID, C_ID[i] |
+--------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Fix references | f_ID, f_ID[i], f_ID[i][j], F_ID, F_ID[i] |
+--------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Other variables | v_name, v_name[i] |
+--------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
Most of the formula elements produce a scalar value. Some produce a
global or per-atom vector of values. Global vectors can be produced
@ -574,9 +595,9 @@ will not work, since the *version* has been introduced more recently):
if $(version<20140513) then "communicate vel yes" else "comm_modify vel yes"
The thermo keywords allowed in a formula are those defined by the
:doc:`thermo_style custom <thermo_style>` command. Thermo keywords that
require a :doc:`compute <compute>` to calculate their values such as
"temp" or "press", use computes stored and invoked by the
:doc:`thermo_style custom <thermo_style>` command. Thermo keywords
that require a :doc:`compute <compute>` to calculate their values such
as "temp" or "press", use computes stored and invoked by the
:doc:`thermo_style <thermo_style>` command. This means that you can
only use those keywords in a variable if the style you are using with
the thermo_style command (and the thermo keywords associated with that
@ -714,10 +735,12 @@ new timestep. X,y,z > 0 and y < z are required. The generated
timesteps are on a base-z logarithmic scale, starting with x, and the
y value is how many of the z-1 possible timesteps within one
logarithmic interval are generated. I.e. the timesteps follow the
sequence x,2x,3x,...y\*x,x\*z,2x\*z,3x\*z,...y\*x\*z,x\*z\^2,2x\*z\^2,etc. For
sequence
x,2x,3x,...y\*x,x\*z,2x\*z,3x\*z,...y\*x\*z,x\*z\^2,2x\*z\^2,etc. For
any current timestep, the next timestep in the sequence is returned.
Thus if logfreq(100,4,10) is used in a variable by the :doc:`dump_modify every <dump_modify>` command, it will generate this sequence of
output timesteps:
Thus if logfreq(100,4,10) is used in a variable by the
:doc:`dump_modify every <dump_modify>` command, it will generate this
sequence of output timesteps:
.. parsed-literal::
@ -726,9 +749,10 @@ output timesteps:
The logfreq2(x,y,z) function is similar to logfreq, except a single
logarithmic interval is divided into y equally-spaced timesteps and
all of them are output. Y < z is not required. Thus, if
logfreq2(100,18,10) is used in a variable by the :doc:`dump_modify every <dump_modify>` command, then the interval between 100 and
1000 is divided as 900/18 = 50 steps, and it will generate the
sequence of output timesteps:
logfreq2(100,18,10) is used in a variable by the :doc:`dump_modify
every <dump_modify>` command, then the interval between 100 and 1000
is divided as 900/18 = 50 steps, and it will generate the sequence of
output timesteps:
.. parsed-literal::
@ -970,53 +994,87 @@ types, bond types and so on. For the full list of available keywords
*name* and their meaning, see the documentation for extract_setting()
via the link in this paragraph.
The label2type() function converts type labels into numeric types, using label
maps created by the :doc:`labelmap <labelmap>` or :doc:`read_data <read_data>`
commands. The first argument is the label map kind (atom, bond, angle,
dihedral, or improper) and the second argument is the label. The function
returns the corresponding numeric type.
The label2type(kind,label) function converts type labels into numeric
types, using label maps created by the :doc:`labelmap <labelmap>` or
:doc:`read_data <read_data>` commands. The first argument is the
label map kind (atom, bond, angle, dihedral, or improper) and the
second argument is the label. The function returns the corresponding
numeric type.
----------
Feature Functions
-----------------
Feature functions allow to probe the running LAMMPS executable for
whether specific features are either active, defined, or available. The
functions take two arguments, a *category* and a corresponding
*argument*\ . The arguments are strings and thus cannot be formulas
Feature functions allow probing of the running LAMMPS executable for
whether specific features are available, active, or defined. All 3 of
the functions take two arguments, a *category* and a category-specific
second argument. Both are strings and thus cannot be formulas
themselves; only $-style immediate variable expansion is possible.
Return value is either 1.0 or 0.0 depending on whether the function
evaluates to true or false, respectively.
The return value of the functions is either 1.0 or 0.0 depending on
whether the function evaluates to true or false, respectively.
The *is_active(category,feature)* function allows to query for active
settings which are grouped by categories. Currently supported categories
and arguments are:
The *is_available(category,name)* function queries whether a specific
feature is available in the LAMMPS executable that is being run, i.e
whether it was included or enabled at compile time.
* *package*\ : argument = *gpu* or *intel* or *kokkos* or *omp*
* *newton*\ : argument = *pair* or *bond* or *any*
* *pair*\ : argument = *single* or *respa* or *manybody* or *tail* or *shift*
* *comm_style*\ : argument = *brick* or *tiled*
* *min_style*\ : argument = any of the compiled in minimizer styles
* *run_style*\ : argument = any of the compiled in run styles
* *atom_style*\ : argument = any of the compiled in atom style)
* *pair_style*\ : argument = any of the compiled in pair styles
* *bond_style*\ : argument = any of the compiled in bond styles
* *angle_style*\ : argument = any of the compiled in angle styles
* *dihedral_style*\ : argument = any of the compiled in dihedral styles
* *improper_style*\ : argument = any of the compiled in improper styles
* *kspace_style*\ : argument = any of the compiled in kspace styles
This supports the following categories: *command*, *compute*, *fix*,
*pair_style* and *feature*\ . For all the categories except *feature*
the *name* is a style name, e.g. *nve* for the *fix* category. Note
that many LAMMPS input script commands such as *create_atoms* are
actually instances of a command style which LAMMPS defines, as opposed
to built-in commands. For all of these styles except *command*,
appending of active suffixes is also tried before reporting failure.
Most of the settings are self-explanatory, the *single* argument in the
*pair* category allows to check whether a pair style supports a
Pair::single() function as needed by compute group/group and others
features or LAMMPS, *respa* allows to check whether the inner/middle/outer
mode of r-RESPA is supported. In the various style categories,
the checking is also done using suffix flags, if available and enabled.
The *feature* category checks the availability of the following
compile-time enabled features: GZIP support, PNG support, JPEG
support, FFMPEG support, and C++ exceptions for error
handling. Corresponding names are *gzip*, *png*, *jpeg*, *ffmpeg* and
*exceptions*\ .
Example 1: disable use of suffix for pppm when using GPU package
(i.e. run it on the CPU concurrently to running the pair style on the
GPU), but do use the suffix otherwise (e.g. with OPENMP).
Example: Only dump in a given format if the compiled binary supports it.
.. code-block:: LAMMPS
if "$(is_available(feature,png))" then "print 'PNG supported'" else "print 'PNG not supported'"
if "$(is_available(feature,ffmpeg)" then "dump 3 all movie 25 movie.mp4 type type zoom 1.6 adiam 1.0"
The *is_active(category,feature)* function queries whether a specific
feature is currently active within LAMMPS. The features are grouped
by categories. Supported categories and features are:
* *package*\ : features = *gpu* or *intel* or *kokkos* or *omp*
* *newton*\ : features = *pair* or *bond* or *any*
* *pair*\ : features = *single* or *respa* or *manybody* or *tail* or *shift*
* *comm_style*\ : features = *brick* or *tiled*
* *min_style*\ : features = a minimizer style name
* *run_style*\ : features = a run style name
* *atom_style*\ : features = an atom style name
* *pair_style*\ : features = a pair style name
* *bond_style*\ : features = a bond style name
* *angle_style*\ : features = an angle style name
* *dihedral_style*\ : features = a dihedral style name
* *improper_style*\ : features = an improper style name
* *kspace_style*\ : features = a kspace style name
Most of the settings are self-explanatory. For the *package*
category, a package may have been included in the LAMMPS build, but
not have enabled by any input script command, and hence be inactive.
The *single* feature in the *pair* category checks whether the
currently defined pair style supports a Pair::single() function as
needed by compute group/group and others features or LAMMPS.
Similarly, the *respa* feature checks whether the inner/middle/outer
mode of r-RESPA is supported by the current pair style.
For the categories with *style* in their name, only a single instance
of the style is ever active at any time in a LAMMPS simulation. Thus
the check is whether the currently active style matches the specified
name. This check is also done using suffix flags, if available and
enabled.
Example 1: Disable use of suffix for PPPM when using GPU package
(i.e. run it on the CPU concurrently while running the pair style on
the GPU), but do use the suffix otherwise (e.g. with OPENMP).
.. code-block:: LAMMPS
@ -1024,39 +1082,23 @@ GPU), but do use the suffix otherwise (e.g. with OPENMP).
if $(is_active(package,gpu)) then "suffix off"
kspace_style pppm
Example 2: use r-RESPA with inner/outer cutoff, if supported by pair
style, otherwise fall back to using pair and reducing the outer time
step
Example 2: Use r-RESPA with inner/outer cutoff, if supported by the
current pair style, otherwise fall back to using r-RESPA with simply
the pair keyword and reducing the outer time step.
.. code-block:: LAMMPS
timestep $(2.0*(1.0+2.0*is_active(pair,respa)))
if $(is_active(pair,respa)) then "run_style respa 4 3 2 2 improper 1 inner 2 5.5 7.0 outer 3 kspace 4" else "run_style respa 3 3 2 improper 1 pair 2 kspace 3"
if $(is_active(pair,respa)) then "run_style respa 4 3 2 2 improper 1 inner 2 5.5 7.0 outer 3 kspace 4" else "run_style respa 3 3 2 improper 1 pair 2 kspace 3"
The *is_available(category,name)* function allows to query whether
a specific optional feature is available, i.e. compiled in.
This currently works for the following categories: *command*,
*compute*, *fix*, *pair_style* and *feature*\ . For all categories
except *command* and *feature* also appending active suffixes is
tried before reporting failure.
The *feature* category is used to check the availability of compiled in
features such as GZIP support, PNG support, JPEG support, FFMPEG support,
and C++ exceptions for error handling. Corresponding values for name are
*gzip*, *png*, *jpeg*, *ffmpeg* and *exceptions*\ .
This enables writing input scripts which only dump using a given format if
the compiled binary supports it.
.. code-block:: LAMMPS
if "$(is_available(feature,png))" then "print 'PNG supported'" else "print 'PNG not supported'"
if "$(is_available(feature,ffmpeg)" then "dump 3 all movie 25 movie.mp4 type type zoom 1.6 adiam 1.0"
The *is_defined(categoy,id)* function allows to query categories like
*compute*, *dump*, *fix*, *group*, *region*, and *variable* whether an
entry with the provided name or id is defined.
The *is_defined(category,id)* function checks whether an instance of a
style or variable with a specific ID or name is currently defined
within LAMMPS. The supported categories are *compute*, *dump*,
*fix*, *group*, *region*, and *variable*. Each of these styles (as
well as the variable command) can be specified multiple times within
LAMMPS, each with a unique *id*. This function checks whether the
specified *id* exists. For category *variable", the *id* is the
variable name.
----------
@ -1268,6 +1310,35 @@ Vectors" discussion above.
----------
Vector Initialization
---------------------
.. versionadded:: TBD
*Vector*-style variables only can be initialized with a special
syntax, instead of using a formula. The syntax is a bracketed,
comma-separated syntax like the following:
.. code-block:: LAMMPS
variable myvec vector [1,3.5,7,10.2]
The 3rd argument formula is replaced by the vector values in brackets,
separated by commas. This example creates a 4-length vector with
specific numeric values, each of which can be specified as an integer
or floating point value. Note that while whitespace can be added
before or after individual values, no other mathematical operations
can be specified. E.g. "3*10" or "3*v_abc" are not valid vector
elements, nor is "10*[1,2,3,4]" valid for the entire vector.
Unlike vector variables specified with formulas, this vector variable
is static; its length and values never changes. Its values can be
used in other commands (including vector-style variables specified
with formulas) via the usual syntax for accessing individual vector
elements or the entire vector.
----------
Immediate Evaluation of Variables
"""""""""""""""""""""""""""""""""
@ -1285,18 +1356,19 @@ with a leading $ sign (e.g. $x or ${abc}) versus with a leading "v\_"
(e.g. v_x or v_abc). The former can be used in any input script
command, including a variable command. The input script parser
evaluates the reference variable immediately and substitutes its value
into the command. As explained on the :doc:`Commands parse <Commands_parse>` doc page, you can also use un-named
"immediate" variables for this purpose. For example, a string like
this $((xlo+xhi)/2+sqrt(v_area)) in an input script command evaluates
the string between the parenthesis as an equal-style variable formula.
into the command. As explained on the :doc:`Commands parse
<Commands_parse>` doc page, you can also use un-named "immediate"
variables for this purpose. For example, a string like this
$((xlo+xhi)/2+sqrt(v_area)) in an input script command evaluates the
string between the parenthesis as an equal-style variable formula.
Referencing a variable with a leading "v\_" is an optional or required
kind of argument for some commands (e.g. the :doc:`fix ave/chunk <fix_ave_chunk>` or :doc:`dump custom <dump>` or
:doc:`thermo_style <thermo_style>` commands) if you wish it to evaluate
a variable periodically during a run. It can also be used in a
variable formula if you wish to reference a second variable. The
second variable will be evaluated whenever the first variable is
evaluated.
kind of argument for some commands (e.g. the :doc:`fix ave/chunk
<fix_ave_chunk>` or :doc:`dump custom <dump>` or :doc:`thermo_style
<thermo_style>` commands) if you wish it to evaluate a variable
periodically during a run. It can also be used in a variable formula
if you wish to reference a second variable. The second variable will
be evaluated whenever the first variable is evaluated.
As an example, suppose you use this command in your input script to
define the variable "v" as
@ -1309,8 +1381,9 @@ before a run where the simulation box size changes. You might think
this will assign the initial volume to the variable "v". That is not
the case. Rather it assigns a formula which evaluates the volume
(using the thermo_style keyword "vol") to the variable "v". If you
use the variable "v" in some other command like :doc:`fix ave/time <fix_ave_time>` then the current volume of the box will be
evaluated continuously during the run.
use the variable "v" in some other command like :doc:`fix ave/time
<fix_ave_time>` then the current volume of the box will be evaluated
continuously during the run.
If you want to store the initial volume of the system, you can do it
this way:

View File

@ -38,6 +38,8 @@
#include "universe.h"
#include "update.h"
#include "fmt/ranges.h"
#include <cctype>
#include <cmath>
#include <cstring>
@ -72,10 +74,10 @@ enum{DONE,ADD,SUBTRACT,MULTIPLY,DIVIDE,CARAT,MODULO,UNARY,
enum{SUM,XMIN,XMAX,AVE,TRAP,SLOPE};
static constexpr double BIG = 1.0e20;
// INT64_MAX cannot be represented with a double. reduce to avoid overflow when casting back.
// INT64_MAX cannot be represented with a double. reduce to avoid overflow when casting back
#if defined(LAMMPS_SMALLBIG) || defined(LAMMPS_BIGBIG)
static constexpr double MAXBIGINT_DOUBLE = (double) (MAXBIGINT-512);
#else
@ -477,8 +479,10 @@ void Variable::set(int narg, char **arg)
// VECTOR
// replace pre-existing var if also style VECTOR (allows it to be reset)
// num = 1, which = 1st value
// data = 1 value, string to eval
// num = 2, which = 1st value
// data = 2 values, 1st is string to eval, 2nd is formatted output string [1,2,3]
// if formula string is [value,value,...] then
// immediately store it as N-length vector and set dynamic flag to 0
} else if (strcmp(arg[1],"vector") == 0) {
if (narg != 3) error->all(FLERR,"Illegal variable command: expected 3 arguments but found {}", narg);
@ -487,16 +491,34 @@ void Variable::set(int narg, char **arg)
if (style[ivar] != VECTOR)
error->all(FLERR,"Cannot redefine variable as a different style");
delete[] data[ivar][0];
delete[] data[ivar][1];
data[ivar][0] = utils::strdup(arg[2]);
if (data[ivar][0][0] != '[')
vecs[ivar].dynamic = 1;
else {
vecs[ivar].dynamic = 0;
parse_vector(ivar,data[ivar][0]);
std::vector <double> vec(vecs[ivar].values,vecs[ivar].values + vecs[ivar].n);
data[ivar][1] = utils::strdup(fmt::format("[{}]", fmt::join(vec,",")));
}
replaceflag = 1;
} else {
if (nvar == maxvar) grow();
style[nvar] = VECTOR;
num[nvar] = 1;
num[nvar] = 2;
which[nvar] = 0;
pad[nvar] = 0;
data[nvar] = new char*[num[nvar]];
data[nvar][0] = utils::strdup(arg[2]);
if (data[nvar][0][0] != '[') {
vecs[nvar].dynamic = 1;
data[nvar][1] = nullptr;
} else {
vecs[nvar].dynamic = 0;
parse_vector(nvar,data[nvar][0]);
std::vector <double> vec(vecs[nvar].values,vecs[nvar].values + vecs[nvar].n);
data[nvar][1] = utils::strdup(fmt::format("[{}]", fmt::join(vec,",")));
}
}
// PYTHON
@ -935,8 +957,9 @@ int Variable::internalstyle(int ivar)
if GETENV, query environment and put result in str
if PYTHON, evaluate Python function, it will put result in str
if INTERNAL, convert dvalue and put result in str
if ATOM or ATOMFILE or VECTOR, return nullptr
return nullptr if no variable with name, or which value is bad,
if VECTOR, return str = [value,value,...]
if ATOM or ATOMFILE, return nullptr
return nullptr if no variable with name or if which value is bad,
caller must respond
------------------------------------------------------------------------- */
@ -956,17 +979,21 @@ char *Variable::retrieve(const char *name)
style[ivar] == UNIVERSE || style[ivar] == STRING ||
style[ivar] == SCALARFILE) {
str = data[ivar][which[ivar]];
} else if (style[ivar] == LOOP || style[ivar] == ULOOP) {
std::string result;
if (pad[ivar] == 0) result = std::to_string(which[ivar]+1);
else result = fmt::format("{:0>{}d}",which[ivar]+1, pad[ivar]);
delete[] data[ivar][0];
str = data[ivar][0] = utils::strdup(result);
} else if (style[ivar] == EQUAL) {
double answer = evaluate(data[ivar][0],nullptr,ivar);
delete[] data[ivar][1];
data[ivar][1] = utils::strdup(fmt::format("{:.15g}",answer));
str = data[ivar][1];
} else if (style[ivar] == FORMAT) {
int jvar = find(data[ivar][0]);
if (jvar < 0)
@ -977,11 +1004,13 @@ char *Variable::retrieve(const char *name)
double answer = compute_equal(jvar);
sprintf(data[ivar][2],data[ivar][1],answer);
str = data[ivar][2];
} else if (style[ivar] == GETENV) {
const char *result = getenv(data[ivar][0]);
if (result == nullptr) result = (const char *) "";
delete[] data[ivar][1];
str = data[ivar][1] = utils::strdup(result);
} else if (style[ivar] == PYTHON) {
int ifunc = python->variable_match(data[ivar][0],name,0);
if (ifunc < 0) {
@ -1001,16 +1030,39 @@ char *Variable::retrieve(const char *name)
}
python->invoke_function(ifunc,data[ivar][1]);
str = data[ivar][1];
// if Python func returns a string longer than VALUELENGTH
// then the Python class stores the result, query it via long_string()
char *strlong = python->long_string(ifunc);
if (strlong) str = strlong;
} else if (style[ivar] == TIMER || style[ivar] == INTERNAL) {
delete[] data[ivar][0];
data[ivar][0] = utils::strdup(fmt::format("{:.15g}",dvalue[ivar]));
str = data[ivar][0];
} else if (style[ivar] == ATOM || style[ivar] == ATOMFILE ||
style[ivar] == VECTOR) return nullptr;
} else if (style[ivar] == VECTOR) {
// check if vector variable needs to be re-computed
// if no, just return previously formatted string in data[ivar][1]
// if yes, invoke compute_vector() and convert vector to formatted string
// must also turn off eval_in_progress b/c compute_vector() checks it
if (vecs[ivar].dynamic || vecs[ivar].currentstep != update->ntimestep) {
eval_in_progress[ivar] = 0;
double *result;
int nvec = compute_vector(ivar,&result);
delete[] data[ivar][1];
std::vector <double> vectmp(vecs[ivar].values,vecs[ivar].values + vecs[ivar].n);
std::string str = fmt::format("[{}]", fmt::join(vectmp,","));
data[ivar][1] = utils::strdup(str);
}
str = data[ivar][1];
} else if (style[ivar] == ATOM || style[ivar] == ATOMFILE)
return nullptr;
eval_in_progress[ivar] = 0;
@ -1137,18 +1189,30 @@ void Variable::compute_atom(int ivar, int igroup, double *result, int stride, in
compute result of vector-style variable evaluation
return length of vector and result pointer to vector values
if length == 0 or -1 (mismatch), generate an error
if variable already computed on this timestep, just return
else evaluate the formula and its length, store results in VecVar entry
if necessary, evaluate the formula and its length,
store results in VecVar entry and return them
------------------------------------------------------------------------- */
int Variable::compute_vector(int ivar, double **result)
{
Tree *tree = nullptr;
// if vector is not dynamic, just return stored values
if (!vecs[ivar].dynamic) {
*result = vecs[ivar].values;
return vecs[ivar].n;
}
// if vector already computed on this timestep, just return stored values
if (vecs[ivar].currentstep == update->ntimestep) {
*result = vecs[ivar].values;
return vecs[ivar].n;
}
// evaluate vector variable afresh
if (eval_in_progress[ivar])
print_var_error(FLERR,"has a circular dependency",ivar);
@ -1246,7 +1310,8 @@ void Variable::grow()
vecs = (VecVar *) memory->srealloc(vecs,maxvar*sizeof(VecVar),"var:vecvar");
for (int i = old; i < maxvar; i++) {
vecs[i].nmax = 0;
vecs[i].n = vecs[i].nmax = 0;
vecs[i].dynamic = 1;
vecs[i].currentstep = -1;
vecs[i].values = nullptr;
}
@ -2031,7 +2096,8 @@ double Variable::evaluate(char *str, Tree **tree, int ivar)
if (math_function(word,contents,tree,treestack,ntreestack,argstack,nargstack,ivar));
else if (group_function(word,contents,tree,treestack,ntreestack,argstack,nargstack,ivar));
else if (special_function(word,contents,tree,treestack,ntreestack,argstack,nargstack,ivar));
else print_var_error(FLERR,fmt::format("Invalid math/group/special function '{}()' "
else if (feature_function(word,contents,tree,treestack,ntreestack,argstack,nargstack,ivar));
else print_var_error(FLERR,fmt::format("Invalid math/group/special/feature function '{}()' "
"in variable formula", word),ivar);
delete[] contents;
@ -2247,7 +2313,7 @@ double Variable::evaluate(char *str, Tree **tree, int ivar)
if (nopstack) print_var_error(FLERR,"Invalid syntax in variable formula",ivar);
// for atom-style variable, return remaining tree
// for atom-style and vector-style variable, return remaining tree
// for equal-style variable, return remaining arg
if (tree) {
@ -3979,11 +4045,12 @@ Region *Variable::region_function(char *id, int ivar)
process a special function in formula
push result onto tree or arg stack
word = special function
contents = str between parentheses with one,two,three args
contents = str between parentheses with one or more args
return 0 if not a match, 1 if successfully processed
customize by adding a special function:
sum(x),min(x),max(x),ave(x),trap(x),slope(x),
gmask(x),rmask(x),grmask(x,y),next(x)
gmask(x),rmask(x),grmask(x,y),next(x),
is_file(x),is_ox(x),extract_setting(x),label2type(x,y)
------------------------------------------------------------------------- */
int Variable::special_function(char *word, char *contents, Tree **tree, Tree **treestack,
@ -3992,20 +4059,66 @@ int Variable::special_function(char *word, char *contents, Tree **tree, Tree **t
double sx,sxx;
double value,sy,sxy;
// word not a match to any special function
// word is not a match to any special function
if (strcmp(word,"sum") != 0 && strcmp(word,"min") && strcmp(word,"max") != 0 && strcmp(word,"ave") != 0 &&
strcmp(word,"trap") != 0 && strcmp(word,"slope") != 0 && strcmp(word,"gmask") != 0 && strcmp(word,"rmask") != 0 &&
strcmp(word,"grmask") != 0 && strcmp(word,"next") != 0 && strcmp(word,"is_active") != 0 &&
strcmp(word,"is_defined") != 0 && strcmp(word,"is_available") != 0 && strcmp(word,"is_file") != 0 &&
strcmp(word,"is_os") != 0 && strcmp(word,"extract_setting") != 0 && strcmp(word,"label2type") != 0)
if (strcmp(word,"sum") != 0 && strcmp(word,"min") && strcmp(word,"max") != 0 &&
strcmp(word,"ave") != 0 && strcmp(word,"trap") != 0 && strcmp(word,"slope") != 0 &&
strcmp(word,"gmask") != 0 && strcmp(word,"rmask") != 0 && strcmp(word,"grmask") != 0 &&
strcmp(word,"next") != 0 && strcmp(word,"is_file") != 0 && strcmp(word,"is_os") != 0 &&
strcmp(word,"extract_setting") != 0 && strcmp(word,"label2type") != 0)
return 0;
// process label2type() separately b/c its label arg can have commas in it
if (strcmp(word,"label2type") == 0) {
if (!atom->labelmapflag)
print_var_error(FLERR,"Cannot use label2type() function without a labelmap",ivar);
std::string contents_copy(contents);
auto pos = contents_copy.find_first_of(',');
if (pos == std::string::npos)
print_var_error(FLERR, fmt::format("Invalid label2type({}) function in variable formula",
contents_copy), ivar);
std::string typestr = contents_copy.substr(pos+1);
std::string kind = contents_copy.substr(0, pos);
int value = -1;
if (kind == "atom") {
value = atom->lmap->find(typestr,Atom::ATOM);
} else if (kind == "bond") {
value = atom->lmap->find(typestr,Atom::BOND);
} else if (kind == "angle") {
value = atom->lmap->find(typestr,Atom::ANGLE);
} else if (kind == "dihedral") {
value = atom->lmap->find(typestr,Atom::DIHEDRAL);
} else if (kind == "improper") {
value = atom->lmap->find(typestr,Atom::IMPROPER);
} else {
print_var_error(FLERR, fmt::format("Invalid kind {} in label2type() in variable",kind),ivar);
}
if (value == -1)
print_var_error(FLERR, fmt::format("Invalid {} type label {} in label2type() in variable",
kind, typestr), ivar);
// save value in tree or on argstack
if (tree) {
Tree *newtree = new Tree();
newtree->type = VALUE;
newtree->value = value;
newtree->first = newtree->second = nullptr;
newtree->nextra = 0;
treestack[ntreestack++] = newtree;
} else argstack[nargstack++] = value;
return 1;
}
// process other special functions
// parse contents for comma-separated args
// narg = number of args, args = strings between commas
std::string contents_copy(contents); // for label2type
char *args[MAXFUNCARG];
int narg = parse_args(contents,args);
@ -4333,54 +4446,6 @@ int Variable::special_function(char *word, char *contents, Tree **tree, Tree **t
} else print_var_error(FLERR,"Invalid variable style in special function next",ivar);
} else if (strcmp(word,"is_active") == 0) {
if (narg != 2)
print_var_error(FLERR,"Invalid is_active() function in variable formula",ivar);
Info info(lmp);
value = (info.is_active(args[0],args[1])) ? 1.0 : 0.0;
// save value in tree or on argstack
if (tree) {
auto newtree = new Tree();
newtree->type = VALUE;
newtree->value = value;
treestack[ntreestack++] = newtree;
} else argstack[nargstack++] = value;
} else if (strcmp(word,"is_available") == 0) {
if (narg != 2)
print_var_error(FLERR,"Invalid is_available() function in variable formula",ivar);
Info info(lmp);
value = (info.is_available(args[0],args[1])) ? 1.0 : 0.0;
// save value in tree or on argstack
if (tree) {
auto newtree = new Tree();
newtree->type = VALUE;
newtree->value = value;
treestack[ntreestack++] = newtree;
} else argstack[nargstack++] = value;
} else if (strcmp(word,"is_defined") == 0) {
if (narg != 2)
print_var_error(FLERR,"Invalid is_defined() function in variable formula",ivar);
Info info(lmp);
value = (info.is_defined(args[0],args[1])) ? 1.0 : 0.0;
// save value in tree or on argstack
if (tree) {
auto newtree = new Tree();
newtree->type = VALUE;
newtree->value = value;
treestack[ntreestack++] = newtree;
} else argstack[nargstack++] = value;
} else if (strcmp(word,"is_file") == 0) {
if (narg != 1)
print_var_error(FLERR,"Invalid is_file() function in variable formula",ivar);
@ -4412,7 +4477,7 @@ int Variable::special_function(char *word, char *contents, Tree **tree, Tree **t
} else argstack[nargstack++] = value;
} else if (strcmp(word,"extract_setting") == 0) {
if (narg != 1) print_var_error(FLERR,"Invalid extract_setting() function syntax in variable formula",ivar);
if (narg != 1) print_var_error(FLERR,"Invalid extract_setting() function in variable formula",ivar);
value = lammps_extract_setting(lmp, args[0]);
if (value < 0) {
@ -4428,45 +4493,87 @@ int Variable::special_function(char *word, char *contents, Tree **tree, Tree **t
newtree->value = value;
treestack[ntreestack++] = newtree;
} else argstack[nargstack++] = value;
}
} else if (strcmp(word,"label2type") == 0) {
if (!atom->labelmapflag)
print_var_error(FLERR,"Cannot use label2type() function without a labelmap",ivar);
// delete stored args
auto pos = contents_copy.find_first_of(',');
if (pos == std::string::npos)
print_var_error(FLERR, fmt::format("Invalid label2type({}) function in variable formula",
contents_copy), ivar);
std::string typestr = contents_copy.substr(pos+1);
std::string kind = contents_copy.substr(0, pos);
for (int i = 0; i < narg; i++) delete[] args[i];
int value = -1;
if (kind == "atom") {
value = atom->lmap->find(typestr,Atom::ATOM);
} else if (kind == "bond") {
value = atom->lmap->find(typestr,Atom::BOND);
} else if (kind == "angle") {
value = atom->lmap->find(typestr,Atom::ANGLE);
} else if (kind == "dihedral") {
value = atom->lmap->find(typestr,Atom::DIHEDRAL);
} else if (kind == "improper") {
value = atom->lmap->find(typestr,Atom::IMPROPER);
} else {
print_var_error(FLERR, fmt::format("Invalid type kind {} in variable formula",kind), ivar);
}
return 1;
}
if (value == -1)
print_var_error(FLERR, fmt::format("Invalid {} type label {} in variable formula",
kind, typestr), ivar);
/* ----------------------------------------------------------------------
process a feature function in formula
push result onto tree or arg stack
word = special function
contents = str between parentheses with one or more args
return 0 if not a match, 1 if successfully processed
customize by adding a feature function:
is_available(x,y),is_active(x,y),is_defined(x,y),
------------------------------------------------------------------------- */
int Variable::feature_function(char *word, char *contents, Tree **tree, Tree **treestack,
int &ntreestack, double *argstack, int &nargstack, int ivar)
{
double value;
// word is not a match to any feature function
if (strcmp(word,"is_available") && strcmp(word,"is_active") && strcmp(word,"is_defined") != 0)
return 0;
// process feature functions
// parse contents for comma-separated args
// narg = number of args, args = strings between commas
char *args[MAXFUNCARG];
int narg = parse_args(contents,args);
if (strcmp(word,"is_available") == 0) {
if (narg != 2)
print_var_error(FLERR,"Invalid is_available() function in variable formula",ivar);
Info info(lmp);
value = (info.is_available(args[0],args[1])) ? 1.0 : 0.0;
// save value in tree or on argstack
if (tree) {
Tree *newtree = new Tree();
auto newtree = new Tree();
newtree->type = VALUE;
newtree->value = value;
treestack[ntreestack++] = newtree;
} else argstack[nargstack++] = value;
} else if (strcmp(word,"is_active") == 0) {
if (narg != 2)
print_var_error(FLERR,"Invalid is_active() function in variable formula",ivar);
Info info(lmp);
value = (info.is_active(args[0],args[1])) ? 1.0 : 0.0;
// save value in tree or on argstack
if (tree) {
auto newtree = new Tree();
newtree->type = VALUE;
newtree->value = value;
treestack[ntreestack++] = newtree;
} else argstack[nargstack++] = value;
} else if (strcmp(word,"is_defined") == 0) {
if (narg != 2)
print_var_error(FLERR,"Invalid is_defined() function in variable formula",ivar);
Info info(lmp);
value = (info.is_defined(args[0],args[1])) ? 1.0 : 0.0;
// save value in tree or on argstack
if (tree) {
auto newtree = new Tree();
newtree->type = VALUE;
newtree->value = value;
newtree->first = newtree->second = nullptr;
newtree->nextra = 0;
treestack[ntreestack++] = newtree;
} else argstack[nargstack++] = value;
}
@ -4656,6 +4763,30 @@ void Variable::atom_vector(char *word, Tree **tree, Tree **treestack, int &ntree
else if (strcmp(word,"fz") == 0) newtree->array = &atom->f[0][2];
}
/* ----------------------------------------------------------------------
parse vector string with format [value,value,...] for vector-style variable
store numeric values in vecs[ivar]
------------------------------------------------------------------------- */
void Variable::parse_vector(int ivar, char *str)
{
// check for square brackets, remove them, and split into vector
int nstr = strlen(str)-1;
if ((str[0] != '[') || (str[nstr] != ']'))
error->all(FLERR,"Vector variable formula lacks opening or closing brace: {}", str);
std::vector<std::string> args = Tokenizer(std::string(str+1, str+nstr), ",").as_vector();
int nvec = args.size();
vecs[ivar].n = nvec;
vecs[ivar].nmax = nvec;
vecs[ivar].currentstep = -1;
memory->destroy(vecs[ivar].values);
memory->create(vecs[ivar].values,vecs[ivar].nmax,"variable:values");
for (int i = 0; i < nvec; i++)
vecs[ivar].values[i] = utils::numeric(FLERR, utils::trim(args[i]), false, lmp);
}
/* ----------------------------------------------------------------------
parse string for comma-separated args
store copy of each arg in args array
@ -4698,7 +4829,6 @@ char *Variable::find_next_comma(char *str)
return nullptr;
}
/* ----------------------------------------------------------------------
helper routine for printing variable name with error message
------------------------------------------------------------------------- */

View File

@ -90,6 +90,7 @@ class Variable : protected Pointers {
struct VecVar {
int n, nmax;
int dynamic;
bigint currentstep;
double *values;
};
@ -141,10 +142,12 @@ class Variable : protected Pointers {
int group_function(char *, char *, Tree **, Tree **, int &, double *, int &, int);
Region *region_function(char *, int);
int special_function(char *, char *, Tree **, Tree **, int &, double *, int &, int);
int feature_function(char *, char *, Tree **, Tree **, int &, double *, int &, int);
void peratom2global(int, char *, double *, int, tagint, Tree **, Tree **, int &, double *, int &);
int is_atom_vector(char *);
void atom_vector(char *, Tree **, Tree **, int &);
int parse_args(char *, char **);
void parse_vector(int, char *);
char *find_next_comma(char *);
void print_var_error(const std::string &, int, const std::string &, int, int global = 1);
void print_tree(Tree *, int);

View File

@ -140,16 +140,18 @@ TEST_F(VariableTest, CreateDelete)
command("variable ten1 universe 1 2 3 4");
command("variable ten2 uloop 4");
command("variable ten3 uloop 4 pad");
command("variable ten4 vector [0,1,2,3,5,7,11]");
command("variable ten5 vector [0.5,1.25]");
command("variable dummy index 0");
command("variable file equal is_file(MYFILE)");
command("variable iswin equal is_os(^Windows)");
command("variable islin equal is_os(^Linux)");
END_HIDE_OUTPUT();
ASSERT_EQ(variable->nvar, 20);
ASSERT_EQ(variable->nvar, 22);
BEGIN_HIDE_OUTPUT();
command("variable dummy delete");
END_HIDE_OUTPUT();
ASSERT_EQ(variable->nvar, 19);
ASSERT_EQ(variable->nvar, 21);
ASSERT_THAT(variable->retrieve("three"), StrEq("three"));
variable->set_string("three", "four");
ASSERT_THAT(variable->retrieve("three"), StrEq("four"));
@ -160,6 +162,8 @@ TEST_F(VariableTest, CreateDelete)
ASSERT_THAT(variable->retrieve("eight"), StrEq(""));
variable->internal_set(variable->find("ten"), 2.5);
ASSERT_THAT(variable->retrieve("ten"), StrEq("2.5"));
EXPECT_THAT(variable->retrieve("ten4"), StrEq("[0,1,2,3,5,7,11]"));
EXPECT_THAT(variable->retrieve("ten5"), StrEq("[0.5,1.25]"));
ASSERT_THAT(variable->retrieve("file"), StrEq("0"));
FILE *fp = fopen("MYFILE", "w");
fputs(" ", fp);
@ -217,7 +221,7 @@ TEST_F(VariableTest, CreateDelete)
TEST_FAILURE(".*ERROR: World variable count doesn't match # of partitions.*",
command("variable ten10 world xxx xxx"););
TEST_FAILURE(".*ERROR: All universe/uloop variables must have same # of values.*",
command("variable ten4 uloop 2"););
command("variable ten6 uloop 2"););
TEST_FAILURE(".*ERROR: Incorrect conversion in format string.*",
command("variable ten11 format two \"%08x\""););
TEST_FAILURE(".*ERROR: Variable name 'ten@12' must have only letters, numbers, or undersc.*",
@ -321,6 +325,9 @@ TEST_F(VariableTest, Expressions)
command("variable err1 equal v_one/v_ten7");
command("variable err2 equal v_one%v_ten7");
command("variable err3 equal v_ten7^-v_one");
command("variable vec1 vector \"[-2, 0, 1,2 ,3, 5 , 7\n]\"");
command("variable vec2 vector v_vec1*0.5");
command("variable vec3 equal v_vec2[3]");
variable->set("dummy index 1 2");
END_HIDE_OUTPUT();
@ -347,6 +354,9 @@ TEST_F(VariableTest, Expressions)
ASSERT_DOUBLE_EQ(variable->compute_equal("v_ten10"), 100);
ASSERT_DOUBLE_EQ(variable->compute_equal("v_ten11"), 1);
ASSERT_DOUBLE_EQ(variable->compute_equal("v_ten12"), 3);
EXPECT_THAT(variable->retrieve("vec1"), StrEq("[-2,0,1,2,3,5,7]"));
EXPECT_THAT(variable->retrieve("vec2"), StrEq("[-1,0,0.5,1,1.5,2.5,3.5]"));
ASSERT_DOUBLE_EQ(variable->compute_equal("v_vec3"), 0.5);
TEST_FAILURE(".*ERROR: Variable six: Invalid thermo keyword 'XXX' in variable formula.*",
command("print \"${six}\""););
@ -402,7 +412,7 @@ TEST_F(VariableTest, Functions)
command("print \"$(extract_setting()\""););
TEST_FAILURE(".*ERROR on proc 0: Invalid immediate variable.*",
command("print \"$(extract_setting()\""););
TEST_FAILURE(".*ERROR: Invalid extract_setting.. function syntax in variable formula.*",
TEST_FAILURE(".*ERROR: Invalid extract_setting.. function in variable formula.*",
command("print \"$(extract_setting(one,two))\""););
TEST_FAILURE(
".*ERROR: Unknown setting nprocs for extract_setting.. function in variable formula.*",
@ -599,9 +609,9 @@ TEST_F(VariableTest, Label2TypeAtomic)
ASSERT_DOUBLE_EQ(variable->compute_equal("label2type(atom,O1)"), 3.0);
ASSERT_DOUBLE_EQ(variable->compute_equal("label2type(atom,H1)"), 4.0);
TEST_FAILURE(".*ERROR: Variable t1: Invalid atom type label C1 in variable formula.*",
TEST_FAILURE(".*ERROR: Variable t1: Invalid atom type label C1 in label2type.. in variable.*",
command("print \"${t1}\""););
TEST_FAILURE(".*ERROR: Invalid bond type label H1 in variable formula.*",
TEST_FAILURE(".*ERROR: Invalid bond type label H1 in label2type.. in variable.*",
variable->compute_equal("label2type(bond,H1)"););
}