diff --git a/doc/src/dump.rst b/doc/src/dump.rst index c2509e6654..8c5ce6e208 100644 --- a/doc/src/dump.rst +++ b/doc/src/dump.rst @@ -169,11 +169,12 @@ or multiple smaller files). .. note:: - Because periodic boundary conditions are enforced only on - timesteps when neighbor lists are rebuilt, the coordinates of an atom - written to a dump file may be slightly outside the simulation box. - Re-neighbor timesteps will not typically coincide with the timesteps - dump snapshots are written. See the :doc:`dump_modify pbc ` command if you with to force coordinates to be + Because periodic boundary conditions are enforced only on timesteps + when neighbor lists are rebuilt, the coordinates of an atom written + to a dump file may be slightly outside the simulation box. + Re-neighbor timesteps will not typically coincide with the + timesteps dump snapshots are written. See the :doc:`dump_modify + pbc ` command if you with to force coordinates to be strictly inside the simulation box. .. note:: @@ -189,20 +190,21 @@ or multiple smaller files). multiple processors, each of which owns a subset of the atoms. For the *atom*, *custom*, *cfg*, and *local* styles, sorting is off by -default. For the *dcd*, *xtc*, *xyz*, and *molfile* styles, sorting by -atom ID is on by default. See the :doc:`dump_modify ` doc -page for details. +default. For the *dcd*, *xtc*, *xyz*, and *molfile* styles, sorting +by atom ID is on by default. See the :doc:`dump_modify ` +doc page for details. -The *atom/gz*, *cfg/gz*, *custom/gz*, *local/gz*, and *xyz/gz* styles are identical -in command syntax to the corresponding styles without "gz", however, -they generate compressed files using the zlib library. Thus the filename -suffix ".gz" is mandatory. This is an alternative approach to writing -compressed files via a pipe, as done by the regular dump styles, which -may be required on clusters where the interface to the high-speed network -disallows using the fork() library call (which is needed for a pipe). -For the remainder of this doc page, you should thus consider the *atom* -and *atom/gz* styles (etc) to be inter-changeable, with the exception -of the required filename suffix. +The *atom/gz*, *cfg/gz*, *custom/gz*, *local/gz*, and *xyz/gz* styles +are identical in command syntax to the corresponding styles without +"gz", however, they generate compressed files using the zlib +library. Thus the filename suffix ".gz" is mandatory. This is an +alternative approach to writing compressed files via a pipe, as done +by the regular dump styles, which may be required on clusters where +the interface to the high-speed network disallows using the fork() +library call (which is needed for a pipe). For the remainder of this +doc page, you should thus consider the *atom* and *atom/gz* styles +(etc) to be inter-changeable, with the exception of the required +filename suffix. Similarly, the *atom/zstd*, *cfg/zstd*, *custom/zstd*, *local/zstd*, and *xyz/zstd* styles are identical to the gz styles, but use the Zstd @@ -275,10 +277,11 @@ This bounding box is convenient for many visualization programs. The meaning of the 6 character flags for "xx yy zz" is the same as above. Note that the first two numbers on each line are now xlo_bound instead -of xlo, etc, since they represent a bounding box. See the :doc:`Howto triclinic ` page for a geometric description -of triclinic boxes, as defined by LAMMPS, simple formulas for how the -6 bounding box extents (xlo_bound,xhi_bound,etc) are calculated from -the triclinic parameters, and how to transform those parameters to and +of xlo, etc, since they represent a bounding box. See the :doc:`Howto +triclinic ` page for a geometric description of +triclinic boxes, as defined by LAMMPS, simple formulas for how the 6 +bounding box extents (xlo_bound,xhi_bound,etc) are calculated from the +triclinic parameters, and how to transform those parameters to and from other commonly used triclinic representations. The "ITEM: ATOMS" line in each snapshot lists column descriptors for @@ -310,23 +313,24 @@ written to the dump file. This local data is typically calculated by each processor based on the atoms it owns, but there may be zero or more entities per atom, e.g. a list of bond distances. An explanation of the possible dump local attributes is given below. Note that by -using input from the :doc:`compute property/local ` command with dump local, -it is possible to generate information on bonds, angles, etc that can -be cut and pasted directly into a data file read by the -:doc:`read_data ` command. +using input from the :doc:`compute property/local +` command with dump local, it is possible to +generate information on bonds, angles, etc that can be cut and pasted +directly into a data file read by the :doc:`read_data ` +command. Style *cfg* has the same command syntax as style *custom* and writes -extended CFG format files, as used by the -`AtomEye `_ visualization -package. Since the extended CFG format uses a single snapshot of the -system per file, a wildcard "\*" must be included in the filename, as -discussed below. The list of atom attributes for style *cfg* must -begin with either "mass type xs ys zs" or "mass type xsu ysu zsu" -since these quantities are needed to write the CFG files in the -appropriate format (though the "mass" and "type" fields do not appear -explicitly in the file). Any remaining attributes will be stored as -"auxiliary properties" in the CFG files. Note that you will typically -want to use the :doc:`dump_modify element ` command with +extended CFG format files, as used by the `AtomEye +`_ visualization package. +Since the extended CFG format uses a single snapshot of the system per +file, a wildcard "\*" must be included in the filename, as discussed +below. The list of atom attributes for style *cfg* must begin with +either "mass type xs ys zs" or "mass type xsu ysu zsu" since these +quantities are needed to write the CFG files in the appropriate format +(though the "mass" and "type" fields do not appear explicitly in the +file). Any remaining attributes will be stored as "auxiliary +properties" in the CFG files. Note that you will typically want to +use the :doc:`dump_modify element ` command with CFG-formatted files, to associate element names with atom types, so that AtomEye can render atoms appropriately. When unwrapped coordinates *xsu*, *ysu*, and *zsu* are requested, the nominal AtomEye diff --git a/doc/src/dump_modify.rst b/doc/src/dump_modify.rst index da7ccffeb2..42bb4d94fd 100644 --- a/doc/src/dump_modify.rst +++ b/doc/src/dump_modify.rst @@ -17,7 +17,7 @@ Syntax * one or more keyword/value pairs may be appended * these keywords apply to various dump styles -* keyword = *append* or *at* or *buffer* or *delay* or *element* or *every* or *fileper* or *first* or *flush* or *format* or *header* or *image* or *label* or *maxfiles* or *nfile* or *pad* or *pbc* or *precision* or *region* or *refresh* or *scale* or *sfactor* or *sort* or *tfactor* or *thermo* or *thresh* or *time* or *units* or *unwrap* +* keyword = *append* or *at* or *buffer* or *delay* or *element* or *every* or *every/time* or *fileper* or *first* or *flush* or *format* or *header* or *image* or *label* or *maxfiles* or *nfile* or *pad* or *pbc* or *precision* or *region* or *refresh* or *scale* or *sfactor* or *sort* or *tfactor* or *thermo* or *thresh* or *time* or *units* or *unwrap* .. parsed-literal:: @@ -32,6 +32,9 @@ Syntax *every* arg = N N = dump every this many timesteps N can be a variable (see below) + *every/time* arg = Delta + Delta = dump every this interval in simulation time (time units) + Delta can be a variable (see below) *fileper* arg = Np Np = write one file for every this many processors *first* arg = *yes* or *no* @@ -197,11 +200,18 @@ will be accepted. ---------- -The *every* keyword changes the dump frequency originally specified by -the :doc:`dump ` command to a new value. The every keyword can be -specified in one of two ways. It can be a numeric value in which case -it must be > 0. Or it can be an :doc:`equal-style variable `, -which should be specified as v_name, where name is the variable name. +The *every* keyword does two things. It specifies that the interval +between dump snapshots will be specified in timesteps, which is the +default if the *every* or *every/time* keywords are not used. See the +*every/time* keyword for how to specify the interval in simulation +time, i.e. in time units of the :doc:`units ` command. This +command also sets the interval value, which overrides the dump +frequency originally specified by the :doc:`dump ` command. + +The every keyword can be specified in one of two ways. It can be a +numeric value in which case it must be > 0. Or it can be an +:doc:`equal-style 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 a dump snapshot will be written @@ -210,11 +220,12 @@ 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 `, as examples of useful functions to use in this context. Other similar math functions -could easily be added as options for :doc:`equal-style variables `. Also see the next() function, which allows -use of a file-style variable which reads successive values from a -file, each time the variable is evaluated. Used with the *every* -keyword, if the file contains a list of ascending timesteps, you can -output snapshots whenever you wish. +could easily be added as options for :doc:`equal-style variables +`. Also see the next() function, which allows use of a +file-style variable which reads successive values from a file, each +time the variable is evaluated. Used with the *every* keyword, if the +file contains a list of ascending timesteps, you can output snapshots +whenever you wish. Note that when using the variable option with the *every* keyword, you need to use the *first* option if you want an initial snapshot written @@ -255,14 +266,95 @@ in file tmp.times: ---------- +The *every/time* keyword does two things. It specifies that the +interval between dump snapshots will be specified in simulation time, +i.e. in time units of the :doc:`units ` command. This can be +useful when the timestep size varies during a simulation run, e.g. by +use of the :doc:`fix dt/reset ` command. The default is +to specify the interval in timesteps; see the *every* keyword. This +command also sets the interval value. + +Note that since snapshots are output on simulation steps, each +snapshot will be written on the first timestep whose associated +simulation time is >= the exact snapshot time value. + +As with the *every* option, the *Delta* value can be specified in one +of two ways. It can be a numeric value in which case it must be > +0.0. Or it can be an :doc:`equal-style 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 simulation time at which a dump snapshot will be +written out. On that timestep the variable will be evaluated again to +determine the next simulation time, etc. Thus the variable should +return values in time units. Note the current timestep or simulation +time can be used in an :doc:`equal-style variables ` since +they are both thermodynamic keywords. Also see the next() function, +which allows use of a file-style variable which reads successive +values from a file, each time the variable is evaluated. Used with +the *every/time* keyword, if the file contains a list of ascending +simulation times, you can output snapshots whenever you wish. + +Note that when using the variable option with the *every/time* +keyword, you need to use the *first* option if you want an initial +snapshot written to the dump file. The *every/time* keyword cannot be +used with the dump *dcd* style. + +For example, the following commands will write snapshots at successive +simulation times which grow by a factor of 1.5 with each interval. +The dt value used in the variable is to avoid a zero result when the +initial simulation time is 0.0. + +.. code-block:: LAMMPS + + variable increase equal 1.5*(time+dt) + dump 1 all atom 100 tmp.dump + dump_modify 1 every/time v_increase first yes + +The following commands would write snapshots at the times listed in +file tmp.times: + +.. code-block:: LAMMPS + + variable f file tmp.times + variable s equal next(f) + dump 1 all atom 100 tmp.dump + dump_modify 1 every/time v_s + +.. note:: + + When using a file-style variable with the *every* keyword, the file + of timesteps must list a first times that is beyond the time + associated with the current timestep (e.g. it cannot be 0.0). And + it must list one or more times beyond the length of the run you + perform. This is because the dump command will generate an error + if the next time it reads from the file is not a value greater than + the current time. Thus if you wanted output at times 0,15,100 of a + run of length 100 in simulation time, the file should contain the + values 15,100,101 and you should also use the dump_modify first + command. Any final value > 100 could be used in place of 101. + +---------- + The *first* keyword determines whether a dump snapshot is written on the very first timestep after the dump command is invoked. This will -always occur if the current timestep is a multiple of N, the frequency -specified in the :doc:`dump ` command, including timestep 0. But -if this is not the case, a dump snapshot will only be written if the -setting of this keyword is *yes*\ . If it is *no*, which is the +always occur if the current timestep is a multiple of $N$, the +frequency specified in the :doc:`dump ` command or +:doc:`dump_modify every ` command, including timestep 0. +It will also always occur if the current simulation time is a multiple +of *Delta*, the time interval specified in the doc:`dump_modify every/time +` command. + +But if this is not the case, a dump snapshot will only be written if +the setting of this keyword is *yes*\ . If it is *no*, which is the default, then it will not be written. +Note that if the argument to the :doc:`dump_modify every +` doc:`dump_modify every/time ` commands is +a variable and not a numeric value, then specifying *first yes* is the +only way to write a dump snapshot on the first timestep after the dump +command is invoked. + ---------- The *flush* keyword determines whether a flush operation is invoked @@ -342,10 +434,10 @@ The *fileper* keyword is documented below with the *nfile* keyword. ---------- -The *header* keyword toggles whether the dump file will include a header. -Excluding a header will reduce the size of the dump file for fixes such as -:doc:`fix pair/tracker ` which do not require the information -typically written to the header. +The *header* keyword toggles whether the dump file will include a +header. Excluding a header will reduce the size of the dump file for +fixes such as :doc:`fix pair/tracker ` which do not +require the information typically written to the header. ---------- diff --git a/doc/src/fix_dt_reset.rst b/doc/src/fix_dt_reset.rst index c3aa431e18..368a3dcd70 100644 --- a/doc/src/fix_dt_reset.rst +++ b/doc/src/fix_dt_reset.rst @@ -78,13 +78,20 @@ outer loop (largest) timestep, which is the same timestep that the Note that the cumulative simulation time (in time units), which accounts for changes in the timestep size as a simulation proceeds, -can be accessed by the :doc:`thermo_style time ` keyword. +can be accessed by the :doc:`thermo_style time ` +keyword. + +Also note that the :doc:`dump_modify every/time ` option +allows dump files to be written at intervals specified by simulation +time, rather than by timesteps. Simulation time is in time units; +see the :doc:`units ` doc page for details. Restart, fix_modify, output, run start/stop, minimize info """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" -No information about this fix is written to :doc:`binary restart files `. None of the :doc:`fix_modify ` options -are relevant to this fix. +No information about this fix is written to :doc:`binary restart files +`. None of the :doc:`fix_modify ` options are +relevant to this fix. This fix computes a global scalar which can be accessed by various :doc:`output commands `. The scalar stores the last @@ -93,7 +100,8 @@ timestep on which the timestep was reset to a new value. The scalar value calculated by this fix is "intensive". No parameter of this fix can be used with the *start/stop* keywords of -the :doc:`run ` command. This fix is not invoked during :doc:`energy minimization `. +the :doc:`run ` command. This fix is not invoked during +:doc:`energy minimization `. Restrictions """""""""""" @@ -102,7 +110,7 @@ Restrictions Related commands """""""""""""""" -:doc:`timestep ` +:doc:`timestep `, :doc:`dump_modify every/time ` Default """"""" diff --git a/src/dump.cpp b/src/dump.cpp index 43fdb775b5..df39f3738d 100644 --- a/src/dump.cpp +++ b/src/dump.cpp @@ -918,24 +918,6 @@ void Dump::modify_params(int narg, char **arg) else delay_flag = 0; iarg += 2; - } else if (strcmp(arg[iarg],"delta") == 0) { - if (iarg+2 > narg) error->all(FLERR,"Illegal dump_modify command"); - int idump; - for (idump = 0; idump < output->ndump; idump++) - if (strcmp(id,output->dump[idump]->id) == 0) break; - double delta; - if (strstr(arg[iarg+1],"v_") == arg[iarg+1]) { - delete [] output->var_dump[idump]; - output->var_dump[idump] = utils::strdup(&arg[iarg+1][2]); - delta = 0.0; - } else { - delta = utils::numeric(FLERR,arg[iarg+1],false,lmp); - if (delta <= 0.0) error->all(FLERR,"Illegal dump_modify command"); - } - output->mode_dump[idump] = 1; - output->delta_dump[idump] = delta; - iarg += 2; - } else if (strcmp(arg[iarg],"every") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal dump_modify command"); int idump; @@ -954,6 +936,24 @@ void Dump::modify_params(int narg, char **arg) output->every_dump[idump] = n; iarg += 2; + } else if (strcmp(arg[iarg],"every/time") == 0) { + if (iarg+2 > narg) error->all(FLERR,"Illegal dump_modify command"); + int idump; + for (idump = 0; idump < output->ndump; idump++) + if (strcmp(id,output->dump[idump]->id) == 0) break; + double delta; + if (strstr(arg[iarg+1],"v_") == arg[iarg+1]) { + delete [] output->var_dump[idump]; + output->var_dump[idump] = utils::strdup(&arg[iarg+1][2]); + delta = 0.0; + } else { + delta = utils::numeric(FLERR,arg[iarg+1],false,lmp); + if (delta <= 0.0) error->all(FLERR,"Illegal dump_modify command"); + } + output->mode_dump[idump] = 1; + output->every_time_dump[idump] = delta; + iarg += 2; + } else if (strcmp(arg[iarg],"fileper") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal dump_modify command"); if (!multiproc) diff --git a/src/fix_dt_reset.cpp b/src/fix_dt_reset.cpp index c80c976504..fbbe7d7c3e 100644 --- a/src/fix_dt_reset.cpp +++ b/src/fix_dt_reset.cpp @@ -197,12 +197,15 @@ void FixDtReset::end_of_step() laststep = update->ntimestep; + // calls to other classes that need to know timestep size changed + update->update_time(); update->dt = dt; update->dt_default = 0; if (respaflag) update->integrate->reset_dt(); if (force->pair) force->pair->reset_dt(); for (int i = 0; i < modify->nfix; i++) modify->fix[i]->reset_dt(); + output->reset_dt(); } /* ---------------------------------------------------------------------- */ diff --git a/src/output.cpp b/src/output.cpp index a47a4f8ec1..9cc90ed4b5 100644 --- a/src/output.cpp +++ b/src/output.cpp @@ -29,6 +29,7 @@ #include "variable.h" #include "write_restart.h" +#include #include using namespace LAMMPS_NS; @@ -62,7 +63,7 @@ Output::Output(LAMMPS *lmp) : Pointers(lmp) max_dump = 0; mode_dump = nullptr; every_dump = nullptr; - delta_dump = nullptr; + every_time_dump = nullptr; next_dump = nullptr; next_time_dump = nullptr; last_dump = nullptr; @@ -98,7 +99,7 @@ Output::~Output() memory->destroy(mode_dump); memory->destroy(every_dump); - memory->destroy(delta_dump); + memory->destroy(every_time_dump); memory->destroy(next_dump); memory->destroy(next_time_dump); memory->destroy(last_dump); @@ -134,7 +135,7 @@ void Output::init() for (int i = 0; i < ndump; i++) dump[i]->init(); for (int i = 0; i < ndump; i++) if ((mode_dump[i] == 0 && every_dump[i] == 0) || - (mode_dump[i] == 1 && delta_dump[i] == 0.0)) { + (mode_dump[i] == 1 && every_time_dump[i] == 0.0)) { ivar_dump[i] = input->variable->find(var_dump[i]); if (ivar_dump[i] < 0) error->all(FLERR,"Variable name for dump every or delta does not exist"); @@ -169,42 +170,61 @@ void Output::setup(int memflag) { bigint ntimestep = update->ntimestep; - // perform dump at start of run only if: - // current timestep is multiple of every and last dump not >= this step - // this is first run after dump created and firstflag is set - // note that variable freq will not write unless triggered by firstflag - // set next_dump, and also next_time_dump for mode_dump = 1 - // set next_dump_any to smallest next_dump - // wrap dumps that invoke computes or do variable eval with clear/add - // if dump not written now, use addstep_compute_all() since don't know - // what computes the dump write would invoke - // if no dumps, set next_dump_any to last+1 so will not influence next - - int writeflag; + // consider all dumps + // decide whether to write snapshot and/or calculate next step for dump if (ndump && update->restrict_output == 0) { for (int idump = 0; idump < ndump; idump++) { + + // wrap dumps that invoke computes or do variable eval with clear/add + if (dump[idump]->clearstep || var_dump[idump]) modify->clearstep_compute(); - writeflag = 0; - if (mode_dump[idump] == 0) { - if (every_dump[idump] && (ntimestep % every_dump[idump] == 0) && - last_dump[idump] != ntimestep) writeflag = 1; - } else { - if (delta_dump[idump] >= 0.0 && last_dump[idump] != ntimestep) - writeflag = 1; - } + // write a snapshot at setup only if any of these 3 conditions hold + // (1) this is first run since dump was created and its first_flag = 0 + // (2) mode_dump = 0 and timestep is multiple of every_dump + // (3) mode_dump = 1 and time is multiple of every_time_dump (within EPSDT) + // (2) and (3) only apply for non-variable dump intervals + // finally, do not write if same snapshot written previously, + // i.e. on last timestep of previous run + + int writeflag = 0; + if (last_dump[idump] < 0 && dump[idump]->first_flag == 1) writeflag = 1; + if (mode_dump[idump] == 0) { + if (every_dump[idump] && (ntimestep % every_dump[idump] == 0)) + writeflag = 1; + } else { + if (every_time_dump[idump] > 0.0) { + double tcurrent = update->atime + + (ntimestep - update->atimestep) * update->dt; + double remainder = fmod(tcurrent,every_time_dump[idump]); + if ((remainder < EPSDT*update->dt) || + (every_time_dump[idump] - remainder < EPSDT*update->dt)) + writeflag = 1; + } + } + + if (last_dump[idump] == ntimestep) writeflag = 0; + + // perform dump + if (writeflag) { dump[idump]->write(); last_dump[idump] = ntimestep; } + // calculate timestep and/or time for next dump // set next_dump and next_time_dump, 0 arg for setup() + // only do this if dump written or dump has not been written yet - calculate_next_dump(0,idump,ntimestep); + if (writeflag || last_dump[idump] < 0) + calculate_next_dump(0,idump,ntimestep); + + // if dump not written now, use addstep_compute_all() + // since don't know what computes the dump will invoke if (dump[idump]->clearstep || var_dump[idump]) { if (writeflag) modify->addstep_compute(next_dump[idump]); @@ -214,6 +234,9 @@ void Output::setup(int memflag) if (idump) next_dump_any = MIN(next_dump_any,next_dump[idump]); else next_dump_any = next_dump[0]; } + + // if no dumps, set next_dump_any to last+1 so will not influence next + } else next_dump_any = update->laststep + 1; // do not write restart files at start of run @@ -296,33 +319,26 @@ void Output::write(bigint ntimestep) // set next_dump and also next_time_dump for mode_dump = 1 // set next_dump_any to smallest next_dump // wrap dumps that invoke computes or do variable eval with clear/add - // if dump not written now, use addstep_compute_all() since don't know - // what computes the dump write would invoke - + int writeflag; if (next_dump_any == ntimestep) { for (int idump = 0; idump < ndump; idump++) { if (next_dump[idump] == ntimestep) { + if (last_dump[idump] == ntimestep) continue; + if (dump[idump]->clearstep || var_dump[idump]) modify->clearstep_compute(); - writeflag = 0; - if (last_dump[idump] != ntimestep) writeflag = 1; + // perform dump + // reset next_dump and next_time_dump, 1 arg for write() - if (writeflag) { - dump[idump]->write(); - last_dump[idump] = ntimestep; - } - - // set next_dump and next_time_dump, 1 arg for write() - + dump[idump]->write(); + last_dump[idump] = ntimestep; calculate_next_dump(1,idump,ntimestep); - if (dump[idump]->clearstep || var_dump[idump]) { - if (writeflag) modify->addstep_compute(next_dump[idump]); - else modify->addstep_compute_all(next_dump[idump]); - } + if (dump[idump]->clearstep || var_dump[idump]) + modify->addstep_compute(next_dump[idump]); } if (idump) next_dump_any = MIN(next_dump_any,next_dump[idump]); @@ -422,6 +438,10 @@ void Output::write_dump(bigint ntimestep) operates in one of two modes, based on mode_dump flag for timestep mode, set next_dump for simulation time mode, set next_time_dump and next_dump + which flag depends on caller + 0 = from setup() at start of run + 1 = from write() during run each time a dump file is written + 2 = from reset_dt() called from fix dt/reset when it changes timestep size ------------------------------------------------------------------------- */ void Output::calculate_next_dump(int which, int idump, bigint ntimestep) @@ -431,13 +451,17 @@ void Output::write_dump(bigint ntimestep) if (mode_dump[idump] == 0) { - // for setup, make next_dump a multiple of every_dump - if (every_dump[idump]) { - if (which == 1) next_dump[idump] += every_dump[idump]; - else + + // which = 0: nextdump = next multiple of every_dump + // which = 1: increment nextdump by every_dump + + if (which == 0) next_dump[idump] = (ntimestep/every_dump[idump])*every_dump[idump] + every_dump[idump]; + else if (which == 1) + next_dump[idump] += every_dump[idump]; + } else { next_dump[idump] = static_cast (input->variable->compute_equal(ivar_dump[idump])); @@ -449,22 +473,29 @@ void Output::write_dump(bigint ntimestep) // set next_time_dump and next_dump } else { + bigint nextdump; double nexttime; double tcurrent = update->atime + (ntimestep - update->atimestep) * update->dt; - // for setup, make nexttime a multiple of delta_dump + if (every_time_dump[idump] > 0.0) { - if (delta_dump[idump] > 0.0) { - if (which == 1) nexttime = next_time_dump[idump] + delta_dump[idump]; - else - nexttime = static_cast (tcurrent/delta_dump[idump]) * - delta_dump[idump] + delta_dump[idump]; + // which = 0: nexttime = next multiple of every_time_dump + // which = 1: increment nexttime by every_time_dump + // which = 2: no change to previous nexttime (only timestep has changed) + + if (which == 0) + nexttime = static_cast (tcurrent/every_time_dump[idump]) * + every_time_dump[idump] + every_time_dump[idump]; + else if (which == 1) + nexttime = next_time_dump[idump] + every_time_dump[idump]; + else if (which == 2) + nexttime = next_time_dump[idump]; nextdump = ntimestep + - static_cast ((nexttime - tcurrent + EPSDT*update->dt) / - update->dt); + static_cast ((nexttime - tcurrent - EPSDT*update->dt) / + update->dt) + 1; // if delta is too small to reach next timestep, use multiple of delta @@ -472,30 +503,35 @@ void Output::write_dump(bigint ntimestep) double tnext = update->atime + (ntimestep+1 - update->atimestep) * update->dt; int multiple = static_cast - ((tnext - nexttime) / delta_dump[idump]); - nexttime = nexttime + (multiple+1)*delta_dump[idump]; + ((tnext - nexttime) / every_time_dump[idump]); + nexttime = nexttime + (multiple+1)*every_time_dump[idump]; nextdump = ntimestep + - static_cast ((nexttime - tcurrent + EPSDT*update->dt) / - update->dt); + static_cast ((nexttime - tcurrent - EPSDT*update->dt) / + update->dt) + 1; } } else { - nexttime = input->variable->compute_equal(ivar_dump[idump]); + + // do not re-evaulate variable for which = 2, leave nexttime as-is + // unless next_time_dump < 0.0, which means variable never yet evaluated + + if (which < 2 || next_time_dump[idump] < 0.0) + nexttime = input->variable->compute_equal(ivar_dump[idump]); + else + nexttime = next_time_dump[idump]; + if (nexttime <= tcurrent) - error->all(FLERR,"Dump delta variable returned a bad time"); + error->all(FLERR,"Dump every/time variable returned a bad time"); + nextdump = ntimestep + - static_cast ((nexttime - tcurrent + EPSDT*update->dt) / - update->dt); + static_cast ((nexttime - tcurrent - EPSDT*update->dt) / + update->dt) + 1; if (nextdump <= ntimestep) - error->all(FLERR,"Dump delta variable too small for next timestep"); + error->all(FLERR,"Dump every/time variable too small for next timestep"); } next_time_dump[idump] = nexttime; next_dump[idump] = nextdump; - - //printf("END time %20.16g step %ld ratio %g\n", - // next_time_dump[idump],next_dump[idump], - // next_time_dump[idump]/update->dt/(next_dump[idump]+1)); } } @@ -529,7 +565,7 @@ void Output::write_restart(bigint ntimestep) /* ---------------------------------------------------------------------- timestep is being changed, called by update->reset_timestep() - reset next timestep values for dumps, restart, thermo output + reset next output values for dumps, restart, thermo output reset to smallest value >= new timestep if next timestep set by variable evaluation, eval for ntimestep-1, so current ntimestep can be returned if needed @@ -626,6 +662,35 @@ void Output::reset_timestep(bigint ntimestep) next = MIN(next,next_thermo); } +/* ---------------------------------------------------------------------- + timestep size is being changed, called by fix dt/reset (at end of step) + reset next output values for dumps which have mode_dump=1 +------------------------------------------------------------------------- */ + +void Output::reset_dt() +{ + next_dump_any = MAXBIGINT; + + for (int idump = 0; idump < ndump; idump++) { + if (mode_dump[idump] == 1) { + + // reset next_dump for unchanged next_time_dump, 2 arg for reset_dt() + + calculate_next_dump(2,idump,update->ntimestep); + + // use compute_all() b/c don't know what computes will be needed + + if (dump[idump]->clearstep || var_dump[idump]) + modify->addstep_compute_all(next_dump[idump]); + } + + next_dump_any = MIN(next_dump_any,next_dump[idump]); + } + + next = MIN(next_dump_any,next_restart); + next = MIN(next,next_thermo); +} + /* ---------------------------------------------------------------------- add a Dump to list of Dumps ------------------------------------------------------------------------- */ @@ -652,7 +717,7 @@ void Output::add_dump(int narg, char **arg) memory->srealloc(dump,max_dump*sizeof(Dump *),"output:dump"); memory->grow(mode_dump,max_dump,"output:mode_dump"); memory->grow(every_dump,max_dump,"output:every_dump"); - memory->grow(delta_dump,max_dump,"output:delta_dump"); + memory->grow(every_time_dump,max_dump,"output:every_time_dump"); memory->grow(next_dump,max_dump,"output:next_dump"); memory->grow(next_time_dump,max_dump,"output:next_time_dump"); memory->grow(last_dump,max_dump,"output:last_dump"); @@ -673,7 +738,8 @@ void Output::add_dump(int narg, char **arg) mode_dump[ndump] = 0; every_dump[ndump] = utils::inumeric(FLERR,arg[3],false,lmp); if (every_dump[ndump] <= 0) error->all(FLERR,"Illegal dump command"); - delta_dump[ndump] = 0.0; + every_time_dump[ndump] = 0.0; + next_time_dump[ndump] = -1.0; last_dump[ndump] = -1; var_dump[ndump] = nullptr; ivar_dump[ndump] = -1; @@ -731,7 +797,7 @@ void Output::delete_dump(char *id) dump[i-1] = dump[i]; mode_dump[i-1] = mode_dump[i]; every_dump[i-1] = every_dump[i]; - delta_dump[i-1] = delta_dump[i]; + every_time_dump[i-1] = every_time_dump[i]; next_dump[i-1] = next_dump[i]; next_time_dump[i-1] = next_time_dump[i]; last_dump[i-1] = last_dump[i]; diff --git a/src/output.h b/src/output.h index ed5bfc162b..c8d3f734e0 100644 --- a/src/output.h +++ b/src/output.h @@ -37,8 +37,8 @@ class Output : protected Pointers { int max_dump; // max size of Dump list bigint next_dump_any; // next timestep for any Dump int *mode_dump; // 0/1 if write every N timesteps or Delta in sim time - int *every_dump; // dump every this many timesteps, 0 if variable - double *delta_dump; // dump every this delta sim time, 0.0 if variable + int *every_dump; // dump every N timesteps, 0 if variable + double *every_time_dump; // dump every Delta of sim time, 0.0 if variable bigint *next_dump; // next timestep to perform dump double *next_time_dump; // next simulation time to perform dump (mode = 1) bigint *last_dump; // last timestep each snapshot was output @@ -75,7 +75,8 @@ class Output : protected Pointers { void write(bigint); // output for current timestep void write_dump(bigint); // force output of dump snapshots void write_restart(bigint); // force output of a restart file - void reset_timestep(bigint); // reset next timestep for all output + void reset_timestep(bigint); // reset output which depeneds on timestep + void reset_dt(); // reset output which depends on dt void add_dump(int, char **); // add a Dump to Dump list void modify_dump(int, char **); // modify a Dump diff --git a/src/update.cpp b/src/update.cpp index 95dc47573e..10b4c573d7 100644 --- a/src/update.cpp +++ b/src/update.cpp @@ -458,7 +458,7 @@ Min *Update::minimize_creator(LAMMPS *lmp) } /* ---------------------------------------------------------------------- - reset timestep as called from input script + reset timestep called from input script ------------------------------------------------------------------------- */ void Update::reset_timestep(int narg, char **arg) @@ -470,24 +470,32 @@ void Update::reset_timestep(int narg, char **arg) /* ---------------------------------------------------------------------- reset timestep - called from rerun command and input script (indirectly) + called from input script (indirectly) or rerun command ------------------------------------------------------------------------- */ void Update::reset_timestep(bigint newstep) { + if (newstep < 0) error->all(FLERR,"Timestep must be >= 0"); + + bigint oldstep = ntimestep; ntimestep = newstep; - if (ntimestep < 0) error->all(FLERR,"Timestep must be >= 0"); - // set atimestep to new timestep - // so future update_time() calls will be correct + // if newstep >= oldstep, update simulation time accordingly + // if newstep < oldstep, zero simulation time - atimestep = ntimestep; + if (newstep >= oldstep) update_time(); - // trigger reset of timestep for output - // do not allow any timestep-dependent fixes to be already defined + if (newstep < oldstep) { + atime = 0.0; + atimestep = newstep; + } + + // changes to output that depend on timestep output->reset_timestep(ntimestep); + // do not allow timestep-dependent fixes to be defined + for (const auto &ifix : modify->get_fix_list()) if (ifix->time_depend) error->all(FLERR, "Cannot reset timestep with time-dependent fix {} defined",ifix->style); @@ -508,7 +516,7 @@ void Update::reset_timestep(bigint newstep) if (icompute->timeflag) icompute->clearstep(); } - // Neighbor Bin/Stencil/Pair classes store timestamps that need to be cleared + // neighbor Bin/Stencil/Pair classes store timestamps that need to be cleared neighbor->reset_timestep(ntimestep); }