Merge pull request #3255 from lammps/timer-variable

Add a timer-style variable
This commit is contained in:
Axel Kohlmeyer
2022-05-06 17:46:22 -04:00
committed by GitHub
7 changed files with 186 additions and 96 deletions

View File

@ -77,18 +77,19 @@ LAMMPS:
so that you do not have to define (or discard) a temporary variable,
"X" in this case.
Additionally, the "immediate" variable expression may be followed by
a colon, followed by a C-style format string, e.g. ":%f" or ":%.10g".
The format string must be appropriate for a double-precision
floating-point value. The format string is used to output the result
of the variable expression evaluation. If a format string is not
specified a high-precision "%.20g" is used as the default.
Additionally, the entire "immediate" variable expression may be
followed by a colon, followed by a C-style format string,
e.g. ":%f" or ":%.10g". The format string must be appropriate for
a double-precision floating-point value. The format string is used
to output the result of the variable expression evaluation. If a
format string is not specified, a high-precision "%.20g" is used as
the default format.
This can be useful for formatting print output to a desired precision:
.. code-block:: LAMMPS
print "Final energy per atom: $(pe/atoms:%10.3f) eV/atom"
print "Final energy per atom: $(v_ke_per_atom+v_pe_per_atom:%10.3f) eV/atom"
Note that neither the curly-bracket or immediate form of variables
can contain nested $ characters for other variables to substitute

View File

@ -39,19 +39,35 @@ Description
Print a text string every N steps during a simulation run. This can
be used for diagnostic purposes or as a debugging tool to monitor some
quantity during a run. The text string must be a single argument, so
it should be enclosed in double quotes if it is more than one word.
If it contains variables it must be enclosed in double quotes to
insure they are not evaluated when the input script line is read, but
will instead be evaluated each time the string is printed.
it should be enclosed in single or double quotes if it is more than
one word. If it contains variables it must be enclosed in double
quotes to insure they are not evaluated when the input script line is
read, but will instead be evaluated each time the string is printed.
Instead of a numeric value, N can be specified as an :doc:`equal-style variable <variable>`, which should be specified as v_name, where
name is the variable name. In this case, the variable is evaluated at
the beginning of a run to determine the **next** timestep at which the
.. note::
As discussed on the :doc:`Commands parse <Commands_parse>` doc
page, the text string can use "immediate" variables, specified as
$(formula) with parenthesis, where the numeric formula has the same
syntax as equal-style variables described on the :doc:`variable
<variable>` doc page. This is a convenient way to evaluate a
formula immediately without using the variable command to define a
named variable and then use that variable in the text string. The
formula can include a trailing colon and format string which
determines the precision with which the numeric value is output.
This is also explained on the :doc:`Commands parse
<Commands_parse>` doc page.
Instead of a numeric value, N can be specified as an :doc:`equal-style
variable <variable>`, which should be specified as v_name, where name
is the variable name. In this case, the variable is evaluated at the
beginning of a run to determine the **next** timestep at which the
string will be written out. On that timestep, the variable will be
evaluated again to determine the next timestep, etc.
Thus the variable should return timestep values. See the stagger()
and logfreq() and stride() math functions for :doc:`equal-style variables <variable>`, as examples of useful functions to use in
this context. For example, the following commands will print output at
evaluated again to determine the next timestep, etc. Thus the
variable should return timestep values. See the stagger() and
logfreq() and stride() math functions for :doc:`equal-style variables
<variable>`, as examples of useful functions to use in this
context. For example, the following commands will print output at
timesteps 10,20,30,100,200,300,1000,2000,etc:
.. code-block:: LAMMPS
@ -61,12 +77,12 @@ 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
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>`.
@ -92,11 +108,13 @@ where ID is replaced with the fix-ID.
Restart, fix_modify, output, run start/stop, minimize info
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
No information about this fix is written to :doc:`binary restart files <restart>`. None of the :doc:`fix_modify <fix_modify>` options
are relevant to this fix. No global or per-atom quantities are stored
by this fix for access by various :doc:`output commands <Howto_output>`.
No information about this fix is written to :doc:`binary restart files
<restart>`. None of the :doc:`fix_modify <fix_modify>` options are
relevant to this fix. No global or per-atom quantities are stored by
this fix for access by various :doc:`output commands <Howto_output>`.
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
""""""""""""

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.
.. note::
As discussed on the :doc:`Commands parse <Commands_parse>` doc
page, the text string can use "immediate" variables, specified as
$(formula) with parenthesis, where the numeric formula has the same
syntax as equal-style variables described on the :doc:`variable
<variable>` doc page. This is a convenient way to evaluate a
formula immediately without using the variable command to define a
named variable and then use that variable in the text string. The
formula can include a trailing colon and format string which
determines the precision with which the numeric value is output.
This is also explained on the :doc:`Commands parse
<Commands_parse>` doc page.
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,

View File

@ -11,7 +11,7 @@ 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 *internal* or *equal* or *vector* or *atom*
* 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*
.. parsed-literal::
@ -42,6 +42,7 @@ Syntax
*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
numbers = 0.0, 100, -5.4, 2.8e-4, etc
@ -96,6 +97,13 @@ Examples
variable str format x %.6g
variable x delete
.. code-block:: LAMMPS
variable start timer
other commands
variable stop timer
print "Elapsed time: $(v_stop-v_start:%.6f)"
Description
"""""""""""
@ -108,32 +116,38 @@ part of a new input command. For variable styles that store multiple
strings, the :doc:`next <next>` command can be used to increment which
string is assigned to the variable. Variables of style *equal* store
a formula which when evaluated produces a single numeric value which
can be output either directly (see the :doc:`print <print>`, :doc:`fix print <fix_print>`, and :doc:`run every <run>` commands) or as part
of thermodynamic output (see the :doc:`thermo_style <thermo_style>`
command), or used as input to an averaging fix (see the :doc:`fix 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 input to various averaging fixes, or elements of which can be
part of thermodynamic output. Variables of style *atom* store a
formula which 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 input to an averaging fix (see the :doc:`fix ave/chunk <fix_ave_chunk>` and :doc:`fix ave/atom <fix_ave_atom>`
commands). Variables of style *atomfile* can be used anywhere in an
input script that atom-style variables are used; they get their
per-atom values from a file rather than from a formula. Variables of
style *python* can be hooked to Python functions using 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.
can be output either directly (see the :doc:`print <print>`, :doc:`fix
print <fix_print>`, and :doc:`run every <run>` commands) or as part of
thermodynamic output (see the :doc:`thermo_style <thermo_style>`
command), or used as input to an averaging fix (see the :doc:`fix
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
input to various averaging fixes, or elements of which can be part of
thermodynamic output. Variables of style *atom* store a formula which
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
input to an averaging fix (see the :doc:`fix ave/chunk
<fix_ave_chunk>` and :doc:`fix ave/atom <fix_ave_atom>` commands).
Variables of style *atomfile* can be used anywhere in an input script
that atom-style variables are used; they get their per-atom values
from a file rather than from a formula. Variables of style *python*
can be hooked to Python functions using 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::
As discussed on the :doc:`Commands parse <Commands_parse>` doc
page, an input script can use "immediate" variables, specified as
$(formula) with parenthesis, where the formula has the same syntax as
equal-style variables described on this page. This is a convenient
way to evaluate a formula immediately without using the variable
command to define a named variable and then evaluate that
variable. See below for a more detailed discussion of this feature.
$(formula) with parenthesis, where the numeric formula has the same
syntax as equal-style variables described on this page. This is a
convenient way to evaluate a formula immediately without using the
variable command to define a named variable and then evaluate that
variable. The formula can include a trailing colon and format
string which determines the precision with which the numeric value
is generated. This is also explained on the :doc:`Commands parse
<Commands_parse>` doc page.
In the discussion that follows, the "name" of the variable is the
arbitrary string that is the first argument in the variable command.
@ -160,22 +174,19 @@ simulation.
Variables of style *equal* and *vector* and *atom* can be used as
inputs to various other commands which evaluate their formulas as
needed, e.g. at different timesteps during a :doc:`run <run>`.
needed, e.g. at different timesteps during a :doc:`run <run>`. In
this context, variables of style *timer* or *internal* or *python* can
be used in place of an equal-style variable, with the following two
caveats.
Variables of style *internal* can be used in place of an equal-style
variable, except by commands that set the value stored by the
internal-style variable. Thus any command that states it can use an
equal-style variable as an argument, can also use an internal-style
variable. This means that when the command evaluates the variable, it
will use the value set (internally) by another command.
Variables of style *python* can be used in place of an equal-style
variable so long as the associated Python function, as defined by the
:doc:`python <python>` command, returns a numeric value. Thus any
command that states it can use an equal-style variable as an argument,
can also use such a python-style variable. This means that when the
LAMMPS command evaluates the variable, the Python function will be
executed.
First, internal-style variables can be used except by commands that
set the value stored by the internal variable. When the LAMMPS
command evaluates the internal-style variable, it will use the value
set (internally) by another command. Second, python-style variables
can be used so long as the associated Python function, as defined by
the :doc:`python <python>` command, returns a numeric value. When the
LAMMPS command evaluates the python-style variable, the Python
function will be executed.
.. note::
@ -271,14 +282,15 @@ 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
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.
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
@ -313,11 +325,12 @@ appropriate for formatting a double-precision floating-point value.
The default format is "%.15g". This variable style allows an
equal-style variable to be formatted precisely when it is evaluated.
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.
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
@ -412,14 +425,25 @@ 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.
: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.
----------
@ -1383,14 +1407,15 @@ commands:
The first run is performed using one setting for the pairwise
potential defined by the :doc:`pair_style <pair_style>` and
:doc:`pair_coeff <pair_coeff>` commands. The potential energy is
evaluated on the final timestep and stored by the :doc:`compute pe <compute_pe>` compute (this is done by the
:doc:`thermo_style <thermo_style>` command). Then a pair coefficient is
changed, altering the potential energy of the system. When the
potential energy is printed via the "e" variable, LAMMPS will use the
potential energy value stored by the :doc:`compute pe <compute_pe>`
compute, thinking it is current. There are many other commands which
could alter the state of the system between runs, causing a variable
to evaluate incorrectly.
evaluated on the final timestep and stored by the :doc:`compute pe
<compute_pe>` compute (this is done by the :doc:`thermo_style
<thermo_style>` command). Then a pair coefficient is changed,
altering the potential energy of the system. When the potential
energy is printed via the "e" variable, LAMMPS will use the potential
energy value stored by the :doc:`compute pe <compute_pe>` compute,
thinking it is current. There are many other commands which could
alter the state of the system between runs, causing a variable to
evaluate incorrectly.
The solution to this issue is the same as for case (2) above, namely
perform a 0-timestep run before the variable is evaluated to insure

View File

@ -110,7 +110,7 @@ using namespace LAMMPS_NS;
static const char *varstyles[] = {
"index", "loop", "world", "universe", "uloop", "string", "getenv",
"file", "atomfile", "format", "equal", "atom", "vector", "python",
"internal", "(unknown)"};
"timer", "internal", "(unknown)"};
static const char *mapstyles[] = { "none", "array", "hash", "yes" };

View File

@ -500,6 +500,31 @@ void Variable::set(int narg, char **arg)
strcpy(data[nvar][1],"(undefined)");
}
// TIMER
// stores current walltime as a timestamp in seconds
// replace pre-existing var if also style TIMER (allows reset with current time)
// num = 1, for string representation of dvalue, set by retrieve()
// dvalue = numeric initialization via platform::walltime()
} else if (strcmp(arg[1],"timer") == 0) {
if (narg != 2) error->all(FLERR,"Illegal variable command");
int ivar = find(arg[0]);
if (ivar >= 0) {
if (style[ivar] != TIMER)
error->all(FLERR,"Cannot redefine variable as a different style");
dvalue[ivar] = platform::walltime();
replaceflag = 1;
} else {
if (nvar == maxvar) grow();
style[nvar] = TIMER;
num[nvar] = 1;
which[nvar] = 0;
pad[nvar] = 0;
data[nvar] = new char*[num[nvar]];
data[nvar][0] = new char[VALUELENGTH];
dvalue[nvar] = platform::walltime();
}
// INTERNAL
// replace pre-existing var if also style INTERNAL (allows it to be reset)
// num = 1, for string representation of dvalue, set by retrieve()
@ -524,6 +549,8 @@ void Variable::set(int narg, char **arg)
dvalue[nvar] = utils::numeric(FLERR,arg[2],false,lmp);
}
// unrecognized variable style
} else error->all(FLERR,"Illegal variable command");
// set name of variable, if not replacing one flagged with replaceflag
@ -610,13 +637,14 @@ int Variable::next(int narg, char **arg)
error->all(FLERR,"All variables in next command must have same style");
}
// invalid styles: STRING, EQUAL, WORLD, ATOM, VECTOR, GETENV,
// FORMAT, PYTHON, INTERNAL
// invalid styles: STRING, EQUAL, WORLD, GETENV, ATOM, VECTOR,
// FORMAT, PYTHON, TIMER, INTERNAL
int istyle = style[find(arg[0])];
if (istyle == STRING || istyle == EQUAL || istyle == WORLD ||
istyle == GETENV || istyle == ATOM || istyle == VECTOR ||
istyle == FORMAT || istyle == PYTHON || istyle == INTERNAL)
if (istyle == STRING || istyle == EQUAL ||
istyle == WORLD || istyle == GETENV || istyle == ATOM ||
istyle == VECTOR || istyle == FORMAT || istyle == PYTHON ||
istyle == TIMER || istyle == INTERNAL)
error->all(FLERR,"Invalid variable style with next command");
// if istyle = UNIVERSE or ULOOP, insure all such variables are incremented
@ -794,13 +822,15 @@ void Variable::python_command(int narg, char **arg)
}
/* ----------------------------------------------------------------------
return 1 if variable is EQUAL or INTERNAL or PYTHON numeric style, 0 if not
return 1 if variable is EQUAL style, 0 if not
TIMER, INTERNAL, PYTHON qualify as EQUAL style
this is checked before call to compute_equal() to return a double
------------------------------------------------------------------------- */
int Variable::equalstyle(int ivar)
{
if (style[ivar] == EQUAL || style[ivar] == INTERNAL) return 1;
if (style[ivar] == EQUAL || style[ivar] == TIMER ||
style[ivar] == INTERNAL) return 1;
if (style[ivar] == PYTHON) {
int ifunc = python->variable_match(data[ivar][0],names[ivar],1);
if (ifunc < 0) return 0;
@ -925,7 +955,7 @@ char *Variable::retrieve(const char *name)
// 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] == INTERNAL) {
} else if (style[ivar] == TIMER || style[ivar] == INTERNAL) {
sprintf(data[ivar][0],"%.15g",dvalue[ivar]);
str = data[ivar][0];
} else if (style[ivar] == ATOM || style[ivar] == ATOMFILE ||
@ -938,7 +968,7 @@ char *Variable::retrieve(const char *name)
/* ----------------------------------------------------------------------
return result of equal-style variable evaluation
can be EQUAL or INTERNAL style or PYTHON numeric style
can be EQUAL or TIMER or INTERNAL style or PYTHON numeric style
for PYTHON, don't need to check python->variable_match() error return,
since caller will have already checked via equalstyle()
------------------------------------------------------------------------- */
@ -952,6 +982,7 @@ double Variable::compute_equal(int ivar)
double value = 0.0;
if (style[ivar] == EQUAL) value = evaluate(data[ivar][0],nullptr,ivar);
else if (style[ivar] == TIMER) value = dvalue[ivar];
else if (style[ivar] == INTERNAL) value = dvalue[ivar];
else if (style[ivar] == PYTHON) {
int ifunc = python->find(data[ivar][0]);

View File

@ -71,6 +71,7 @@ class Variable : protected Pointers {
ATOM,
VECTOR,
PYTHON,
TIMER,
INTERNAL
};
static constexpr int VALUELENGTH = 64;