Update FixColvars to expand usage of fix_modify commands
See https://github.com/Colvars/colvars/pull/418 Also moving inthash code to a separate file to simplify future refactoring
This commit is contained in:
@ -8,151 +8,228 @@ Syntax
|
||||
|
||||
.. code-block:: LAMMPS
|
||||
|
||||
fix ID group-ID colvars configfile keyword values ...
|
||||
fix ID group-ID colvars *configfile* keyword value ...
|
||||
|
||||
* ID, group-ID are documented in :doc:`fix <fix>` command
|
||||
* colvars = style name of this fix command
|
||||
* configfile = the configuration file for the colvars module
|
||||
* keyword = *input* or *output* or *seed* or *unwrap* or *tstat*
|
||||
* *ID*, *group-ID* are documented in :doc:`fix <fix>` command
|
||||
* "colvars" = style name of this fix command
|
||||
* *configfile* = configuration file for Colvars (use "*none*" to provide it inline)
|
||||
* keyword = *output* or *input* or *unwrap* or *tstat* or *seed*
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
*input* arg = colvars.state file name or prefix or NULL (default: NULL)
|
||||
*output* arg = output filename prefix (default: out)
|
||||
*seed* arg = seed for random number generator (default: 1966)
|
||||
*unwrap* arg = *yes* or *no*
|
||||
use unwrapped coordinates in collective variables (default: yes)
|
||||
*tstat* arg = fix id of a thermostat or NULL (default: NULL)
|
||||
*output* value = state filename/prefix for Colvars (default: "out")
|
||||
*input* value = input state filename/prefix for Colvars (optional, default: "NULL")
|
||||
*unwrap* value = "yes" or "no" (default: "yes")
|
||||
*tstat* value = fix ID of thermostat applied to relevant atoms (default: "NULL")
|
||||
*seed* value = seed for random number generator (default: 1966)
|
||||
|
||||
Examples
|
||||
""""""""
|
||||
|
||||
.. code-block:: LAMMPS
|
||||
|
||||
fix mtd all colvars peptide.colvars.inp seed 2122 input peptide.colvars.state output peptide
|
||||
fix abf all colvars colvars.inp tstat 1
|
||||
# Create the fix using a config file, set prefix for its output files
|
||||
fix Colvars all colvars colvars.inp output ${JOB}
|
||||
|
||||
# Communicate the LAMMPS target temperature to the Colvars module
|
||||
fix_modify Colvars tstat NPT
|
||||
|
||||
# Add a new restraint specific to this LAMMPS run
|
||||
fix_modify Colvars config """
|
||||
harmonic {
|
||||
name restraint
|
||||
colvars distance1 distance2
|
||||
centers ${ref1} ${ref2}
|
||||
forceConstant ${kappa}
|
||||
}"""
|
||||
|
||||
Description
|
||||
"""""""""""
|
||||
|
||||
This fix interfaces LAMMPS to the collective variables (Colvars)
|
||||
library, which allows to calculate potentials of mean force (PMFs) for
|
||||
any set of colvars, using sampling methods, including but not limited to
|
||||
Adaptive Biasing Force (ABF), metadynamics (MtD), Steered Molecular
|
||||
Dynamics (SMD) and Umbrella Sampling (US) via a flexible harmonic
|
||||
restraint bias.
|
||||
This fix interfaces LAMMPS to the collective variables `Colvars
|
||||
<https://colvars.github.io>`_ library, which allows to accelerate sampling of
|
||||
rare events and the computation of free energy surfaces and potentials of
|
||||
mean force (PMFs) for any set of collective variables using a variety of
|
||||
sampling methods (e.g. umbrella-sampling, metadynamics, ABF...).
|
||||
|
||||
This documentation describes only the ``fix colvars`` command itself in
|
||||
a LAMMPS script. The Colvars library is documented via the included
|
||||
`PDF manual <PDF/colvars-refman-lammps.pdf>`_ or at the webpage
|
||||
This documentation describes only the "fix colvars" command itself in
|
||||
a LAMMPS script. The Colvars library is fully documented in the included
|
||||
`PDF manual <PDF/colvars-refman-lammps.pdf>`_ or in the webpage
|
||||
`https://colvars.github.io/colvars-refman-lammps/colvars-refman-lammps.html
|
||||
<https://colvars.github.io/colvars-refman-lammps/colvars-refman-lammps.html>`_.
|
||||
|
||||
The Colvars library is developed at `https://github.com/Colvars/colvars
|
||||
<https://github.com/Colvars/colvars>`_ A detailed discussion of its
|
||||
implementation is in :ref:`(Fiorin) <Fiorin>`; additional references are
|
||||
printed at runtime based on specific features being used.
|
||||
<https://github.com/colvars/colvars>`_ A detailed discussion of its
|
||||
implementation is in :ref:`(Fiorin) <Fiorin>`; additional citations for
|
||||
specific features are printed at runtime if these features are used.
|
||||
|
||||
There are some example scripts for using this package with LAMMPS in the
|
||||
``examples/PACKAGES/colvars`` directory.
|
||||
There are example scripts on the `Colvars website <https://colvars.github.io>`_
|
||||
as well as in the ``examples/PACKAGES/colvars`` directory in the LAMMPS
|
||||
source tree.
|
||||
|
||||
----------
|
||||
|
||||
The only required argument to ``fix colvars`` is the filename to the
|
||||
Colvars configuration file that contains the definition of the variables
|
||||
and any biasing methods applied to them. from the MD program in which
|
||||
the colvars library has been integrated.
|
||||
The only required argument to the fix is the name of the Colvars
|
||||
configuration file. The contents of this file are independent from the MD
|
||||
engine in which the Colvars library has been integrated, save for the units
|
||||
that are specific to each engine. In LAMMPS, the units used by Colvars are
|
||||
consistent with those specificed by the :doc:`units <units>` command.
|
||||
|
||||
The *group-ID* entry is ignored. ``fix colvars`` will always apply to
|
||||
.. versionadded:: Colvars_2023-06-04 The special value "*none*"
|
||||
(lowercase) initializes an empty Colvars module, which
|
||||
allows loading configuration dynamically using
|
||||
:doc:`fix_modify <fix_modify>` (see below).
|
||||
|
||||
The *group-ID* entry is ignored. "fix colvars" will always apply to
|
||||
the entire system, but specific atoms will be selected based on
|
||||
selection keywords in the Colvars configuration file or files. There is
|
||||
no need to define multiple ``fix colvars`` instances and it is not
|
||||
no need to define multiple "fix colvars" instances and it is not
|
||||
allowed.
|
||||
|
||||
The *output* keyword allows to specify the prefix of output files
|
||||
generated by Colvars, for example ``output.colvars.traj`` or
|
||||
``output.pmf``.
|
||||
The "output" keyword allows to specify the prefix of output files generated
|
||||
by Colvars, for example "*output*.colvars.traj" or "output.pmf". Supplying
|
||||
an empty string suppresses any file output from Colvars to file, except for
|
||||
data saved into the LAMMPS :doc:`binary restart <restart>` files.
|
||||
|
||||
The *input* keyword allows to specify an optional state file that
|
||||
contains the restart information needed to continue a previous
|
||||
simulation state. Note, however, that ``fix colvars`` records its state
|
||||
in :doc:`binary restart <restart>` files, so when using the
|
||||
:doc:`read_restart <read_restart>` command, this is usually not needed.
|
||||
The "input" keyword allows to specify an optional state file that contains
|
||||
the restart information needed to continue a previous simulation state.
|
||||
However, because "fix colvars" records its state in LAMMPS :doc:`binary
|
||||
restart <restart>` files, this is usually not needed when using the
|
||||
:doc:`read_restart <read_restart>` command.
|
||||
|
||||
The *seed* keyword contains the seed for the random number generator
|
||||
used by Colvars.
|
||||
|
||||
The *unwrap* keyword controls whether wrapped or unwrapped coordinates
|
||||
are passed to the Colvars library for calculation of the collective
|
||||
variables and the resulting forces. The default is *yes*, i.e. to use
|
||||
the image flags to reconstruct the absolute atom positions. Setting
|
||||
this to *no* will use the current local coordinates that are wrapped
|
||||
back into the simulation cell at each re-neighboring instead. For
|
||||
information about when and how this affects results, please see
|
||||
The *unwrap* keyword controls whether wrapped or unwrapped coordinates are
|
||||
passed to the Colvars library for calculation of the collective variables and
|
||||
the resulting forces. The default is *yes*, i.e. the image flags are used to
|
||||
reconstruct the absolute atom positions. Setting this to *no* will use the
|
||||
current local coordinates that are wrapped back into the simulation cell at
|
||||
each re-neighboring step instead. For information about when and how this
|
||||
affects results, please see
|
||||
`https://colvars.github.io/colvars-refman-lammps/colvars-refman-lammps.html#sec:colvar_atom_groups_wrapping
|
||||
<https://colvars.github.io/colvars-refman-lammps/colvars-refman-lammps.html#sec:colvar_atom_groups_wrapping>`_.
|
||||
|
||||
The *tstat* keyword can be either NULL or the label of a thermostatting
|
||||
fix that thermostats all atoms in the fix colvars group. This will be
|
||||
used to let Colvars know what is the current thermostat target
|
||||
The *tstat* keyword can be either "NULL" or the label of a thermostatting
|
||||
fix that thermostats all atoms in the fix colvars group. This will be
|
||||
used to provide the colvars module with the current thermostat target
|
||||
temperature.
|
||||
|
||||
Restart, fix_modify, output, run start/stop, minimize info
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
The *seed* keyword contains the seed for the random number generator
|
||||
that will be used in the colvars module.
|
||||
|
||||
This fix writes the current status of the colvars module into
|
||||
:doc:`binary restart files <restart>`. This is in addition to the text
|
||||
mode ``.colvars.state`` written by Colvars itself and the information in
|
||||
both files is identical.
|
||||
|
||||
The :doc:`fix_modify <fix_modify>` *energy* option is supported by this
|
||||
fix to add the energy change from the biasing force added by Colvars to
|
||||
the global potential energy of the system as part of :doc:`thermodynamic
|
||||
output <thermo_style>`. The default setting for this fix is
|
||||
:doc:`fix_modify energy no <fix_modify>`.
|
||||
Restarting
|
||||
""""""""""
|
||||
|
||||
The *fix_modify configfile <config file>* option loads Colvars
|
||||
configuration from an additional file. This option can only be used,
|
||||
after the system has been initialized with a :doc:`run <run>` command.
|
||||
This fix writes the current state of the Colvars module into :doc:`binary
|
||||
restart files <restart>`. This is in addition to the text-mode
|
||||
".colvars.state" state file that is written by the Colvars module itself.
|
||||
The information contained in both files is identical, and the binary LAMMPS
|
||||
restart file is also used by fix colvars when :doc:`read_restart
|
||||
<read_restart>` is called in a LAMMPS script. In that case, there is
|
||||
typically no need to specify the *input* keyword.
|
||||
|
||||
The *fix_modify config <quoted string>* option allows to add settings
|
||||
from inline strings. Those have to fit on a single line when enclosed in
|
||||
a pair of double quotes ("), or can span multiple lines when bracketed
|
||||
by a pair of triple double quotes (""", like Python embedded
|
||||
documentation).
|
||||
As long as LAMMPS binary restarts will be used to continue a simulation, it
|
||||
is safe to delete the ".colvars.state" files to save space. However, when a
|
||||
LAMMPS simulation is restarted using :doc:`read_data <read_data>`, the
|
||||
Colvars state file must be available and loaded via the "input" keyword or
|
||||
via a "fix_modify Colvars load" command (see below).
|
||||
|
||||
When restarting, the fix and the Colvars module should be created and
|
||||
configured using either the original configuration file(s).
|
||||
|
||||
|
||||
Output
|
||||
""""""
|
||||
|
||||
This fix computes a global scalar which can be accessed by various
|
||||
:doc:`output commands <Howto_output>`. The scalar is the Colvars energy
|
||||
mentioned above. The scalar value calculated by this fix is
|
||||
"extensive".
|
||||
:doc:`output commands <Howto_output>`. The scalar is the energy due to all
|
||||
external potentials defined in the Colvars configuration. The scalar value
|
||||
calculated by this fix is "extensive".
|
||||
|
||||
Aside from the state information in a ".colvars.state" file, other
|
||||
`output files <https://colvars.github.io/colvars-refman-lammps/colvars-refman-lammps.html#sec:colvars_output>`_
|
||||
are produced by Colvars depending on the type of simulation.
|
||||
For this reason, the "output" keyword is required for fix colvars.
|
||||
|
||||
|
||||
Controlling Colvars via `fix_modify`
|
||||
""""""""""""""""""""""""""""""""""""
|
||||
|
||||
The :doc:`fix_modify <fix_modify>` command may be used on "fix colvars" in
|
||||
either one of two ways:
|
||||
|
||||
(1) Provide updated values for the fix parameters, such as *output*, *input*,
|
||||
*unwrap*, *tstat* and *seed*. Additionally, the :doc:`fix_modify
|
||||
<fix_modify>` *energy* keyword is supported by this fix to add the energy
|
||||
change from the biasing force added by Colvars to the global potential
|
||||
energy of the system as part of :doc:`thermodynamic output <thermo_style>`
|
||||
(the default is :doc:`fix_modify energy no <fix_modify>`).
|
||||
For example, in a multi-step LAMMPS script involving multiple thermostats
|
||||
(e.g. fix nvt followed by fix npt), Colvars can read a new thermostat's
|
||||
target temperature like this:
|
||||
|
||||
.. code-block:: LAMMPS
|
||||
|
||||
fix NVT all nvt ...
|
||||
fix Colvars all colvars <configfile> output equil1 tstat NVT
|
||||
run <NUMSTEPS>
|
||||
unfix nvt
|
||||
fix NPT all n ...
|
||||
fix_modify Colvars tstat NPT
|
||||
fix_modify Colvars output equil2
|
||||
|
||||
|
||||
(2) .. versionadded:: Colvars_2023-06-04 Call one of the scripting
|
||||
functions provided by the Colvars module itself (a full list is available
|
||||
in the Colvars doc). The arguments to these functions are provided as
|
||||
strings and passed to Colvars.
|
||||
|
||||
LAMMPS variables referenced by their string representation
|
||||
"${variable}" will be expanded immediately. Note also that this
|
||||
variable expansion *will also happen within quotes*, similar to what the
|
||||
:doc:`mdi <mdi>` command provides. This feature makes it possible to use
|
||||
the values of certain LAMMPS variables in Colvars configuration strings.
|
||||
For example, to synchronize the LAMMPS and Colvars dump frequencies:
|
||||
|
||||
.. code-block:: LAMMPS
|
||||
|
||||
variable freq index 10000
|
||||
dump myDump all atom/zstd ${freq} dump.atom.zstd
|
||||
fix_modify Colvars config "colvarsTrajFrequency ${freq}"
|
||||
|
||||
.. note::
|
||||
|
||||
Although it is possible to use :doc:`fix_modify <fix_modify>` at any time,
|
||||
its results will only reflect the state of the Colvars module at the end
|
||||
of the most recent "run" or "minimize" command. Any new configuration
|
||||
added via "fix_modify Colvars configfile" or "fix_modify Colvars config"
|
||||
will only be loaded when the simulation resumes. Configuration files or
|
||||
strings will be parsed in the same sequence as they were provided in the
|
||||
LAMMPS script.
|
||||
|
||||
|
||||
Restrictions
|
||||
""""""""""""
|
||||
|
||||
``fix colvars`` is provided by the COLVARS package and is only available
|
||||
if LAMMPS was built with that package. Some of the features also
|
||||
require code available from the LEPTON package. See the :doc:`Build
|
||||
This fix is provided by the COLVARS package and is only available if LAMMPS
|
||||
was built with that package (default in most builds). Some of the features
|
||||
also require code available from the LEPTON package. See the :doc:`Build
|
||||
package <Build_package>` page for more info.
|
||||
|
||||
There can only be one Colvars instance defined at a time. Since the
|
||||
interface communicates only the minimum amount of information and the
|
||||
Colvars module itself can handle an arbitrary number of collective
|
||||
interface communicates only the minimum required amount of information, and
|
||||
the Colvars module itself can handle an arbitrary number of collective
|
||||
variables, this is not a limitation of functionality.
|
||||
|
||||
|
||||
Related commands
|
||||
""""""""""""""""
|
||||
|
||||
:doc:`fix smd <fix_smd>`, :doc:`fix spring <fix_spring>`,
|
||||
:doc:`fix plumed <fix_plumed>`
|
||||
|
||||
Default
|
||||
"""""""
|
||||
|
||||
The default options are input = NULL, output = out, seed = 1966, unwrap yes,
|
||||
and tstat = NULL.
|
||||
|
||||
----------
|
||||
|
||||
.. _Fiorin:
|
||||
**(Fiorin)** Fiorin, Klein, Henin, Mol. Phys. 111, 3345 (2013) https://doi.org/10.1080/00268976.2013.813594
|
||||
|
||||
**(Fiorin)** Fiorin, Klein, Henin, Mol. Phys., DOI:10.1080/00268976.2013.813594
|
||||
.. _Colvars_LAMMPS_doc:
|
||||
https://colvars.github.io/colvars-refman-lammps/colvars-refman-lammps.html
|
||||
|
||||
@ -9,78 +9,42 @@
|
||||
// Colvars repository at GitHub.
|
||||
|
||||
|
||||
#include "colvarproxy_lammps.h"
|
||||
#include <mpi.h>
|
||||
#include <sys/stat.h>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "lammps.h"
|
||||
#include "error.h"
|
||||
#include "output.h"
|
||||
#include "utils.h"
|
||||
#include "random_park.h"
|
||||
|
||||
#include "colvarmodule.h"
|
||||
#include "colvarproxy.h"
|
||||
|
||||
#include <sstream>
|
||||
#include "colvarproxy_lammps.h"
|
||||
#include "colvarscript.h"
|
||||
|
||||
#define HASH_FAIL -1
|
||||
|
||||
|
||||
colvarproxy_lammps::colvarproxy_lammps(LAMMPS_NS::LAMMPS *lmp,
|
||||
const char *inp_name,
|
||||
const char *out_name,
|
||||
const int seed,
|
||||
const double temp,
|
||||
MPI_Comm root2root)
|
||||
: _lmp(lmp), inter_comm(root2root)
|
||||
colvarproxy_lammps::colvarproxy_lammps(LAMMPS_NS::LAMMPS *lmp)
|
||||
: _lmp(lmp)
|
||||
{
|
||||
if (cvm::debug())
|
||||
log("Initializing the colvars proxy object.\n");
|
||||
_random = nullptr;
|
||||
|
||||
_random = new LAMMPS_NS::RanPark(lmp,seed);
|
||||
engine_name_ = "LAMMPS";
|
||||
|
||||
first_timestep=true;
|
||||
previous_step=-1;
|
||||
do_exit=false;
|
||||
first_timestep = true;
|
||||
previous_step = -1;
|
||||
do_exit = false;
|
||||
|
||||
inter_me = 0;
|
||||
inter_num = 1;
|
||||
|
||||
engine_ready_ = false;
|
||||
|
||||
// set input restart name and strip the extension, if present
|
||||
input_prefix_str = std::string(inp_name ? inp_name : "");
|
||||
if (input_prefix_str.rfind(".colvars.state") != std::string::npos)
|
||||
input_prefix_str.erase(input_prefix_str.rfind(".colvars.state"),
|
||||
std::string(".colvars.state").size());
|
||||
|
||||
// output prefix is always given
|
||||
output_prefix_str = std::string(out_name);
|
||||
// not so for restarts
|
||||
restart_output_prefix_str = std::string("rest");
|
||||
|
||||
// check if it is possible to save output configuration
|
||||
if ((!output_prefix_str.size()) && (!restart_output_prefix_str.size())) {
|
||||
error("Error: neither the final output state file or "
|
||||
"the output restart file could be defined, exiting.\n");
|
||||
}
|
||||
|
||||
// try to extract a restart prefix from a potential restart command.
|
||||
LAMMPS_NS::Output *outp = _lmp->output;
|
||||
if ((outp->restart_every_single > 0) && (outp->restart1 != nullptr)) {
|
||||
restart_frequency_engine = outp->restart_every_single;
|
||||
restart_output_prefix_str = std::string(outp->restart1);
|
||||
} else if ((outp->restart_every_double > 0) && (outp->restart2a != nullptr)) {
|
||||
restart_frequency_engine = outp->restart_every_double;
|
||||
restart_output_prefix_str = std::string(outp->restart2a);
|
||||
}
|
||||
// trim off unwanted stuff from the restart prefix
|
||||
if (restart_output_prefix_str.rfind(".*") != std::string::npos)
|
||||
restart_output_prefix_str.erase(restart_output_prefix_str.rfind(".*"),2);
|
||||
|
||||
// initialize multi-replica support, if available
|
||||
if (replica_enabled() == COLVARS_OK) {
|
||||
MPI_Comm_rank(inter_comm, &inter_me);
|
||||
MPI_Comm_size(inter_comm, &inter_num);
|
||||
}
|
||||
|
||||
if (cvm::debug())
|
||||
log("Done initializing the colvars proxy object.\n");
|
||||
}
|
||||
|
||||
|
||||
@ -91,63 +55,58 @@ void colvarproxy_lammps::init()
|
||||
// create the colvarmodule instance
|
||||
colvars = new colvarmodule(this);
|
||||
|
||||
// Create instance of scripting interface
|
||||
script = new colvarscript(this, colvars);
|
||||
|
||||
cvm::log("Using LAMMPS interface, version "+
|
||||
cvm::to_str(COLVARPROXY_VERSION)+".\n");
|
||||
|
||||
colvars->cite_feature("LAMMPS engine");
|
||||
colvars->cite_feature("Colvars-LAMMPS interface");
|
||||
|
||||
my_angstrom = _lmp->force->angstrom;
|
||||
// Front-end unit is the same as back-end
|
||||
angstrom_value_ = my_angstrom;
|
||||
|
||||
// my_kcal_mol = _lmp->force->qe2f / 23.060549;
|
||||
// force->qe2f is 1eV expressed in LAMMPS' energy unit (1 if unit is eV, 23 if kcal/mol)
|
||||
angstrom_value_ = _lmp->force->angstrom;
|
||||
boltzmann_ = _lmp->force->boltz;
|
||||
my_timestep = _lmp->update->dt * _lmp->force->femtosecond;
|
||||
set_integration_timestep(_lmp->update->dt * _lmp->force->femtosecond);
|
||||
|
||||
if (_lmp->update->ntimestep != 0) {
|
||||
cvm::log("Setting initial step number from LAMMPS: "+
|
||||
cvm::to_str(_lmp->update->ntimestep)+"\n");
|
||||
colvarmodule::it = colvarmodule::it_restart =
|
||||
static_cast<cvm::step_number>(_lmp->update->ntimestep);
|
||||
}
|
||||
|
||||
if (cvm::debug()) {
|
||||
cvm::log("atoms_ids = "+cvm::to_str(atoms_ids)+"\n");
|
||||
cvm::log("atoms_refcount = "+cvm::to_str(atoms_refcount)+"\n");
|
||||
cvm::log("atoms_positions = "+cvm::to_str(atoms_positions)+"\n");
|
||||
cvm::log(cvm::line_marker);
|
||||
cvm::log("Info: done initializing the colvars proxy object.\n");
|
||||
colvars->set_initial_step(static_cast<cvm::step_number>(_lmp->update->ntimestep));
|
||||
}
|
||||
}
|
||||
|
||||
int colvarproxy_lammps::add_config_file(const char *conf_file)
|
||||
{
|
||||
return colvars->read_config_file(conf_file);
|
||||
}
|
||||
|
||||
int colvarproxy_lammps::add_config_string(const std::string &conf)
|
||||
{
|
||||
return colvars->read_config_string(conf);
|
||||
}
|
||||
|
||||
int colvarproxy_lammps::read_state_file(char const *state_filename)
|
||||
{
|
||||
input_prefix() = std::string(state_filename);
|
||||
return colvars->setup_input();
|
||||
}
|
||||
|
||||
colvarproxy_lammps::~colvarproxy_lammps()
|
||||
{
|
||||
delete _random;
|
||||
if (_random) {
|
||||
delete _random;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void colvarproxy_lammps::set_random_seed(int seed)
|
||||
{
|
||||
if (_random) {
|
||||
delete _random;
|
||||
}
|
||||
_random = new LAMMPS_NS::RanPark(_lmp, seed);
|
||||
}
|
||||
|
||||
|
||||
void colvarproxy_lammps::set_replicas_communicator(MPI_Comm root2root)
|
||||
{
|
||||
inter_comm = root2root;
|
||||
// initialize multi-replica support, if available
|
||||
if (replica_enabled() == COLVARS_OK) {
|
||||
MPI_Comm_rank(inter_comm, &inter_me);
|
||||
MPI_Comm_size(inter_comm, &inter_num);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// re-initialize data where needed
|
||||
int colvarproxy_lammps::setup()
|
||||
{
|
||||
int error_code = colvarproxy::setup();
|
||||
my_timestep = _lmp->update->dt * _lmp->force->femtosecond;
|
||||
set_integration_timestep(_lmp->update->dt * _lmp->force->femtosecond);
|
||||
error_code |= colvars->update_engine_parameters();
|
||||
error_code |= colvars->setup_input();
|
||||
error_code |= colvars->setup_output();
|
||||
@ -238,26 +197,6 @@ double colvarproxy_lammps::compute()
|
||||
return bias_energy;
|
||||
}
|
||||
|
||||
void colvarproxy_lammps::serialize_status(std::string &rst)
|
||||
{
|
||||
std::ostringstream os;
|
||||
colvars->write_restart(os);
|
||||
rst = os.str();
|
||||
}
|
||||
|
||||
// set status from string
|
||||
bool colvarproxy_lammps::deserialize_status(std::string &rst)
|
||||
{
|
||||
std::istringstream is;
|
||||
is.str(rst);
|
||||
|
||||
if (!colvars->read_restart(is)) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
cvm::rvector colvarproxy_lammps::position_distance(cvm::atom_pos const &pos1,
|
||||
cvm::atom_pos const &pos2)
|
||||
@ -292,6 +231,24 @@ void colvarproxy_lammps::error(std::string const &message)
|
||||
}
|
||||
|
||||
|
||||
char const *colvarproxy_lammps::script_obj_to_str(unsigned char *obj)
|
||||
{
|
||||
// For now we assume that all objects passed by FixColvars are strings
|
||||
return reinterpret_cast<char *>(obj);
|
||||
}
|
||||
|
||||
|
||||
std::vector<std::string> colvarproxy_lammps::script_obj_to_str_vector(unsigned char *obj)
|
||||
{
|
||||
if (cvm::debug()) {
|
||||
cvm::log("Called colvarproxy_lammps::script_obj_to_str_vector().\n");
|
||||
}
|
||||
std::string const input(reinterpret_cast<char *>(obj));
|
||||
return LAMMPS_NS::utils::split_words(input); // :-)))
|
||||
}
|
||||
|
||||
|
||||
|
||||
int colvarproxy_lammps::set_unit_system(std::string const &units_in, bool /*check_only*/)
|
||||
{
|
||||
std::string lmp_units = _lmp->update->unit_style;
|
||||
|
||||
@ -33,9 +33,8 @@ class colvarproxy_lammps : public colvarproxy {
|
||||
LAMMPS_NS::RanPark *_random;
|
||||
|
||||
// state of LAMMPS properties
|
||||
double my_timestep, my_angstrom;
|
||||
double bias_energy;
|
||||
int previous_step;
|
||||
cvm::step_number previous_step;
|
||||
|
||||
bool first_timestep;
|
||||
bool do_exit;
|
||||
@ -47,10 +46,19 @@ class colvarproxy_lammps : public colvarproxy {
|
||||
|
||||
public:
|
||||
friend class cvm::atom;
|
||||
colvarproxy_lammps(LAMMPS_NS::LAMMPS *lmp, const char *, const char *, const int, const double,
|
||||
MPI_Comm);
|
||||
|
||||
colvarproxy_lammps(LAMMPS_NS::LAMMPS *lmp);
|
||||
|
||||
~colvarproxy_lammps() override;
|
||||
|
||||
void init();
|
||||
|
||||
/// Set the internal seed used by \link rand_gaussian() \endlink
|
||||
void set_random_seed(int seed);
|
||||
|
||||
/// Set the multiple replicas communicator
|
||||
void set_replicas_communicator(MPI_Comm root2root);
|
||||
|
||||
int setup() override;
|
||||
|
||||
// disable default and copy constructor
|
||||
@ -68,28 +76,14 @@ class colvarproxy_lammps : public colvarproxy {
|
||||
// perform colvars computation. returns biasing energy
|
||||
double compute();
|
||||
|
||||
// dump status to string
|
||||
void serialize_status(std::string &);
|
||||
|
||||
// set status from string
|
||||
bool deserialize_status(std::string &);
|
||||
|
||||
// read additional config from file
|
||||
int add_config_file(char const *config_filename);
|
||||
|
||||
// read additional config from string
|
||||
int add_config_string(const std::string &config);
|
||||
|
||||
// load a state file
|
||||
int read_state_file(char const *state_filename);
|
||||
|
||||
// Request to set the units used internally by Colvars
|
||||
int set_unit_system(std::string const &units_in, bool check_only) override;
|
||||
|
||||
inline cvm::real dt() override
|
||||
{
|
||||
return my_timestep;
|
||||
}; // return _lmp->update->dt * _lmp->force->femtosecond; };
|
||||
/// Convert a command-line argument to string
|
||||
char const *script_obj_to_str(unsigned char *obj);
|
||||
|
||||
/// Convert a command-line argument to a vector of strings
|
||||
std::vector<std::string> script_obj_to_str_vector(unsigned char *obj);
|
||||
|
||||
void add_energy(cvm::real energy) override { bias_energy += energy; };
|
||||
void request_total_force(bool yesno) override { total_force_requested = yesno; };
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
#ifndef COLVARPROXY_VERSION
|
||||
#define COLVARPROXY_VERSION "2023-04-12"
|
||||
#define COLVARPROXY_VERSION "2024-07-05"
|
||||
#endif
|
||||
|
||||
@ -23,26 +23,33 @@
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
Contributing author: Axel Kohlmeyer (Temple U)
|
||||
Currently maintained by: Giacomo Fiorin (NIH)
|
||||
------------------------------------------------------------------------- */
|
||||
|
||||
#include "fix_colvars.h"
|
||||
#include "inthash.h"
|
||||
|
||||
#include "atom.h"
|
||||
#include "citeme.h"
|
||||
#include "comm.h"
|
||||
#include "domain.h"
|
||||
#include "error.h"
|
||||
#include "input.h"
|
||||
#include "memory.h"
|
||||
#include "modify.h"
|
||||
#include "output.h"
|
||||
#include "respa.h"
|
||||
#include "universe.h"
|
||||
#include "update.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include "colvarproxy_lammps.h"
|
||||
#include "colvarmodule.h"
|
||||
#include "colvarproxy.h"
|
||||
#include "colvarproxy_lammps.h"
|
||||
#include "colvarscript.h"
|
||||
#include "colvars_memstream.h"
|
||||
|
||||
|
||||
/* struct for packed data communication of coordinates and forces. */
|
||||
@ -58,197 +65,11 @@ inline std::ostream & operator<< (std::ostream &out, const LAMMPS_NS::commdata &
|
||||
return out;
|
||||
}
|
||||
|
||||
/* re-usable integer hash table code with static linkage. */
|
||||
|
||||
/** hash table top level data structure */
|
||||
typedef struct inthash_t {
|
||||
struct inthash_node_t **bucket; /* array of hash nodes */
|
||||
int size; /* size of the array */
|
||||
int entries; /* number of entries in table */
|
||||
int downshift; /* shift cound, used in hash function */
|
||||
int mask; /* used to select bits for hashing */
|
||||
} inthash_t;
|
||||
|
||||
/** hash table node data structure */
|
||||
typedef struct inthash_node_t {
|
||||
int data; /* data in hash node */
|
||||
int key; /* key for hash lookup */
|
||||
struct inthash_node_t *next; /* next node in hash chain */
|
||||
} inthash_node_t;
|
||||
|
||||
#define HASH_FAIL -1
|
||||
#define HASH_LIMIT 0.5
|
||||
|
||||
/* initialize new hash table */
|
||||
static void inthash_init(inthash_t *tptr, int buckets);
|
||||
/* lookup entry in hash table */
|
||||
static int inthash_lookup(void *tptr, int key);
|
||||
/* insert an entry into hash table. */
|
||||
static int inthash_insert(inthash_t *tptr, int key, int data);
|
||||
/* delete the hash table */
|
||||
static void inthash_destroy(inthash_t *tptr);
|
||||
|
||||
/************************************************************************
|
||||
* integer hash code:
|
||||
************************************************************************/
|
||||
|
||||
/* inthash() - Hash function returns a hash number for a given key.
|
||||
* tptr: Pointer to a hash table, key: The key to create a hash number for */
|
||||
static int inthash(const inthash_t *tptr, int key) {
|
||||
int hashvalue;
|
||||
|
||||
hashvalue = (((key*1103515249)>>tptr->downshift) & tptr->mask);
|
||||
if (hashvalue < 0) {
|
||||
hashvalue = 0;
|
||||
}
|
||||
|
||||
return hashvalue;
|
||||
}
|
||||
|
||||
/*
|
||||
* rebuild_table_int() - Create new hash table when old one fills up.
|
||||
*
|
||||
* tptr: Pointer to a hash table
|
||||
*/
|
||||
static void rebuild_table_int(inthash_t *tptr) {
|
||||
inthash_node_t **old_bucket, *old_hash, *tmp;
|
||||
int old_size, h, i;
|
||||
|
||||
old_bucket=tptr->bucket;
|
||||
old_size=tptr->size;
|
||||
|
||||
/* create a new table and rehash old buckets */
|
||||
inthash_init(tptr, old_size<<1);
|
||||
for (i=0; i<old_size; i++) {
|
||||
old_hash=old_bucket[i];
|
||||
while (old_hash) {
|
||||
tmp=old_hash;
|
||||
old_hash=old_hash->next;
|
||||
h=inthash(tptr, tmp->key);
|
||||
tmp->next=tptr->bucket[h];
|
||||
tptr->bucket[h]=tmp;
|
||||
tptr->entries++;
|
||||
} /* while */
|
||||
} /* for */
|
||||
|
||||
/* free memory used by old table */
|
||||
free(old_bucket);
|
||||
}
|
||||
|
||||
/*
|
||||
* inthash_init() - Initialize a new hash table.
|
||||
*
|
||||
* tptr: Pointer to the hash table to initialize
|
||||
* buckets: The number of initial buckets to create
|
||||
*/
|
||||
void inthash_init(inthash_t *tptr, int buckets) {
|
||||
|
||||
/* make sure we allocate something */
|
||||
if (buckets==0)
|
||||
buckets=16;
|
||||
|
||||
/* initialize the table */
|
||||
tptr->entries=0;
|
||||
tptr->size=2;
|
||||
tptr->mask=1;
|
||||
tptr->downshift=29;
|
||||
|
||||
/* ensure buckets is a power of 2 */
|
||||
while (tptr->size<buckets) {
|
||||
tptr->size<<=1;
|
||||
tptr->mask=(tptr->mask<<1)+1;
|
||||
tptr->downshift--;
|
||||
} /* while */
|
||||
|
||||
/* allocate memory for table */
|
||||
tptr->bucket=(inthash_node_t **) calloc(tptr->size, sizeof(inthash_node_t *));
|
||||
}
|
||||
|
||||
/*
|
||||
* inthash_lookup() - Lookup an entry in the hash table and return a pointer to
|
||||
* it or HASH_FAIL if it wasn't found.
|
||||
*
|
||||
* tptr: Pointer to the hash table
|
||||
* key: The key to lookup
|
||||
*/
|
||||
int inthash_lookup(void *ptr, int key) {
|
||||
const inthash_t *tptr = (const inthash_t *) ptr;
|
||||
int h;
|
||||
inthash_node_t *node;
|
||||
|
||||
|
||||
/* find the entry in the hash table */
|
||||
h=inthash(tptr, key);
|
||||
for (node=tptr->bucket[h]; node!=nullptr; node=node->next) {
|
||||
if (node->key == key)
|
||||
break;
|
||||
}
|
||||
|
||||
/* return the entry if it exists, or HASH_FAIL */
|
||||
return(node ? node->data : HASH_FAIL);
|
||||
}
|
||||
|
||||
/*
|
||||
* inthash_insert() - Insert an entry into the hash table. If the entry already
|
||||
* exists return a pointer to it, otherwise return HASH_FAIL.
|
||||
*
|
||||
* tptr: A pointer to the hash table
|
||||
* key: The key to insert into the hash table
|
||||
* data: A pointer to the data to insert into the hash table
|
||||
*/
|
||||
int inthash_insert(inthash_t *tptr, int key, int data) {
|
||||
int tmp;
|
||||
inthash_node_t *node;
|
||||
int h;
|
||||
|
||||
/* check to see if the entry exists */
|
||||
if ((tmp=inthash_lookup(tptr, key)) != HASH_FAIL)
|
||||
return(tmp);
|
||||
|
||||
/* expand the table if needed */
|
||||
while (tptr->entries>=HASH_LIMIT*tptr->size)
|
||||
rebuild_table_int(tptr);
|
||||
|
||||
/* insert the new entry */
|
||||
h=inthash(tptr, key);
|
||||
node=(struct inthash_node_t *) malloc(sizeof(inthash_node_t));
|
||||
node->data=data;
|
||||
node->key=key;
|
||||
node->next=tptr->bucket[h];
|
||||
tptr->bucket[h]=node;
|
||||
tptr->entries++;
|
||||
|
||||
return HASH_FAIL;
|
||||
}
|
||||
|
||||
/*
|
||||
* inthash_destroy() - Delete the entire table, and all remaining entries.
|
||||
*
|
||||
*/
|
||||
void inthash_destroy(inthash_t *tptr) {
|
||||
inthash_node_t *node, *last;
|
||||
int i;
|
||||
|
||||
for (i=0; i<tptr->size; i++) {
|
||||
node = tptr->bucket[i];
|
||||
while (node != nullptr) {
|
||||
last = node;
|
||||
node = node->next;
|
||||
free(last);
|
||||
}
|
||||
}
|
||||
|
||||
/* free the entire array of buckets */
|
||||
if (tptr->bucket != nullptr) {
|
||||
free(tptr->bucket);
|
||||
memset(tptr, 0, sizeof(inthash_t));
|
||||
}
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
|
||||
using namespace LAMMPS_NS;
|
||||
using namespace FixConst;
|
||||
using namespace IntHash_NS;
|
||||
|
||||
// initialize static class members
|
||||
int FixColvars::instances=0;
|
||||
@ -287,72 +108,137 @@ FixColvars::FixColvars(LAMMPS *lmp, int narg, char **arg) :
|
||||
|
||||
me = comm->me;
|
||||
root2root = MPI_COMM_NULL;
|
||||
proxy = nullptr;
|
||||
|
||||
if (strcmp(arg[3], "none") == 0) {
|
||||
conf_file = nullptr;
|
||||
} else {
|
||||
conf_file = utils::strdup(arg[3]);
|
||||
}
|
||||
|
||||
conf_file = utils::strdup(arg[3]);
|
||||
rng_seed = 1966;
|
||||
unwrap_flag = 1;
|
||||
|
||||
inp_name = nullptr;
|
||||
out_name = nullptr;
|
||||
tmp_name = nullptr;
|
||||
|
||||
/* parse optional arguments */
|
||||
int iarg = 4;
|
||||
while (iarg < narg) {
|
||||
// we have keyword/value pairs. check if value is missing
|
||||
if (iarg+1 == narg)
|
||||
error->all(FLERR,"Missing argument to keyword");
|
||||
|
||||
if (0 == strcmp(arg[iarg], "input")) {
|
||||
inp_name = utils::strdup(arg[iarg+1]);
|
||||
} else if (0 == strcmp(arg[iarg], "output")) {
|
||||
out_name = utils::strdup(arg[iarg+1]);
|
||||
} else if (0 == strcmp(arg[iarg], "seed")) {
|
||||
rng_seed = utils::inumeric(FLERR,arg[iarg+1],false,lmp);
|
||||
} else if (0 == strcmp(arg[iarg], "unwrap")) {
|
||||
unwrap_flag = utils::logical(FLERR,arg[iarg+1],false,lmp);
|
||||
} else if (0 == strcmp(arg[iarg], "tstat")) {
|
||||
tmp_name = utils::strdup(arg[iarg+1]);
|
||||
} else {
|
||||
error->all(FLERR,"Unknown fix colvars parameter");
|
||||
}
|
||||
++iarg; ++iarg;
|
||||
}
|
||||
|
||||
if (!out_name) out_name = utils::strdup("out");
|
||||
tfix_name = nullptr;
|
||||
|
||||
/* initialize various state variables. */
|
||||
tstat_fix = nullptr;
|
||||
energy = 0.0;
|
||||
nlevels_respa = 0;
|
||||
init_flag = 0;
|
||||
num_coords = 0;
|
||||
comm_buf = nullptr;
|
||||
taglist = nullptr;
|
||||
force_buf = nullptr;
|
||||
proxy = nullptr;
|
||||
idmap = nullptr;
|
||||
|
||||
script_args[0] = reinterpret_cast<unsigned char *>(strdup("fix_modify"));
|
||||
|
||||
parse_fix_arguments(narg, arg, true);
|
||||
|
||||
if (!out_name) out_name = utils::strdup("out");
|
||||
|
||||
if (me == 0) {
|
||||
#ifdef LAMMPS_BIGBIG
|
||||
utils::logmesg(lmp, "colvars: Warning: cannot handle atom ids > 2147483647\n");
|
||||
#endif
|
||||
proxy = new colvarproxy_lammps(lmp);
|
||||
proxy->init();
|
||||
proxy->set_random_seed(rng_seed);
|
||||
proxy->set_target_temperature(t_target);
|
||||
if (conf_file) {
|
||||
proxy->add_config("configfile", conf_file);
|
||||
}
|
||||
}
|
||||
|
||||
/* storage required to communicate a single coordinate or force. */
|
||||
size_one = sizeof(struct commdata);
|
||||
}
|
||||
|
||||
/*********************************
|
||||
* Clean up on deleting the fix. *
|
||||
*********************************/
|
||||
|
||||
int FixColvars::parse_fix_arguments(int narg, char **arg, bool fix_constructor)
|
||||
{
|
||||
int const iarg_start = fix_constructor ? 4 : 0;
|
||||
int iarg = iarg_start;
|
||||
while (iarg < narg) {
|
||||
|
||||
bool is_fix_keyword = false;
|
||||
|
||||
if (0 == strcmp(arg[iarg], "input")) {
|
||||
inp_name = utils::strdup(arg[iarg+1]);
|
||||
// input prefix is set in FixColvars::setup()
|
||||
is_fix_keyword = true;
|
||||
} else if (0 == strcmp(arg[iarg], "output")) {
|
||||
out_name = utils::strdup(arg[iarg+1]);
|
||||
// output prefix is set in FixColvars::setup()
|
||||
is_fix_keyword = true;
|
||||
} else if (0 == strcmp(arg[iarg], "seed")) {
|
||||
rng_seed = utils::inumeric(FLERR, arg[iarg+1], false, lmp);
|
||||
is_fix_keyword = true;
|
||||
} else if (0 == strcmp(arg[iarg], "unwrap")) {
|
||||
unwrap_flag = utils::logical(FLERR, arg[iarg+1], false, lmp);
|
||||
is_fix_keyword = true;
|
||||
} else if (0 == strcmp(arg[iarg], "tstat")) {
|
||||
tfix_name = utils::strdup(arg[iarg+1]);
|
||||
if (me == 0) set_thermostat_temperature();
|
||||
is_fix_keyword = true;
|
||||
}
|
||||
|
||||
if (is_fix_keyword) {
|
||||
|
||||
// Valid LAMMPS fix keyword: raise error if it has no argument
|
||||
if (iarg + 1 == narg) {
|
||||
if (fix_constructor) {
|
||||
error->all(FLERR, ("Missing argument to keyword \""+
|
||||
std::string(arg[iarg]) +"\""));
|
||||
} else {
|
||||
// Error code consistent with Fix::modify_param()
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (fix_constructor) {
|
||||
error->all(FLERR, "Unrecognized fix colvars argument: please note that "
|
||||
"Colvars script commands are not allowed until after the "
|
||||
"fix is created");
|
||||
} else {
|
||||
if (iarg > iarg_start) {
|
||||
error->all(FLERR,
|
||||
"Unrecognized fix colvars argument: please note that "
|
||||
"you cannot combine fix colvars keywords and Colvars "
|
||||
"script commands in the same line");
|
||||
} else {
|
||||
// Return negative error code to try the Colvars script commands
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
iarg += 2;
|
||||
}
|
||||
|
||||
return iarg;
|
||||
}
|
||||
|
||||
|
||||
FixColvars::~FixColvars()
|
||||
{
|
||||
delete[] conf_file;
|
||||
delete[] inp_name;
|
||||
delete[] out_name;
|
||||
delete[] tmp_name;
|
||||
delete[] tfix_name;
|
||||
memory->sfree(comm_buf);
|
||||
|
||||
if (proxy) {
|
||||
delete proxy;
|
||||
inthash_t *hashtable = (inthash_t *)idmap;
|
||||
inthash_destroy(hashtable);
|
||||
delete hashtable;
|
||||
}
|
||||
|
||||
if (idmap) {
|
||||
inthash_destroy(idmap);
|
||||
delete idmap;
|
||||
}
|
||||
|
||||
if (root2root != MPI_COMM_NULL)
|
||||
@ -374,31 +260,20 @@ int FixColvars::setmask()
|
||||
return mask;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
// initial checks for colvars run.
|
||||
|
||||
void FixColvars::init()
|
||||
{
|
||||
if (atom->tag_enable == 0)
|
||||
error->all(FLERR,"Cannot use fix colvars without atom IDs");
|
||||
error->all(FLERR, "Cannot use fix colvars without atom IDs");
|
||||
|
||||
if (atom->map_style == Atom::MAP_NONE)
|
||||
error->all(FLERR,"Fix colvars requires an atom map, see atom_modify");
|
||||
error->all(FLERR, "Fix colvars requires an atom map, see atom_modify");
|
||||
|
||||
if ((me == 0) && (update->whichflag == 2))
|
||||
error->warning(FLERR,"Using fix colvars with minimization");
|
||||
error->warning(FLERR, "Using fix colvars with minimization");
|
||||
|
||||
if (utils::strmatch(update->integrate_style,"^respa"))
|
||||
if (utils::strmatch(update->integrate_style, "^respa"))
|
||||
nlevels_respa = ((Respa *) update->integrate)->nlevels;
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
void FixColvars::one_time_init()
|
||||
{
|
||||
int i,tmp;
|
||||
|
||||
if (init_flag) return;
|
||||
init_flag = 1;
|
||||
@ -406,103 +281,188 @@ void FixColvars::one_time_init()
|
||||
if (universe->nworlds > 1) {
|
||||
// create inter root communicator
|
||||
int color = 1;
|
||||
if (me == 0) color = 0;
|
||||
MPI_Comm_split(universe->uworld,color,universe->iworld,&root2root);
|
||||
if (me == 0) {
|
||||
color = 0;
|
||||
}
|
||||
MPI_Comm_split(universe->uworld, color, universe->iworld, &root2root);
|
||||
if (me == 0) {
|
||||
proxy->set_replicas_communicator(root2root);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// create and initialize the colvars proxy
|
||||
|
||||
void FixColvars::set_thermostat_temperature()
|
||||
{
|
||||
if (me == 0) {
|
||||
utils::logmesg(lmp,"colvars: Creating proxy instance\n");
|
||||
|
||||
#ifdef LAMMPS_BIGBIG
|
||||
utils::logmesg(lmp,"colvars: cannot handle atom ids > 2147483647\n");
|
||||
#endif
|
||||
|
||||
if (inp_name) {
|
||||
if (strcmp(inp_name,"NULL") == 0) {
|
||||
delete[] inp_name;
|
||||
inp_name = nullptr;
|
||||
if (tfix_name) {
|
||||
if (strcmp(tfix_name, "NULL") != 0) {
|
||||
Fix *tstat_fix = modify->get_fix_by_id(tfix_name);
|
||||
if (!tstat_fix) {
|
||||
error->one(FLERR, "Could not find thermostat fix ID {}", tfix_name);
|
||||
}
|
||||
int tmp = 0;
|
||||
double *tt = reinterpret_cast<double *>(tstat_fix->extract("t_target",
|
||||
tmp));
|
||||
if (tt) {
|
||||
t_target = *tt;
|
||||
} else {
|
||||
error->one(FLERR, "Fix ID {} is not a thermostat fix", tfix_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// try to determine thermostat target temperature
|
||||
double t_target = 0.0;
|
||||
if (tmp_name) {
|
||||
if (strcmp(tmp_name,"NULL") == 0) {
|
||||
tstat_fix = nullptr;
|
||||
} else {
|
||||
tstat_fix = modify->get_fix_by_id(tmp_name);
|
||||
if (!tstat_fix) error->one(FLERR, "Could not find thermostat fix ID {}", tmp_name);
|
||||
double *tt = (double*) tstat_fix->extract("t_target", tmp);
|
||||
if (tt) t_target = *tt;
|
||||
else error->one(FLERR, "Fix ID {} is not a thermostat fix", tmp_name);
|
||||
}
|
||||
}
|
||||
|
||||
proxy = new colvarproxy_lammps(lmp,inp_name,out_name,rng_seed,t_target,root2root);
|
||||
proxy->init();
|
||||
proxy->add_config("configfile", conf_file);
|
||||
proxy->parse_module_config();
|
||||
|
||||
num_coords = (proxy->modify_atom_positions()->size());
|
||||
}
|
||||
}
|
||||
|
||||
// send the list of all colvar atom IDs to all nodes.
|
||||
// also initialize and build hashtable on master.
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
MPI_Bcast(&num_coords, 1, MPI_INT, 0, world);
|
||||
memory->create(taglist,num_coords,"colvars:taglist");
|
||||
memory->create(force_buf,3*num_coords,"colvars:force_buf");
|
||||
void FixColvars::init_taglist()
|
||||
{
|
||||
int new_taglist_size = -1;
|
||||
|
||||
if (me == 0) {
|
||||
|
||||
// Number of atoms requested by Colvars
|
||||
num_coords = static_cast<int>(proxy->modify_atom_positions()->size());
|
||||
|
||||
if (proxy->modified_atom_list()) {
|
||||
new_taglist_size = num_coords;
|
||||
proxy->reset_modified_atom_list();
|
||||
} else {
|
||||
new_taglist_size = -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Broadcast number of colvar atoms; negative means no updates
|
||||
MPI_Bcast(&new_taglist_size, 1, MPI_INT, 0, world);
|
||||
|
||||
if (new_taglist_size < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
num_coords = new_taglist_size;
|
||||
|
||||
if (taglist) {
|
||||
memory->destroy(taglist);
|
||||
memory->destroy(force_buf);
|
||||
}
|
||||
memory->create(taglist, num_coords, "colvars:taglist");
|
||||
memory->create(force_buf, 3*num_coords, "colvars:force_buf");
|
||||
|
||||
if (me == 0) {
|
||||
|
||||
// Initialize and build hashtable on MPI rank 0
|
||||
|
||||
std::vector<int> const &tl = *(proxy->get_atom_ids());
|
||||
inthash_t *hashtable=new inthash_t;
|
||||
inthash_init(hashtable, num_coords);
|
||||
idmap = (void *)hashtable;
|
||||
|
||||
for (i=0; i < num_coords; ++i) {
|
||||
if (idmap) {
|
||||
delete idmap;
|
||||
idmap = nullptr;
|
||||
}
|
||||
|
||||
idmap = new inthash_t;
|
||||
inthash_init(idmap, num_coords);
|
||||
for (int i = 0; i < num_coords; ++i) {
|
||||
taglist[i] = tl[i];
|
||||
inthash_insert(hashtable, tl[i], i);
|
||||
inthash_insert(idmap, tl[i], i);
|
||||
}
|
||||
}
|
||||
|
||||
// Broadcast colvar atom ID list
|
||||
MPI_Bcast(taglist, num_coords, MPI_LMP_TAGINT, 0, world);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
int FixColvars::modify_param(int narg, char **arg)
|
||||
{
|
||||
if (strcmp(arg[0],"configfile") == 0) {
|
||||
if (narg < 2) error->all(FLERR,"Illegal fix_modify command");
|
||||
if (me == 0) {
|
||||
if (! proxy)
|
||||
error->one(FLERR,"Cannot use fix_modify before initialization");
|
||||
return proxy->add_config_file(arg[1]) == COLVARS_OK ? 2 : 0;
|
||||
}
|
||||
return 2;
|
||||
} else if (strcmp(arg[0],"config") == 0) {
|
||||
if (narg < 2) error->all(FLERR,"Illegal fix_modify command");
|
||||
if (me == 0) {
|
||||
if (! proxy)
|
||||
error->one(FLERR,"Cannot use fix_modify before initialization");
|
||||
std::string const conf(arg[1]);
|
||||
return proxy->add_config_string(conf) == COLVARS_OK ? 2 : 0;
|
||||
}
|
||||
return 2;
|
||||
} else if (strcmp(arg[0],"load") == 0) {
|
||||
if (narg < 2) error->all(FLERR,"Illegal fix_modify command");
|
||||
if (me == 0) {
|
||||
if (! proxy)
|
||||
error->one(FLERR,"Cannot use fix_modify before initialization");
|
||||
return proxy->read_state_file(arg[1]) == COLVARS_OK ? 2 : 0;
|
||||
}
|
||||
if (narg > 100) {
|
||||
error->one(FLERR, "Too many arguments for fix_modify command");
|
||||
return 2;
|
||||
}
|
||||
|
||||
// Parse arguments to fix colvars
|
||||
int return_code = parse_fix_arguments(narg, arg, false);
|
||||
|
||||
if (return_code >= 0) {
|
||||
// A fix colvars argument was detected, return directly
|
||||
return return_code;
|
||||
}
|
||||
|
||||
// Any unknown arguments will go through the Colvars scripting interface
|
||||
if (me == 0) {
|
||||
int error_code = COLVARSCRIPT_OK;
|
||||
colvarscript *script = proxy->script;
|
||||
script->set_cmdline_main_cmd("fix_modify " + std::string(id));
|
||||
|
||||
for (int i = 0; i < narg; i++) {
|
||||
|
||||
// Substitute LAMMPS variables
|
||||
// See https://github.com/lammps/lammps/commit/f9be11ac8ab460edff3709d66734d3fc2cd806dd
|
||||
char *new_arg = arg[i];
|
||||
int ncopy = strlen(new_arg) + 1;
|
||||
char *copy = utils::strdup(new_arg);
|
||||
char *work = new char[ncopy];
|
||||
int nwork = ncopy;
|
||||
lmp->input->substitute(copy,work,ncopy,nwork,0);
|
||||
delete[] work;
|
||||
new_arg = copy;
|
||||
|
||||
script_args[i+1] = reinterpret_cast<unsigned char *>(new_arg);
|
||||
}
|
||||
|
||||
// Run the command through Colvars
|
||||
error_code |= script->run(narg+1, script_args);
|
||||
|
||||
std::string const result = proxy->get_error_msgs() + script->str_result();
|
||||
if (result.size()) {
|
||||
std::istringstream is(result);
|
||||
std::string line;
|
||||
while (std::getline(is, line)) {
|
||||
if (lmp->screen) fprintf(lmp->screen, "%s\n", line.c_str());
|
||||
if (lmp->logfile) fprintf(lmp->logfile, "%s\n", line.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
return (error_code == COLVARSCRIPT_OK) ? narg : 0;
|
||||
|
||||
} else {
|
||||
|
||||
// Return without error, don't block Fix::modify_params()
|
||||
return narg;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
void FixColvars::setup_io()
|
||||
{
|
||||
if (me == 0) {
|
||||
proxy->set_input_prefix(std::string(inp_name ? inp_name : ""));
|
||||
if (proxy->input_prefix().size() > 0) {
|
||||
proxy->log("Will read input state from file \""+
|
||||
proxy->input_prefix()+".colvars.state\"");
|
||||
}
|
||||
|
||||
proxy->set_output_prefix(std::string(out_name ? out_name : ""));
|
||||
|
||||
// Try to extract a restart prefix from a potential restart command
|
||||
LAMMPS_NS::Output *outp = lmp->output;
|
||||
if ((outp->restart_every_single > 0) &&
|
||||
(outp->restart1 != nullptr)) {
|
||||
|
||||
proxy->set_default_restart_frequency(outp->restart_every_single);
|
||||
proxy->set_restart_output_prefix(std::string(outp->restart1));
|
||||
|
||||
} else if ((outp->restart_every_double > 0) &&
|
||||
(outp->restart2a != nullptr)) {
|
||||
|
||||
proxy->set_default_restart_frequency(outp->restart_every_double);
|
||||
proxy->set_restart_output_prefix(std::string(outp->restart2a));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FixColvars::setup(int vflag)
|
||||
{
|
||||
@ -514,7 +474,12 @@ void FixColvars::setup(int vflag)
|
||||
MPI_Status status;
|
||||
MPI_Request request;
|
||||
|
||||
one_time_init();
|
||||
if (me == 0) {
|
||||
setup_io();
|
||||
proxy->parse_module_config();
|
||||
}
|
||||
|
||||
init_taglist();
|
||||
|
||||
// determine size of comm buffer
|
||||
nme=0;
|
||||
@ -675,19 +640,6 @@ void FixColvars::post_force(int /*vflag*/)
|
||||
if (me == 0) {
|
||||
if (proxy->want_exit())
|
||||
error->one(FLERR,"Run aborted on request from colvars module.\n");
|
||||
|
||||
if (!tstat_fix) {
|
||||
proxy->set_target_temperature(0.0);
|
||||
} else {
|
||||
int tmp;
|
||||
// get thermostat target temperature from corresponding fix,
|
||||
// if the fix supports extraction.
|
||||
double *tt = (double *) tstat_fix->extract("t_target", tmp);
|
||||
if (tt)
|
||||
proxy->set_target_temperature(*tt);
|
||||
else
|
||||
proxy->set_target_temperature(0.0);
|
||||
}
|
||||
}
|
||||
|
||||
const tagint * const tag = atom->tag;
|
||||
@ -935,13 +887,17 @@ void FixColvars::end_of_step()
|
||||
void FixColvars::write_restart(FILE *fp)
|
||||
{
|
||||
if (me == 0) {
|
||||
std::string rest_text;
|
||||
proxy->serialize_status(rest_text);
|
||||
// TODO call write_output_files()
|
||||
const char *cvm_state = rest_text.c_str();
|
||||
int len = strlen(cvm_state) + 1; // need to include terminating null byte.
|
||||
fwrite(&len,sizeof(int),1,fp);
|
||||
fwrite(cvm_state,1,len,fp);
|
||||
cvm::memory_stream ms;
|
||||
if (proxy->colvars->write_state(ms)) {
|
||||
int len_cv_state = ms.length();
|
||||
// Will write the buffer's length twice, so that the fix can read it later, too
|
||||
int len = len_cv_state + sizeof(int);
|
||||
fwrite(&len, sizeof(int), 1, fp);
|
||||
fwrite(&len, sizeof(int), 1, fp);
|
||||
fwrite(ms.output_buffer(), 1, len_cv_state, fp);
|
||||
} else {
|
||||
error->all(FLERR, "Failed to write Colvars state to binary file");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -949,11 +905,13 @@ void FixColvars::write_restart(FILE *fp)
|
||||
|
||||
void FixColvars::restart(char *buf)
|
||||
{
|
||||
one_time_init();
|
||||
|
||||
if (me == 0) {
|
||||
std::string rest_text(buf);
|
||||
proxy->deserialize_status(rest_text);
|
||||
// Read the buffer's length, then load it into Colvars starting right past that location
|
||||
int length = *(reinterpret_cast<int *>(buf));
|
||||
unsigned char *colvars_state_buffer = reinterpret_cast<unsigned char *>(buf + sizeof(int));
|
||||
if (proxy->colvars->set_input_state_buffer(length, colvars_state_buffer) != COLVARS_OK) {
|
||||
error->all(FLERR, "Failed to set the Colvars input state from string buffer");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -21,7 +21,8 @@
|
||||
------------------------------------------------------------------------- */
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
Contributing author: Axel Kohlmeyer (Temple U)
|
||||
Contributing author: Axel Kohlmeyer (Temple U)
|
||||
Currently maintained by: Giacomo Fiorin (NIH)
|
||||
------------------------------------------------------------------------- */
|
||||
|
||||
#ifdef FIX_CLASS
|
||||
@ -35,6 +36,10 @@ FixStyle(colvars,FixColvars);
|
||||
|
||||
#include "fix.h"
|
||||
|
||||
// Forward declarations
|
||||
namespace IntHash_NS {
|
||||
class inthash_t;
|
||||
}
|
||||
class colvarproxy_lammps;
|
||||
|
||||
namespace LAMMPS_NS {
|
||||
@ -66,9 +71,9 @@ class FixColvars : public Fix {
|
||||
char *conf_file; // name of colvars config file
|
||||
char *inp_name; // name/prefix of colvars restart file
|
||||
char *out_name; // prefix string for all output files
|
||||
char *tmp_name; // name of thermostat fix.
|
||||
char *tfix_name; // name of thermostat fix.
|
||||
int rng_seed; // seed to initialize random number generator
|
||||
Fix *tstat_fix; // pointer to thermostat fix
|
||||
double t_target = 0.0; // thermostat target temperature
|
||||
double energy; // biasing energy of the fix
|
||||
|
||||
int me; // my MPI rank in this "world".
|
||||
@ -80,8 +85,10 @@ class FixColvars : public Fix {
|
||||
struct commdata *comm_buf; // communication buffer
|
||||
double *force_buf; // communication buffer
|
||||
|
||||
void *idmap; // hash for mapping atom indices to consistent order.
|
||||
int *rev_idmap; // list of the hash keys for reverse mapping.
|
||||
/// Arguments passed from fix_modify to the Colvars script interface
|
||||
unsigned char *script_args[100];
|
||||
|
||||
IntHash_NS::inthash_t *idmap; // hash for mapping atom indices to consistent order.
|
||||
|
||||
int nlevels_respa; // flag to determine respa levels.
|
||||
int store_forces; // flag to determine whether to store total forces
|
||||
@ -90,7 +97,20 @@ class FixColvars : public Fix {
|
||||
static int instances; // count fix instances, since colvars currently
|
||||
// only supports one instance at a time
|
||||
MPI_Comm root2root; // inter-root communicator for multi-replica support
|
||||
void one_time_init(); // one time initialization
|
||||
|
||||
void init_taglist(); // initialize list of atom tags and hash table
|
||||
|
||||
/// Share with Colvars the thermostat fix named by tfix_name
|
||||
void set_thermostat_temperature();
|
||||
|
||||
/// Tell Colvars where to get its state from and where to save it
|
||||
void setup_io();
|
||||
|
||||
/// Parse LAMMPS-specific arguments to either fix or fix_modify
|
||||
/// \param narg Number of arguments
|
||||
/// \param arg Array of strings
|
||||
/// \param fix_constructor If false, try Colvars commands if LAMMPS ones fail
|
||||
int parse_fix_arguments(int narg, char **arg, bool fix_constructor = true);
|
||||
};
|
||||
|
||||
} // namespace LAMMPS_NS
|
||||
|
||||
169
src/COLVARS/inthash.cpp
Normal file
169
src/COLVARS/inthash.cpp
Normal file
@ -0,0 +1,169 @@
|
||||
// clang-format off
|
||||
// -*- c++ -*-
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
|
||||
#include "inthash.h"
|
||||
|
||||
|
||||
namespace IntHash_NS {
|
||||
|
||||
/************************************************************************
|
||||
* integer hash code:
|
||||
************************************************************************/
|
||||
|
||||
/* inthash() - Hash function returns a hash number for a given key.
|
||||
* tptr: Pointer to a hash table, key: The key to create a hash number for */
|
||||
int inthash(const inthash_t *tptr, int key) {
|
||||
int hashvalue;
|
||||
|
||||
hashvalue = (((key*1103515249)>>tptr->downshift) & tptr->mask);
|
||||
if (hashvalue < 0) {
|
||||
hashvalue = 0;
|
||||
}
|
||||
|
||||
return hashvalue;
|
||||
}
|
||||
|
||||
/*
|
||||
* rebuild_table_int() - Create new hash table when old one fills up.
|
||||
*
|
||||
* tptr: Pointer to a hash table
|
||||
*/
|
||||
void rebuild_table_int(inthash_t *tptr) {
|
||||
inthash_node_t **old_bucket, *old_hash, *tmp;
|
||||
int old_size, h, i;
|
||||
|
||||
old_bucket=tptr->bucket;
|
||||
old_size=tptr->size;
|
||||
|
||||
/* create a new table and rehash old buckets */
|
||||
inthash_init(tptr, old_size<<1);
|
||||
for (i=0; i<old_size; i++) {
|
||||
old_hash=old_bucket[i];
|
||||
while (old_hash) {
|
||||
tmp=old_hash;
|
||||
old_hash=old_hash->next;
|
||||
h=inthash(tptr, tmp->key);
|
||||
tmp->next=tptr->bucket[h];
|
||||
tptr->bucket[h]=tmp;
|
||||
tptr->entries++;
|
||||
} /* while */
|
||||
} /* for */
|
||||
|
||||
/* free memory used by old table */
|
||||
free(old_bucket);
|
||||
}
|
||||
|
||||
/*
|
||||
* inthash_init() - Initialize a new hash table.
|
||||
*
|
||||
* tptr: Pointer to the hash table to initialize
|
||||
* buckets: The number of initial buckets to create
|
||||
*/
|
||||
void inthash_init(inthash_t *tptr, int buckets) {
|
||||
|
||||
/* make sure we allocate something */
|
||||
if (buckets==0)
|
||||
buckets=16;
|
||||
|
||||
/* initialize the table */
|
||||
tptr->entries=0;
|
||||
tptr->size=2;
|
||||
tptr->mask=1;
|
||||
tptr->downshift=29;
|
||||
|
||||
/* ensure buckets is a power of 2 */
|
||||
while (tptr->size<buckets) {
|
||||
tptr->size<<=1;
|
||||
tptr->mask=(tptr->mask<<1)+1;
|
||||
tptr->downshift--;
|
||||
} /* while */
|
||||
|
||||
/* allocate memory for table */
|
||||
tptr->bucket=(inthash_node_t **) calloc(tptr->size, sizeof(inthash_node_t *));
|
||||
}
|
||||
|
||||
/*
|
||||
* inthash_lookup() - Lookup an entry in the hash table and return a pointer to
|
||||
* it or HASH_FAIL if it wasn't found.
|
||||
*
|
||||
* tptr: Pointer to the hash table
|
||||
* key: The key to lookup
|
||||
*/
|
||||
int inthash_lookup(inthash_t *tptr, int key) {
|
||||
int h;
|
||||
inthash_node_t *node;
|
||||
|
||||
|
||||
/* find the entry in the hash table */
|
||||
h=inthash(tptr, key);
|
||||
for (node=tptr->bucket[h]; node!=nullptr; node=node->next) {
|
||||
if (node->key == key)
|
||||
break;
|
||||
}
|
||||
|
||||
/* return the entry if it exists, or HASH_FAIL */
|
||||
return(node ? node->data : HASH_FAIL);
|
||||
}
|
||||
|
||||
/*
|
||||
* inthash_insert() - Insert an entry into the hash table. If the entry already
|
||||
* exists return a pointer to it, otherwise return HASH_FAIL.
|
||||
*
|
||||
* tptr: A pointer to the hash table
|
||||
* key: The key to insert into the hash table
|
||||
* data: A pointer to the data to insert into the hash table
|
||||
*/
|
||||
int inthash_insert(inthash_t *tptr, int key, int data) {
|
||||
int tmp;
|
||||
inthash_node_t *node;
|
||||
int h;
|
||||
|
||||
/* check to see if the entry exists */
|
||||
if ((tmp=inthash_lookup(tptr, key)) != HASH_FAIL)
|
||||
return(tmp);
|
||||
|
||||
/* expand the table if needed */
|
||||
while (tptr->entries>=HASH_LIMIT*tptr->size)
|
||||
rebuild_table_int(tptr);
|
||||
|
||||
/* insert the new entry */
|
||||
h=inthash(tptr, key);
|
||||
node=(struct inthash_node_t *) malloc(sizeof(inthash_node_t));
|
||||
node->data=data;
|
||||
node->key=key;
|
||||
node->next=tptr->bucket[h];
|
||||
tptr->bucket[h]=node;
|
||||
tptr->entries++;
|
||||
|
||||
return HASH_FAIL;
|
||||
}
|
||||
|
||||
/*
|
||||
* inthash_destroy() - Delete the entire table, and all remaining entries.
|
||||
*
|
||||
*/
|
||||
void inthash_destroy(inthash_t *tptr) {
|
||||
inthash_node_t *node, *last;
|
||||
int i;
|
||||
|
||||
for (i=0; i<tptr->size; i++) {
|
||||
node = tptr->bucket[i];
|
||||
while (node != nullptr) {
|
||||
last = node;
|
||||
node = node->next;
|
||||
free(last);
|
||||
}
|
||||
}
|
||||
|
||||
/* free the entire array of buckets */
|
||||
if (tptr->bucket != nullptr) {
|
||||
free(tptr->bucket);
|
||||
memset(tptr, 0, sizeof(inthash_t));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
38
src/COLVARS/inthash.h
Normal file
38
src/COLVARS/inthash.h
Normal file
@ -0,0 +1,38 @@
|
||||
#ifndef INTHASH_H
|
||||
#define INTHASH_H
|
||||
|
||||
namespace IntHash_NS {
|
||||
|
||||
/* re-usable integer hash table code. */
|
||||
|
||||
/** hash table top level data structure */
|
||||
typedef struct inthash_t {
|
||||
struct inthash_node_t **bucket; /* array of hash nodes */
|
||||
int size; /* size of the array */
|
||||
int entries; /* number of entries in table */
|
||||
int downshift; /* shift cound, used in hash function */
|
||||
int mask; /* used to select bits for hashing */
|
||||
} inthash_t;
|
||||
|
||||
/** hash table node data structure */
|
||||
typedef struct inthash_node_t {
|
||||
int data; /* data in hash node */
|
||||
int key; /* key for hash lookup */
|
||||
struct inthash_node_t *next; /* next node in hash chain */
|
||||
} inthash_node_t;
|
||||
|
||||
#define HASH_FAIL -1
|
||||
#define HASH_LIMIT 0.5
|
||||
|
||||
/* initialize new hash table */
|
||||
void inthash_init(inthash_t *tptr, int buckets);
|
||||
/* lookup entry in hash table */
|
||||
int inthash_lookup(inthash_t *tptr, int key);
|
||||
/* insert an entry into hash table. */
|
||||
int inthash_insert(inthash_t *tptr, int key, int data);
|
||||
/* delete the hash table */
|
||||
void inthash_destroy(inthash_t *tptr);
|
||||
|
||||
} // namespace IntHash_NS
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user