git-svn-id: svn://svn.icms.temple.edu/lammps-ro/trunk@8162 f3b2605a-c512-4ea7-a41b-209d697bcdaa

This commit is contained in:
sjplimp
2012-05-23 16:20:04 +00:00
parent 191c2ca3e5
commit 1cfdda1f82
31 changed files with 17242 additions and 0 deletions

View File

@ -0,0 +1,96 @@
# library build makefile for colvars module
# ------ SETTINGS ------
CXX = g++
CXXFLAGS = -O2 -mpc64 -march=native -funroll-loops \
-fno-rtti -fno-exceptions # -DCOLVARS_DEBUG
ARCHIVE = ar
ARCHFLAG = -rscv
SHELL = /bin/sh
# ------ DEFINITIONS ------
SRC = colvaratoms.cpp colvarbias_abf.cpp colvarbias.cpp colvarbias_meta.cpp \
colvar.cpp colvarcomp_angles.cpp colvarcomp.cpp colvarcomp_coordnums.cpp \
colvarcomp_distances.cpp colvarcomp_protein.cpp colvarcomp_rotations.cpp \
colvargrid.cpp colvarmodule.cpp colvarparse.cpp colvarvalue.cpp
LIB = libcolvars.a
OBJ = $(SRC:.cpp=.o)
EXE = #colvars_standalone
# ------ MAKE PROCEDURE ------
default: $(LIB) $(EXE)
$(LIB): $(OBJ)
$(ARCHIVE) $(ARFLAGS) $(LIB) $(OBJ)
colvars_standalone: colvars_main.o colvarproxy_standalone.o $(LIB)
$(CXX) -o $@ $(CXXFLAGS) $^
# ------ MAKE FLAGS ------
.SUFFIXES:
.SUFFIXES: .cpp .o
.PHONY: default clean
# ------ COMPILE RULES ------
.cpp.o:
$(CXX) $(CXXFLAGS) -c $<
# ------ DEPENDENCIES ------
#
colvars_main.o: colvars_main.cpp colvarmodule.h colvartypes.h colvarproxy.h \
colvarproxy_standalone.h colvaratoms.h colvarparse.h colvarvalue.h
colvarproxy_standalone.o: colvarproxy_standalone.cpp colvarmodule.h \
colvartypes.h colvarproxy.h colvaratoms.h colvarparse.h colvarvalue.h \
colvarproxy_standalone.h
colvaratoms.o: colvaratoms.cpp colvarmodule.h colvartypes.h colvarproxy.h \
colvarparse.h colvarvalue.h colvaratoms.h
colvarbias_abf.o: colvarbias_abf.cpp colvarmodule.h colvartypes.h \
colvarproxy.h colvar.h colvarvalue.h colvarparse.h colvarbias_abf.h \
colvarbias.h colvargrid.h
colvarbias.o: colvarbias.cpp colvarmodule.h colvartypes.h colvarproxy.h \
colvarvalue.h colvarbias.h colvar.h colvarparse.h
colvarbias_meta.o: colvarbias_meta.cpp colvar.h colvarmodule.h \
colvartypes.h colvarproxy.h colvarvalue.h colvarparse.h \
colvarbias_meta.h colvarbias.h colvargrid.h
colvar.o: colvar.cpp colvarmodule.h colvartypes.h colvarproxy.h \
colvarvalue.h colvarparse.h colvar.h colvarcomp.h colvaratoms.h
colvarcomp_angles.o: colvarcomp_angles.cpp colvarmodule.h colvartypes.h \
colvarproxy.h colvar.h colvarvalue.h colvarparse.h colvarcomp.h \
colvaratoms.h
colvarcomp.o: colvarcomp.cpp colvarmodule.h colvartypes.h colvarproxy.h \
colvarvalue.h colvar.h colvarparse.h colvarcomp.h colvaratoms.h
colvarcomp_coordnums.o: colvarcomp_coordnums.cpp colvarmodule.h \
colvartypes.h colvarproxy.h colvarparse.h colvarvalue.h colvaratoms.h \
colvar.h colvarcomp.h
colvarcomp_distances.o: colvarcomp_distances.cpp colvarmodule.h \
colvartypes.h colvarproxy.h colvarvalue.h colvarparse.h colvar.h \
colvarcomp.h colvaratoms.h
colvarcomp_protein.o: colvarcomp_protein.cpp colvarmodule.h colvartypes.h \
colvarproxy.h colvarvalue.h colvarparse.h colvar.h colvarcomp.h \
colvaratoms.h
colvarcomp_rotations.o: colvarcomp_rotations.cpp colvarmodule.h \
colvartypes.h colvarproxy.h colvarvalue.h colvarparse.h colvar.h \
colvarcomp.h colvaratoms.h
colvargrid.o: colvargrid.cpp colvarmodule.h colvartypes.h colvarproxy.h \
colvarvalue.h colvarparse.h colvar.h colvarcomp.h colvaratoms.h \
colvargrid.h
colvarmodule.o: colvarmodule.cpp colvarmodule.h colvartypes.h colvarproxy.h \
colvarparse.h colvarvalue.h colvar.h colvarbias.h colvarbias_meta.h \
colvargrid.h colvarbias_abf.h
colvarparse.o: colvarparse.cpp colvarmodule.h colvartypes.h colvarproxy.h \
colvarvalue.h colvarparse.h
colvarvalue.o: colvarvalue.cpp colvarmodule.h colvartypes.h colvarproxy.h \
colvarvalue.h
# ------ CLEAN ------
clean:
-rm *.o *~ $(LIB)

95
lib/colvars/Makefile.g++ Normal file
View File

@ -0,0 +1,95 @@
# library build makefile for colvars module
# ------ SETTINGS ------
CXX = g++
CXXFLAGS = -O2 -g -funroll-loops # -DCOLVARS_DEBUG
ARCHIVE = ar
ARCHFLAG = -rscv
SHELL = /bin/sh
# ------ DEFINITIONS ------
SRC = colvaratoms.cpp colvarbias_abf.cpp colvarbias.cpp colvarbias_meta.cpp \
colvar.cpp colvarcomp_angles.cpp colvarcomp.cpp colvarcomp_coordnums.cpp \
colvarcomp_distances.cpp colvarcomp_protein.cpp colvarcomp_rotations.cpp \
colvargrid.cpp colvarmodule.cpp colvarparse.cpp colvarvalue.cpp
LIB = libcolvars.a
OBJ = $(SRC:.cpp=.o)
EXE = #colvars_standalone
# ------ MAKE PROCEDURE ------
default: $(LIB) $(EXE)
$(LIB): $(OBJ)
$(ARCHIVE) $(ARFLAGS) $(LIB) $(OBJ)
colvars_standalone: colvars_main.o colvarproxy_standalone.o $(LIB)
$(CXX) -o $@ $(CXXFLAGS) $^
# ------ MAKE FLAGS ------
.SUFFIXES:
.SUFFIXES: .cpp .o
.PHONY: default clean
# ------ COMPILE RULES ------
.cpp.o:
$(CXX) $(CXXFLAGS) -c $<
# ------ DEPENDENCIES ------
#
colvars_main.o: colvars_main.cpp colvarmodule.h colvartypes.h colvarproxy.h \
colvarproxy_standalone.h colvaratoms.h colvarparse.h colvarvalue.h
colvarproxy_standalone.o: colvarproxy_standalone.cpp colvarmodule.h \
colvartypes.h colvarproxy.h colvaratoms.h colvarparse.h colvarvalue.h \
colvarproxy_standalone.h
colvaratoms.o: colvaratoms.cpp colvarmodule.h colvartypes.h colvarproxy.h \
colvarparse.h colvarvalue.h colvaratoms.h
colvarbias_abf.o: colvarbias_abf.cpp colvarmodule.h colvartypes.h \
colvarproxy.h colvar.h colvarvalue.h colvarparse.h colvarbias_abf.h \
colvarbias.h colvargrid.h
colvarbias.o: colvarbias.cpp colvarmodule.h colvartypes.h colvarproxy.h \
colvarvalue.h colvarbias.h colvar.h colvarparse.h
colvarbias_meta.o: colvarbias_meta.cpp colvar.h colvarmodule.h \
colvartypes.h colvarproxy.h colvarvalue.h colvarparse.h \
colvarbias_meta.h colvarbias.h colvargrid.h
colvar.o: colvar.cpp colvarmodule.h colvartypes.h colvarproxy.h \
colvarvalue.h colvarparse.h colvar.h colvarcomp.h colvaratoms.h
colvarcomp_angles.o: colvarcomp_angles.cpp colvarmodule.h colvartypes.h \
colvarproxy.h colvar.h colvarvalue.h colvarparse.h colvarcomp.h \
colvaratoms.h
colvarcomp.o: colvarcomp.cpp colvarmodule.h colvartypes.h colvarproxy.h \
colvarvalue.h colvar.h colvarparse.h colvarcomp.h colvaratoms.h
colvarcomp_coordnums.o: colvarcomp_coordnums.cpp colvarmodule.h \
colvartypes.h colvarproxy.h colvarparse.h colvarvalue.h colvaratoms.h \
colvar.h colvarcomp.h
colvarcomp_distances.o: colvarcomp_distances.cpp colvarmodule.h \
colvartypes.h colvarproxy.h colvarvalue.h colvarparse.h colvar.h \
colvarcomp.h colvaratoms.h
colvarcomp_protein.o: colvarcomp_protein.cpp colvarmodule.h colvartypes.h \
colvarproxy.h colvarvalue.h colvarparse.h colvar.h colvarcomp.h \
colvaratoms.h
colvarcomp_rotations.o: colvarcomp_rotations.cpp colvarmodule.h \
colvartypes.h colvarproxy.h colvarvalue.h colvarparse.h colvar.h \
colvarcomp.h colvaratoms.h
colvargrid.o: colvargrid.cpp colvarmodule.h colvartypes.h colvarproxy.h \
colvarvalue.h colvarparse.h colvar.h colvarcomp.h colvaratoms.h \
colvargrid.h
colvarmodule.o: colvarmodule.cpp colvarmodule.h colvartypes.h colvarproxy.h \
colvarparse.h colvarvalue.h colvar.h colvarbias.h colvarbias_meta.h \
colvargrid.h colvarbias_abf.h
colvarparse.o: colvarparse.cpp colvarmodule.h colvartypes.h colvarproxy.h \
colvarvalue.h colvarparse.h
colvarvalue.o: colvarvalue.cpp colvarmodule.h colvartypes.h colvarproxy.h \
colvarvalue.h
# ------ CLEAN ------
clean:
-rm *.o *~ $(LIB)

View File

@ -0,0 +1,5 @@
# Settings that the LAMMPS build will import when this package library is used
colvars_SYSINC = # -DCOLVARS_DEBUG
colvars_SYSLIB =
colvars_SYSPATH =

50
lib/colvars/README Normal file
View File

@ -0,0 +1,50 @@
This directory has source files to build a library that LAMMPS
links against when using the USER-COLVARS package.
When you are done building this library, two files should
exist in this directory:
libcolvars.a the library LAMMPS will link against
Makefile.lammps settings the LAMMPS Makefile will import
The latter file will have settings like this (can be omitted if blank):
colvars_SYSINC =
colvars_SYSLIB =
colvars_SYSPATH =
SYSINC is for settings needed to compile LAMMPS source files
SYSLIB is for additional system libraries needed by this package
SYSPATH is the path(s) to where those libraries are
You must insure these settings are correct for your system, else
the LAMMPS build will likely fail.
-------------------------------------------------------------------------
The following publication describes the principles of
the implementation of this library:
Exploring Multidimensional Free Energy Landscapes Using
Time-Dependent Biases on Collective Variables,
J. Hénin, G. Fiorin, C. Chipot, and M. L. Klein,
J. Chem. Theory Comput., 6, 35-47 (2010).
This library is the portable "colvars" module, originally interfaced
with the NAMD MD code, to provide an extensible software framework,
that allows enhanced sampling in molecular dynamics simulations.
The module is written to maximize performance, portability,
flexibility of usage for the user, and extensibility for the developer.
This library must be built with a C++ compiler, before LAMMPS is
built, so LAMMPS can link against it.
--------------
Build the library using one of the provided Makefiles or creating your
own, specific to your compiler and system. For example:
make -f Makefile.g++
If the build is successful, you should end up with a libcolvars.a file.

1600
lib/colvars/colvar.cpp Normal file

File diff suppressed because it is too large Load Diff

561
lib/colvars/colvar.h Normal file
View File

@ -0,0 +1,561 @@
// -*- c++ -*-
#ifndef COLVAR_H
#define COLVAR_H
#include <iostream>
#include <iomanip>
#include <cmath>
#include "colvarmodule.h"
#include "colvarvalue.h"
#include "colvarparse.h"
/// \brief A collective variable (main class); to be defined, it needs
/// at least one object of a derived class of colvar::cvc; it
/// calculates and returns a \link colvarvalue \endlink object
///
/// This class parses the configuration, defines the behaviour and
/// stores the value (\link colvar::x \endlink) and all related data
/// of a collective variable. How the value is calculated is defined
/// in \link colvar::cvc \endlink and its derived classes. The
/// \link colvar \endlink object contains pointers to multiple \link
/// colvar::cvc \endlink derived objects, which can be combined
/// together into one collective variable. This makes possible to
/// implement new collective variables at runtime based on the
/// existing ones. Currently, this possibility is limited to a
/// polynomial, using the coefficients cvc::sup_coeff and the
/// exponents cvc::sup_np. In case of non-scalar variables,
/// only exponents equal to 1 are accepted.
///
/// Please note that most of its members are \link colvarvalue
/// \endlink objects, i.e. they can handle different data types
/// together, and must all be set to the same type of colvar::x by
/// using the colvarvalue::type() member function before using them
/// together in assignments or other operations; this is usually done
/// automatically in the constructor. If you add a new member of
/// \link colvarvalue \endlink type, you should also add its
/// initialization line in the \link colvar \endlink constructor.
class colvar : public colvarparse {
public:
/// Name
std::string name;
/// Type of value
colvarvalue::Type type() const;
/// \brief Current value (previously obtained from calc() or read_traj())
colvarvalue const & value() const;
/// \brief Current velocity (previously obtained from calc() or read_traj())
colvarvalue const & velocity() const;
/// \brief Current system force (previously obtained from calc() or
/// read_traj()). Note: this is calculated using the atomic forces
/// from the last simulation step.
///
/// Total atom forces are read from the MD program, the total force
/// acting on the collective variable is calculated summing those
/// from all colvar components, the bias and walls forces are
/// subtracted.
colvarvalue const & system_force() const;
/// \brief
/// \brief Typical fluctuation amplitude for this collective
/// variable (e.g. local width of a free energy basin)
///
/// In metadynamics calculations, \link colvarbias_meta \endlink,
/// this value is used to calculate the width of a gaussian. In ABF
/// calculations, \link colvarbias_abf \endlink, it is used to
/// calculate the grid spacing in the direction of this collective
/// variable.
cvm::real width;
/// \brief True if this \link colvar \endlink is a linear
/// combination of \link cvc \endlink elements
bool b_linear;
/// \brief True if all \link cvc \endlink objects are capable
/// of calculating inverse gradients
bool b_inverse_gradients;
/// \brief True if all \link cvc \endlink objects are capable
/// of calculating Jacobian forces
bool b_Jacobian_force;
/// \brief Options controlling the behaviour of the colvar during
/// the simulation, which are set from outside the cvcs
enum task {
/// \brief Gradients are calculated and temporarily stored, so
/// that external forces can be applied
task_gradients,
/// \brief Collect atomic gradient data from all cvcs into vector
/// atomic_gradients
task_collect_gradients,
/// \brief Calculate the velocity with finite differences
task_fdiff_velocity,
/// \brief The system force is calculated, projecting the atomic
/// forces on the inverse gradients
task_system_force,
/// \brief The variable has a harmonic restraint around a moving
/// center with fictitious mass; bias forces will be applied to
/// the center
task_extended_lagrangian,
/// \brief The extended system coordinate undergoes Langevin
/// dynamics
task_langevin,
/// \brief Output the potential and kinetic energies
/// (for extended Lagrangian colvars only)
task_output_energy,
/// \brief Compute analytically the "force" arising from the
/// geometric entropy component (for example, that from the angular
/// states orthogonal to a distance vector)
task_Jacobian_force,
/// \brief Report the Jacobian force as part of the system force
/// if disabled, apply a correction internally to cancel it
task_report_Jacobian_force,
/// \brief Output the value to the trajectory file (on by default)
task_output_value,
/// \brief Output the velocity to the trajectory file
task_output_velocity,
/// \brief Output the applied force to the trajectory file
task_output_applied_force,
/// \brief Output the system force to the trajectory file
task_output_system_force,
/// \brief A lower boundary is defined
task_lower_boundary,
/// \brief An upper boundary is defined
task_upper_boundary,
/// \brief Provide a discretization of the values of the colvar to
/// be used by the biases or in analysis (needs lower and upper
/// boundary)
task_grid,
/// \brief Apply a restraining potential (|x-xb|^2) to the colvar
/// when it goes below the lower wall
task_lower_wall,
/// \brief Apply a restraining potential (|x-xb|^2) to the colvar
/// when it goes above the upper wall
task_upper_wall,
/// \brief Compute running average
task_runave,
/// \brief Compute time correlation function
task_corrfunc,
/// \brief Number of possible tasks
task_ntot
};
/// Tasks performed by this colvar
bool tasks[task_ntot];
protected:
/*
extended:
H = H_{0} + \sum_{i} 1/2*\lambda*(S_i(x(t))-s_i(t))^2 \\
+ \sum_{i} 1/2*m_i*(ds_i(t)/dt)^2 \\
+ \sum_{t'<t} W * exp (-1/2*\sum_{i} (s_i(t')-s_i(t))^2/(\delta{}s_i)^2) \\
+ \sum_{w} (\sum_{i}c_{w,i}s_i(t) - D_w)^M/(\Sigma_w)^M
normal:
H = H_{0} + \sum_{t'<t} W * exp (-1/2*\sum_{i} (S_i(x(t'))-S_i(x(t)))^2/(\delta{}S_i)^2) \\
+ \sum_{w} (\sum_{i}c_{w,i}S_i(t) - D_w)^M/(\Sigma_w)^M
output:
H = H_{0} (only output S(x), no forces)
Here:
S(x(t)) = x
s(t) = xr
DS = Ds = delta
*/
/// Value of the colvar
colvarvalue x;
/// Cached reported value (x may be manipulated)
colvarvalue x_reported;
/// Finite-difference velocity
colvarvalue v_fdiff;
inline colvarvalue fdiff_velocity (colvarvalue const &xold,
colvarvalue const &xnew)
{
// using the gradient of the square distance to calculate the
// velocity (non-scalar variables automatically taken into
// account)
cvm::real dt = cvm::dt();
return ( ( (dt > 0.0) ? (1.0/dt) : 1.0 ) *
0.5 * dist2_lgrad (xnew, xold) );
}
/// Cached reported velocity
colvarvalue v_reported;
// Options for task_extended_lagrangian
/// Restraint center
colvarvalue xr;
/// Velocity of the restraint center
colvarvalue vr;
/// Mass of the restraint center
cvm::real ext_mass;
/// Restraint force constant
cvm::real ext_force_k;
/// Friction coefficient for Langevin extended dynamics
cvm::real ext_gamma;
/// Amplitude of Gaussian white noise for Langevin extended dynamics
cvm::real ext_sigma;
/// \brief Harmonic restraint force
colvarvalue fr;
/// \brief Jacobian force, when task_Jacobian_force is enabled
colvarvalue fj;
/// Cached reported system force
colvarvalue ft_reported;
public:
/// \brief Bias force; reset_bias_force() should be called before
/// the biases are updated
colvarvalue fb;
/// \brief Total \em applied force; fr (if task_extended_lagrangian
/// is defined), fb (if biases are applied) and the walls' forces
/// (if defined) contribute to it
colvarvalue f;
/// \brief Total force, as derived from the atomic trajectory;
/// should equal the total system force plus \link f \endlink
colvarvalue ft;
/// Period, if it is a constant
cvm::real period;
/// \brief Same as above, but also takes into account components
/// with a variable period, such as distanceZ
bool b_periodic;
/// \brief Expand the boundaries of multiples of width, to keep the
/// value always within range
bool expand_boundaries;
/// \brief Location of the lower boundary
colvarvalue lower_boundary;
/// \brief Location of the lower wall
colvarvalue lower_wall;
/// \brief Force constant for the lower boundary potential (|x-xb|^2)
cvm::real lower_wall_k;
/// \brief Location of the upper boundary
colvarvalue upper_boundary;
/// \brief Location of the upper wall
colvarvalue upper_wall;
/// \brief Force constant for the upper boundary potential (|x-xb|^2)
cvm::real upper_wall_k;
/// \brief Is the interval defined by the two boundaries periodic?
bool periodic_boundaries() const;
/// \brief Is the interval defined by the two boundaries periodic?
bool periodic_boundaries (colvarvalue const &lb, colvarvalue const &ub) const;
/// Constructor
colvar (std::string const &conf);
/// Enable the specified task
void enable (colvar::task const &t);
/// Disable the specified task
void disable (colvar::task const &t);
/// Destructor
~colvar();
/// \brief Calculate the colvar value and all the other requested
/// quantities
void calc();
/// \brief Calculate just the value, and store it in the argument
void calc_value (colvarvalue &xn);
/// Set the total biasing force to zero
void reset_bias_force();
/// Add to the total force from biases
void add_bias_force (colvarvalue const &force);
/// \brief Collect all forces on this colvar, integrate internal
/// equations of motion of internal degrees of freedom; see also
/// colvar::communicate_forces()
/// return colvar energy if extended Lagrandian active
cvm::real update();
/// \brief Communicate forces (previously calculated in
/// colvar::update()) to the external degrees of freedom
void communicate_forces();
/// \brief Use the internal metrics (as from \link cvc
/// \endlink objects) to calculate square distances and gradients
///
/// Handles correctly symmetries and periodic boundary conditions
cvm::real dist2 (colvarvalue const &x1,
colvarvalue const &x2) const;
/// \brief Use the internal metrics (as from \link cvc
/// \endlink objects) to calculate square distances and gradients
///
/// Handles correctly symmetries and periodic boundary conditions
colvarvalue dist2_lgrad (colvarvalue const &x1,
colvarvalue const &x2) const;
/// \brief Use the internal metrics (as from \link cvc
/// \endlink objects) to calculate square distances and gradients
///
/// Handles correctly symmetries and periodic boundary conditions
colvarvalue dist2_rgrad (colvarvalue const &x1,
colvarvalue const &x2) const;
/// \brief Use the internal metrics (as from \link cvc
/// \endlink objects) to compare colvar values
///
/// Handles correctly symmetries and periodic boundary conditions
cvm::real compare (colvarvalue const &x1,
colvarvalue const &x2) const;
/// \brief Use the internal metrics (as from \link cvc
/// \endlink objects) to wrap a value into a standard interval
///
/// Handles correctly symmetries and periodic boundary conditions
void wrap (colvarvalue &x) const;
/// Read the analysis tasks
void parse_analysis (std::string const &conf);
/// Perform analysis tasks
void analyse();
/// Read the value from a collective variable trajectory file
std::istream & read_traj (std::istream &is);
/// Output formatted values to the trajectory file
std::ostream & write_traj (std::ostream &os);
/// Write a label to the trajectory file (comment line)
std::ostream & write_traj_label (std::ostream &os);
/// Read the collective variable from a restart file
std::istream & read_restart (std::istream &is);
/// Write the collective variable to a restart file
std::ostream & write_restart (std::ostream &os);
protected:
/// Previous value (to calculate velocities during analysis)
colvarvalue x_old;
/// Time series of values and velocities used in correlation
/// functions
std::list< std::list<colvarvalue> > acf_x_history, acf_v_history;
/// Time series of values and velocities used in correlation
/// functions (pointers)x
std::list< std::list<colvarvalue> >::iterator acf_x_history_p, acf_v_history_p;
/// Time series of values and velocities used in running averages
std::list< std::list<colvarvalue> > x_history;
/// Time series of values and velocities used in correlation
/// functions (pointers)x
std::list< std::list<colvarvalue> >::iterator x_history_p;
/// \brief Collective variable with which the correlation is
/// calculated (default: itself)
std::string acf_colvar_name;
/// Length of autocorrelation function (ACF)
size_t acf_length;
/// After how many steps the ACF starts
size_t acf_offset;
/// How many timesteps separate two ACF values
size_t acf_stride;
/// Number of frames for each ACF point
size_t acf_nframes;
/// Normalize the ACF to a maximum value of 1?
bool acf_normalize;
/// ACF values
std::vector<cvm::real> acf;
/// Name of the file to write the ACF
std::string acf_outfile;
/// Type of autocorrelation function (ACF)
enum acf_type_e {
/// Unset type
acf_notset,
/// Velocity ACF, scalar product between v(0) and v(t)
acf_vel,
/// Coordinate ACF, scalar product between x(0) and x(t)
acf_coor,
/// \brief Coordinate ACF, second order Legendre polynomial
/// between x(0) and x(t) (does not work with scalar numbers)
acf_p2coor
};
/// Type of autocorrelation function (ACF)
acf_type_e acf_type;
/// \brief Velocity ACF, scalar product between v(0) and v(t)
void calc_vel_acf (std::list<colvarvalue> &v_history,
colvarvalue const &v);
/// \brief Coordinate ACF, scalar product between x(0) and x(t)
/// (does not work with scalar numbers)
void calc_coor_acf (std::list<colvarvalue> &x_history,
colvarvalue const &x);
/// \brief Coordinate ACF, second order Legendre polynomial between
/// x(0) and x(t) (does not work with scalar numbers)
void calc_p2coor_acf (std::list<colvarvalue> &x_history,
colvarvalue const &x);
/// Calculate the auto-correlation function (ACF)
void calc_acf();
/// Save the ACF to a file
void write_acf (std::ostream &os);
/// Length of running average series
size_t runave_length;
/// Timesteps to skip between two values in the running average series
size_t runave_stride;
/// Name of the file to write the running average
std::ofstream runave_os;
/// Current value of the running average
colvarvalue runave;
/// Current value of the square deviation from the running average
cvm::real runave_variance;
/// Calculate the running average and its standard deviation
void calc_runave();
/// If extended Lagrangian active: colvar energies (kinetic and harmonic potential)
cvm::real kinetic_energy;
cvm::real potential_energy;
public:
// collective variable component base class
class cvc;
// currently available collective variable components
// scalar colvar components
class distance;
class distance_z;
class distance_xy;
class min_distance;
class angle;
class dihedral;
class coordnum;
class selfcoordnum;
class h_bond;
class rmsd;
class logmsd;
class orientation_angle;
class tilt;
class spin_angle;
class gyration;
class eigenvector;
class alpha_dihedrals;
class alpha_angles;
class dihedPC;
// non-scalar components
class distance_vec;
class distance_dir;
class orientation;
protected:
/// \brief Array of \link cvc \endlink objects
std::vector<cvc *> cvcs;
/// \brief Initialize the sorted list of atom IDs for atoms involved
/// in all cvcs (called when enabling task_collect_gradients)
void build_atom_list (void);
public:
/// \brief Sorted array of (zero-based) IDs for all atoms involved
std::vector<int> atom_ids;
/// \brief Array of atomic gradients collected from all cvcs
/// with appropriate components, rotations etc.
/// For scalar variables only!
std::vector<cvm::rvector> atomic_gradients;
inline size_t n_components () const {
return cvcs.size();
}
};
inline colvar * cvm::colvar_p (std::string const &name)
{
for (std::vector<colvar *>::iterator cvi = cvm::colvars.begin();
cvi != cvm::colvars.end();
cvi++) {
if ((*cvi)->name == name) {
return (*cvi);
}
}
return NULL;
}
inline colvarvalue::Type colvar::type() const
{
return x.type();
}
inline colvarvalue const & colvar::value() const
{
return x_reported;
}
inline colvarvalue const & colvar::velocity() const
{
return v_reported;
}
inline colvarvalue const & colvar::system_force() const
{
return ft_reported;
}
inline void colvar::add_bias_force (colvarvalue const &force)
{
fb += force;
}
inline void colvar::reset_bias_force() {
fb.reset();
}
#endif

761
lib/colvars/colvaratoms.cpp Normal file
View File

@ -0,0 +1,761 @@
#include "colvarmodule.h"
#include "colvarparse.h"
#include "colvaratoms.h"
// atom member functions depend tightly on the MD interface, and are
// thus defined in colvarproxy_xxx.cpp
// atom_group member functions
cvm::atom_group::atom_group (std::string const &conf,
char const *key,
atom_group *ref_pos_group_in)
: b_center (false), b_rotate (false),
ref_pos_group (NULL), // this is always set within parse(),
// regardless of ref_pos_group_in
noforce (false)
{
cvm::log ("Defining atom group \""+
std::string (key)+"\".\n");
parse (conf, key, ref_pos_group_in);
}
void cvm::atom_group::parse (std::string const &conf,
char const *key,
atom_group *ref_pos_group_in)
{
std::string group_conf;
// save_delimiters is set to false for this call, because "conf" is
// not the config string of this group, but of its parent object
// (which has already taken care of the delimiters)
save_delimiters = false;
key_lookup (conf, key, group_conf, dummy_pos);
// restoring the normal value, because we do want keywords checked
// inside "group_conf"
save_delimiters = true;
if (group_conf.size() == 0) {
cvm::fatal_error ("Error: atom group \""+
std::string (key)+"\" is set, but "
"has no definition.\n");
}
cvm::increase_depth();
cvm::log ("Initializing atom group \""+std::string (key)+"\".\n");
// whether or not to include messages in the log
colvarparse::Parse_Mode mode = parse_silent;
{
bool b_verbose;
get_keyval (group_conf, "verboseOutput", b_verbose, false, parse_silent);
if (b_verbose) mode = parse_normal;
}
{
// get the atoms by numbers
std::vector<int> atom_indexes;
if (get_keyval (group_conf, "atomNumbers", atom_indexes, atom_indexes, mode)) {
if (atom_indexes.size()) {
this->reserve (this->size()+atom_indexes.size());
for (size_t i = 0; i < atom_indexes.size(); i++) {
this->push_back (cvm::atom (atom_indexes[i]));
}
} else
cvm::fatal_error ("Error: no numbers provided for \""
"atomNumbers\".\n");
}
}
{
std::string range_conf = "";
size_t pos = 0;
while (key_lookup (group_conf, "atomNumbersRange",
range_conf, pos)) {
if (range_conf.size()) {
std::istringstream is (range_conf);
int initial, final;
char dash;
if ( (is >> initial) && (initial > 0) &&
(is >> dash) && (dash == '-') &&
(is >> final) && (final > 0) ) {
for (int anum = initial; anum <= final; anum++) {
this->push_back (cvm::atom (anum));
}
range_conf = "";
continue;
}
}
cvm::fatal_error ("Error: no valid definition for \""
"atomNumbersRange\", \""+
range_conf+"\".\n");
}
}
std::vector<std::string> psf_segids;
get_keyval (group_conf, "psfSegID", psf_segids, std::vector<std::string> (), mode);
for (std::vector<std::string>::iterator psii = psf_segids.begin();
psii < psf_segids.end(); psii++) {
if ( (psii->size() == 0) || (psii->size() > 4) ) {
cvm::fatal_error ("Error: invalid segmend identifier provided, \""+
(*psii)+"\".\n");
}
}
{
std::string range_conf = "";
size_t pos = 0;
size_t range_count = 0;
std::vector<std::string>::iterator psii = psf_segids.begin();
while (key_lookup (group_conf, "atomNameResidueRange",
range_conf, pos)) {
if (psii > psf_segids.end()) {
cvm::fatal_error ("Error: more instances of \"atomNameResidueRange\" than "
"values of \"psfSegID\".\n");
}
std::string const &psf_segid = psf_segids.size() ? *psii : std::string ("");
if (range_conf.size()) {
std::istringstream is (range_conf);
std::string atom_name;
int initial, final;
char dash;
if ( (is >> atom_name) && (atom_name.size()) &&
(is >> initial) && (initial > 0) &&
(is >> dash) && (dash == '-') &&
(is >> final) && (final > 0) ) {
for (int resid = initial; resid <= final; resid++) {
this->push_back (cvm::atom (resid, atom_name, psf_segid));
}
range_conf = "";
} else {
cvm::fatal_error ("Error: cannot parse definition for \""
"atomNameResidueRange\", \""+
range_conf+"\".\n");
}
} else {
cvm::fatal_error ("Error: atomNameResidueRange with empty definition.\n");
}
if (psf_segid.size())
psii++;
}
}
{
// read the atoms from a file
std::string atoms_file_name;
if (get_keyval (group_conf, "atomsFile", atoms_file_name, std::string (""), mode)) {
std::string atoms_col;
if (!get_keyval (group_conf, "atomsCol", atoms_col, std::string (""), mode)) {
cvm::fatal_error ("Error: parameter atomsCol is required if atomsFile is set.\n");
}
double atoms_col_value;
bool const atoms_col_value_defined = get_keyval (group_conf, "atomsColValue", atoms_col_value, 0.0, mode);
if (atoms_col_value_defined && (!atoms_col_value))
cvm::fatal_error ("Error: atomsColValue, "
"if provided, must be non-zero.\n");
cvm::load_atoms (atoms_file_name.c_str(), *this, atoms_col, atoms_col_value);
}
}
for (std::vector<cvm::atom>::iterator a1 = this->begin();
a1 != this->end(); a1++) {
std::vector<cvm::atom>::iterator a2 = a1;
++a2;
for ( ; a2 != this->end(); a2++) {
if (a1->id == a2->id) {
if (cvm::debug())
cvm::log ("Discarding doubly counted atom with number "+
cvm::to_str (a1->id+1)+".\n");
a2 = this->erase (a2);
if (a2 == this->end())
break;
}
}
}
if (get_keyval (group_conf, "dummyAtom", dummy_atom_pos, cvm::atom_pos(), mode)) {
b_dummy = true;
this->total_mass = 1.0;
} else
b_dummy = false;
if (b_dummy && (this->size()))
cvm::fatal_error ("Error: cannot set up group \""+
std::string (key)+"\" as a dummy atom "
"and provide it with atom definitions.\n");
#if (! defined (COLVARS_STANDALONE))
if ( (!b_dummy) && (!cvm::b_analysis) && (!(this->size())) ) {
cvm::fatal_error ("Error: no atoms defined for atom group \""+
std::string (key)+"\".\n");
}
#endif
if (!b_dummy) {
this->total_mass = 0.0;
for (cvm::atom_iter ai = this->begin();
ai != this->end(); ai++) {
this->total_mass += ai->mass;
}
}
get_keyval (group_conf, "disableForces", noforce, false, mode);
get_keyval (group_conf, "centerReference", b_center, false, mode);
get_keyval (group_conf, "rotateReference", b_rotate, false, mode);
if (b_center || b_rotate) {
if (b_dummy)
cvm::fatal_error ("Error: cannot set \"centerReference\" or "
"\"rotateReference\" with \"dummyAtom\".\n");
// use refPositionsGroup instead of this group as the one which is
// used to fit the coordinates
if (key_lookup (group_conf, "refPositionsGroup")) {
if (ref_pos_group) {
cvm::fatal_error ("Error: the atom group \""+
std::string (key)+"\" has already a reference group "
"for the rototranslational fit, which was communicated by the "
"colvar component. You should not use refPositionsGroup "
"in this case.\n");
}
cvm::log ("Within atom group \""+std::string (key)+"\":\n");
ref_pos_group = new atom_group (group_conf, "refPositionsGroup");
}
atom_group *ag = ref_pos_group ? ref_pos_group : this;
if (get_keyval (group_conf, "refPositions", ref_pos, ref_pos, mode)) {
cvm::log ("Using reference positions from input file.\n");
if (ref_pos.size() != ag->size()) {
cvm::fatal_error ("Error: the number of reference positions provided ("+
cvm::to_str (ref_pos.size())+
") does not match the number of atoms within \""+
std::string (key)+
"\" ("+cvm::to_str (ag->size())+").\n");
}
}
std::string ref_pos_file;
if (get_keyval (group_conf, "refPositionsFile", ref_pos_file, std::string (""), mode)) {
if (ref_pos.size()) {
cvm::fatal_error ("Error: cannot specify \"refPositionsFile\" and "
"\"refPositions\" at the same time.\n");
}
std::string ref_pos_col;
double ref_pos_col_value;
if (get_keyval (group_conf, "refPositionsCol", ref_pos_col, std::string (""), mode)) {
// if provided, use PDB column to select coordinates
bool found = get_keyval (group_conf, "refPositionsColValue", ref_pos_col_value, 0.0, mode);
if (found && !ref_pos_col_value)
cvm::fatal_error ("Error: refPositionsColValue, "
"if provided, must be non-zero.\n");
} else {
// if not, rely on existing atom indices for the group
ag->create_sorted_ids();
}
cvm::load_coords (ref_pos_file.c_str(), ref_pos, ag->sorted_ids,
ref_pos_col, ref_pos_col_value);
}
if (ref_pos.size()) {
if (b_rotate) {
if (ref_pos.size() != ag->size())
cvm::fatal_error ("Error: the number of reference positions provided ("+
cvm::to_str (ref_pos.size())+
") does not match the number of atoms within \""+
std::string (key)+
"\" ("+cvm::to_str (ag->size())+").\n");
}
// save the center of mass of ref_pos and then subtract it from
// them; in this way it is possible to use the coordinates for
// the rotational fit, if needed
ref_pos_cog = cvm::atom_pos (0.0, 0.0, 0.0);
std::vector<cvm::atom_pos>::iterator pi = ref_pos.begin();
for ( ; pi != ref_pos.end(); pi++) {
ref_pos_cog += *pi;
}
ref_pos_cog /= (cvm::real) ref_pos.size();
for (std::vector<cvm::atom_pos>::iterator pi = ref_pos.begin();
pi != ref_pos.end(); pi++) {
(*pi) -= ref_pos_cog;
}
} else {
#if (! defined (COLVARS_STANDALONE))
if (!cvm::b_analysis)
cvm::fatal_error ("Error: no reference positions provided.\n");
#endif
}
if (b_rotate && !noforce) {
cvm::log ("Warning: atom group \""+std::string (key)+
"\" is set to be rotated to a reference orientation: "
"a torque different than zero on this group "
"could make the simulation unstable. "
"If this happens, set \"disableForces\" to yes "
"for this group.\n");
}
}
if (cvm::debug())
cvm::log ("Done initializing atom group with name \""+
std::string (key)+"\".\n");
this->check_keywords (group_conf, key);
cvm::log ("Atom group \""+std::string (key)+"\" defined, "+
cvm::to_str (this->size())+" initialized: total mass = "+
cvm::to_str (this->total_mass)+".\n");
cvm::decrease_depth();
}
cvm::atom_group::atom_group (std::vector<cvm::atom> const &atoms)
: b_dummy (false), b_center (false), b_rotate (false),
ref_pos_group (NULL), noforce (false)
{
this->reserve (atoms.size());
for (size_t i = 0; i < atoms.size(); i++) {
this->push_back (atoms[i]);
}
total_mass = 0.0;
for (cvm::atom_iter ai = this->begin();
ai != this->end(); ai++) {
total_mass += ai->mass;
}
}
cvm::atom_group::atom_group()
: b_dummy (false), b_center (false), b_rotate (false),
ref_pos_group (NULL), noforce (false)
{
total_mass = 0.0;
}
cvm::atom_group::~atom_group()
{
if (ref_pos_group) {
delete ref_pos_group;
ref_pos_group = NULL;
}
}
void cvm::atom_group::add_atom (cvm::atom const &a)
{
if (b_dummy) {
cvm::fatal_error ("Error: cannot add atoms to a dummy group.\n");
} else {
this->push_back (a);
total_mass += a.mass;
}
}
void cvm::atom_group::create_sorted_ids (void)
{
// Only do the work if the vector is not yet populated
if (sorted_ids.size())
return;
std::list<int> temp_id_list;
for (cvm::atom_iter ai = this->begin(); ai != this->end(); ai++) {
temp_id_list.push_back (ai->id);
}
temp_id_list.sort();
temp_id_list.unique();
if (temp_id_list.size() != this->size()) {
cvm::fatal_error ("Error: duplicate atom IDs in atom group? (found " +
cvm::to_str(temp_id_list.size()) +
" unique atom IDs instead of" +
cvm::to_str(this->size()) + ").\n");
}
sorted_ids = std::vector<int> (temp_id_list.begin(), temp_id_list.end());
return;
}
void cvm::atom_group::read_positions()
{
if (b_dummy) return;
#if (! defined (COLVARS_STANDALONE))
if (!this->size())
cvm::fatal_error ("Error: no atoms defined in the requested group.\n");
#endif
for (cvm::atom_iter ai = this->begin();
ai != this->end(); ai++) {
ai->read_position();
}
if (ref_pos_group)
ref_pos_group->read_positions();
atom_group *fit_group = ref_pos_group ? ref_pos_group : this;
if (b_center) {
// store aside the current center of geometry (all positions will be
// set to the closest images to the first one) and then center on
// the origin
cvm::atom_pos const cog = fit_group->center_of_geometry();
for (cvm::atom_iter ai = this->begin();
ai != this->end(); ai++) {
ai->pos -= cog;
}
}
if (b_rotate) {
// rotate the group (around the center of geometry if b_center is
// true, around the origin otherwise); store the rotation, in
// order to bring back the forces to the original frame before
// applying them
rot.calc_optimal_rotation (fit_group->positions(), ref_pos);
for (cvm::atom_iter ai = this->begin();
ai != this->end(); ai++) {
ai->pos = rot.rotate (ai->pos);
}
}
if (b_center) {
// use the center of geometry of ref_pos
for (cvm::atom_iter ai = this->begin();
ai != this->end(); ai++) {
ai->pos += ref_pos_cog;
}
}
}
void cvm::atom_group::apply_translation (cvm::rvector const &t)
{
if (b_dummy) return;
for (cvm::atom_iter ai = this->begin();
ai != this->end(); ai++) {
ai->pos += t;
}
}
void cvm::atom_group::apply_rotation (cvm::rotation const &rot)
{
if (b_dummy) return;
for (cvm::atom_iter ai = this->begin();
ai != this->end(); ai++) {
ai->pos = rot.rotate (ai->pos);
}
}
void cvm::atom_group::read_velocities()
{
if (b_dummy) return;
if (b_rotate) {
for (cvm::atom_iter ai = this->begin();
ai != this->end(); ai++) {
ai->read_velocity();
ai->vel = rot.rotate (ai->vel);
}
} else {
for (cvm::atom_iter ai = this->begin();
ai != this->end(); ai++) {
ai->read_velocity();
}
}
}
void cvm::atom_group::read_system_forces()
{
if (b_dummy) return;
if (b_rotate) {
for (cvm::atom_iter ai = this->begin();
ai != this->end(); ai++) {
ai->read_system_force();
ai->system_force = rot.rotate (ai->system_force);
}
} else {
for (cvm::atom_iter ai = this->begin();
ai != this->end(); ai++) {
ai->read_system_force();
}
}
}
cvm::atom_pos cvm::atom_group::center_of_geometry (cvm::atom_pos const &ref_pos)
{
if (b_dummy) {
cvm::select_closest_image (dummy_atom_pos, ref_pos);
return dummy_atom_pos;
}
cvm::atom_pos cog (0.0, 0.0, 0.0);
for (cvm::atom_iter ai = this->begin();
ai != this->end(); ai++) {
cvm::select_closest_image (ai->pos, ref_pos);
cog += ai->pos;
}
cog /= this->size();
return cog;
}
cvm::atom_pos cvm::atom_group::center_of_geometry() const
{
if (b_dummy)
return dummy_atom_pos;
cvm::atom_pos cog (0.0, 0.0, 0.0);
for (cvm::atom_const_iter ai = this->begin();
ai != this->end(); ai++) {
cog += ai->pos;
}
cog /= this->size();
return cog;
}
cvm::atom_pos cvm::atom_group::center_of_mass (cvm::atom_pos const &ref_pos)
{
if (b_dummy) {
cvm::select_closest_image (dummy_atom_pos, ref_pos);
return dummy_atom_pos;
}
cvm::atom_pos com (0.0, 0.0, 0.0);
for (cvm::atom_iter ai = this->begin();
ai != this->end(); ai++) {
cvm::select_closest_image (ai->pos, ref_pos);
com += ai->mass * ai->pos;
}
com /= this->total_mass;
return com;
}
cvm::atom_pos cvm::atom_group::center_of_mass() const
{
if (b_dummy)
return dummy_atom_pos;
cvm::atom_pos com (0.0, 0.0, 0.0);
for (cvm::atom_const_iter ai = this->begin();
ai != this->end(); ai++) {
com += ai->mass * ai->pos;
}
com /= this->total_mass;
return com;
}
void cvm::atom_group::set_weighted_gradient (cvm::rvector const &grad)
{
if (b_dummy) return;
for (cvm::atom_iter ai = this->begin();
ai != this->end(); ai++) {
ai->grad = (ai->mass/this->total_mass) * grad;
}
}
std::vector<cvm::atom_pos> cvm::atom_group::positions() const
{
if (b_dummy)
cvm::fatal_error ("Error: positions are not available "
"from a dummy atom group.\n");
std::vector<cvm::atom_pos> x (this->size(), 0.0);
cvm::atom_const_iter ai = this->begin();
std::vector<cvm::atom_pos>::iterator xi = x.begin();
for ( ; ai != this->end(); xi++, ai++) {
*xi = ai->pos;
}
return x;
}
std::vector<cvm::atom_pos> cvm::atom_group::positions_shifted (cvm::rvector const &shift) const
{
if (b_dummy)
cvm::fatal_error ("Error: positions are not available "
"from a dummy atom group.\n");
std::vector<cvm::atom_pos> x (this->size(), 0.0);
cvm::atom_const_iter ai = this->begin();
std::vector<cvm::atom_pos>::iterator xi = x.begin();
for ( ; ai != this->end(); xi++, ai++) {
*xi = (ai->pos + shift);
}
return x;
}
std::vector<cvm::rvector> cvm::atom_group::velocities() const
{
if (b_dummy)
cvm::fatal_error ("Error: velocities are not available "
"from a dummy atom group.\n");
std::vector<cvm::rvector> v (this->size(), 0.0);
cvm::atom_const_iter ai = this->begin();
std::vector<cvm::atom_pos>::iterator vi = v.begin();
for ( ; ai != this->end(); vi++, ai++) {
*vi = ai->vel;
}
return v;
}
std::vector<cvm::rvector> cvm::atom_group::system_forces() const
{
if (b_dummy)
cvm::fatal_error ("Error: system forces are not available "
"from a dummy atom group.\n");
std::vector<cvm::rvector> f (this->size(), 0.0);
cvm::atom_const_iter ai = this->begin();
std::vector<cvm::atom_pos>::iterator fi = f.begin();
for ( ; ai != this->end(); fi++, ai++) {
*fi = ai->system_force;
}
return f;
}
cvm::rvector cvm::atom_group::system_force() const
{
if (b_dummy)
cvm::fatal_error ("Error: system forces are not available "
"from a dummy atom group.\n");
cvm::rvector f (0.0);
for (cvm::atom_const_iter ai = this->begin(); ai != this->end(); ai++) {
f += ai->system_force;
}
return f;
}
void cvm::atom_group::apply_colvar_force (cvm::real const &force)
{
if (b_dummy)
return;
if (noforce)
cvm::fatal_error ("Error: sending a force to a group that has "
"\"disableForces\" defined.\n");
if (b_rotate) {
// get the forces back from the rotated frame
cvm::rotation const rot_inv = rot.inverse();
for (cvm::atom_iter ai = this->begin();
ai != this->end(); ai++) {
ai->apply_force (rot_inv.rotate (force * ai->grad));
}
} else {
// no need to manipulate gradients, they are still in the original
// frame
for (cvm::atom_iter ai = this->begin();
ai != this->end(); ai++) {
ai->apply_force (force * ai->grad);
}
}
}
void cvm::atom_group::apply_force (cvm::rvector const &force)
{
if (b_dummy)
return;
if (noforce)
cvm::fatal_error ("Error: sending a force to a group that has "
"\"disableForces\" defined.\n");
if (b_rotate) {
cvm::rotation const rot_inv = rot.inverse();
for (cvm::atom_iter ai = this->begin();
ai != this->end(); ai++) {
ai->apply_force (rot_inv.rotate ((ai->mass/this->total_mass) * force));
}
} else {
for (cvm::atom_iter ai = this->begin();
ai != this->end(); ai++) {
ai->apply_force ((ai->mass/this->total_mass) * force);
}
}
}
void cvm::atom_group::apply_forces (std::vector<cvm::rvector> const &forces)
{
if (b_dummy)
return;
if (noforce)
cvm::fatal_error ("Error: sending a force to a group that has "
"\"disableForces\" defined.\n");
if (forces.size() != this->size()) {
cvm::fatal_error ("Error: trying to apply an array of forces to an atom "
"group which does not have the same length.\n");
}
if (b_rotate) {
cvm::rotation const rot_inv = rot.inverse();
cvm::atom_iter ai = this->begin();
std::vector<cvm::rvector>::const_iterator fi = forces.begin();
for ( ; ai != this->end(); fi++, ai++) {
ai->apply_force (rot_inv.rotate (*fi));
}
} else {
cvm::atom_iter ai = this->begin();
std::vector<cvm::rvector>::const_iterator fi = forces.begin();
for ( ; ai != this->end(); fi++, ai++) {
ai->apply_force (*fi);
}
}
}

318
lib/colvars/colvaratoms.h Normal file
View File

@ -0,0 +1,318 @@
#ifndef COLVARATOMS_H
#define COLVARATOMS_H
#include "colvarmodule.h"
#include "colvarparse.h"
/// \brief Stores numeric id, mass and all mutable data for an atom,
/// mostly used by a \link cvc \endlink
///
/// This class may be used (although not necessarily) to keep atomic
/// data (id, mass, position and collective variable derivatives)
/// altogether. There may be multiple instances with identical
/// numeric id, all acting independently: forces communicated through
/// these instances will be summed together.
///
/// Read/write operations depend on the underlying code: hence, some
/// member functions are defined in colvarproxy_xxx.h.
class colvarmodule::atom {
protected:
/// \brief Index in the list of atoms involved by the colvars (\b
/// NOT in the global topology!)
size_t index;
public:
/// Internal identifier (zero-based)
int id;
/// Mass
cvm::real mass;
/// \brief Current position (copied from the program, can be
/// manipulated)
cvm::atom_pos pos;
/// \brief Current velocity (copied from the program, can be
/// manipulated)
cvm::rvector vel;
/// \brief System force at the previous step (copied from the
/// program, can be manipulated)
cvm::rvector system_force;
/// \brief Gradient of a scalar collective variable with respect
/// to this atom
///
/// This can only handle a scalar collective variable (i.e. when
/// the \link colvarvalue::real_value \endlink member is used
/// from the \link colvarvalue \endlink class), which is also the
/// most frequent case. For more complex types of \link
/// colvarvalue \endlink objects, atomic gradients should be
/// defined within the specific \link cvc \endlink
/// implementation
cvm::rvector grad;
/// \brief Default constructor, setting id to a non-valid value
inline atom() {}
/// \brief Initialize an atom for collective variable calculation
/// and get its internal identifier \param atom_number Atom index in
/// the system topology (starting from 1)
atom (int const &atom_number);
/// \brief Initialize an atom for collective variable calculation
/// and get its internal identifier \param residue Residue number
/// \param atom_name Name of the atom in the residue \param
/// segment_id For PSF topologies, the segment identifier; for other
/// type of topologies, may not be required
atom (cvm::residue_id const &residue,
std::string const &atom_name,
std::string const &segment_id = std::string (""));
/// Copy constructor
atom (atom const &a);
/// Destructor
~atom();
/// Set non-constant data (everything except id and mass) to zero
inline void reset_data() {
pos = atom_pos (0.0);
vel = grad = system_force = rvector (0.0);
}
/// Get the current position
void read_position();
/// Get the current velocity
void read_velocity();
/// Get the system force
void read_system_force();
/// \brief Apply a force to the atom
///
/// The force will be used later by the MD integrator, the
/// collective variables module does not integrate equations of
/// motion. Multiple calls to this function by either the same
/// \link atom \endlink object or different objects with identical
/// \link id \endlink, will all add to the existing MD force.
void apply_force (cvm::rvector const &new_force);
};
/// \brief Group of \link atom \endlink objects, mostly used by a
/// \link cvc \endlink
///
/// This class inherits from \link colvarparse \endlink and from
/// std::vector<colvarmodule::atom>, and hence all functions and
/// operators (including the bracket operator, group[i]) can be used
/// on an \link atom_group \endlink object. It can be initialized as
/// a vector, or by parsing a keyword in the configuration.
class colvarmodule::atom_group
: public std::vector<cvm::atom>,
public colvarparse
{
public:
// Note: all members here are kept public, to make possible to any
// object accessing and manipulating them
/// \brief If this option is on, this group merely acts as a wrapper
/// for a fixed position; any calls to atoms within or to
/// functions that return disaggregated data will fail
bool b_dummy;
/// \brief dummy atom position
cvm::atom_pos dummy_atom_pos;
/// Sorted list of zero-based (internal) atom ids
/// (populated on-demand by create_sorted_ids)
std::vector<int> sorted_ids;
/// Allocates and populates the sorted list of atom ids
void create_sorted_ids (void);
/// \brief Before calculating colvars, move the group to overlap the
/// center of mass of reference coordinates
bool b_center;
/// \brief Right after updating atom coordinates (and after
/// centering coordinates, if b_center is true), rotate the group to
/// overlap the reference coordinates. You should not manipulate
/// atoms individually if you turn on this flag.
///
/// Note: gradients will be calculated in the rotated frame: when
/// forces will be applied, they will rotated back to the original
/// frame
bool b_rotate;
/// Rotation between the group and its reference coordinates
cvm::rotation rot;
/// \brief In case b_center or b_rotate is true, use these reference
/// coordinates
std::vector<cvm::atom_pos> ref_pos;
/// \brief Center of geometry of the reference coordinates; regardless
/// of whether b_center is true, ref_pos is centered to zero at
/// initialization, and ref_pos_cog serves to center the positions
cvm::atom_pos ref_pos_cog;
/// \brief In case b_center or b_rotate is true, fit this group to
/// the reference positions (default: the parent group itself)
atom_group *ref_pos_group;
/// Total mass of the atom group
cvm::real total_mass;
/// \brief Don't apply any force on this group (use its coordinates
/// only to calculate a colvar)
bool noforce;
/// \brief Initialize the group by looking up its configuration
/// string in conf and parsing it; this is actually done by parse(),
/// which is a member function so that a group can be initialized
/// also after construction
atom_group (std::string const &conf,
char const *key,
atom_group *ref_pos_group = NULL);
/// \brief Initialize the group by looking up its configuration
/// string in conf and parsing it
void parse (std::string const &conf,
char const *key,
atom_group *ref_pos_group = NULL);
/// \brief Initialize the group after a temporary vector of atoms
atom_group (std::vector<cvm::atom> const &atoms);
/// \brief Add an atom to this group
void add_atom (cvm::atom const &a);
/// \brief Default constructor
atom_group();
/// \brief Destructor
~atom_group();
/// \brief Get the current positions; if b_center or b_rotate are
/// true, center and/or rotate the coordinates right after reading
/// them
void read_positions();
/// \brief Move all positions
void apply_translation (cvm::rvector const &t);
/// \brief Rotate all positions
void apply_rotation (cvm::rotation const &q);
/// \brief Get the current velocities; this must be called always
/// *after* read_positions(); if b_rotate is defined, the same
/// rotation applied to the coordinates will be used
void read_velocities();
/// \brief Get the current system_forces; this must be called always
/// *after* read_positions(); if b_rotate is defined, the same
/// rotation applied to the coordinates will be used
void read_system_forces();
/// Call reset_data() for each atom
inline void reset_atoms_data()
{
for (cvm::atom_iter ai = this->begin(); ai != this->end(); ai++)
ai->reset_data();
}
/// \brief Return a copy of the current atom positions
std::vector<cvm::atom_pos> positions() const;
/// \brief Return a copy of the current atom positions, shifted by a constant vector
std::vector<cvm::atom_pos> positions_shifted (cvm::rvector const &shift) const;
/// \brief Return the center of geometry of the positions \param ref_pos
/// Use the closest periodic images to this position
cvm::atom_pos center_of_geometry (cvm::atom_pos const &ref_pos);
/// \brief Return the center of geometry of the positions, assuming
/// that coordinates are already pbc-wrapped
cvm::atom_pos center_of_geometry() const;
/// \brief Return the center of mass of the positions \param ref_pos
/// Use the closest periodic images to this position
cvm::atom_pos center_of_mass (cvm::atom_pos const &ref_pos);
/// \brief Return the center of mass of the positions, assuming that
/// coordinates are already pbc-wrapped
cvm::atom_pos center_of_mass() const;
/// \brief Store atom positions from the previous step
std::vector<cvm::atom_pos> old_pos;
/// \brief Return a copy of the current atom velocities
std::vector<cvm::rvector> velocities() const;
/// \brief Return a copy of the system forces
std::vector<cvm::rvector> system_forces() const;
/// \brief Return a copy of the aggregated total force on the group
cvm::rvector system_force() const;
/// \brief Shorthand: save the specified gradient on each atom,
/// weighting with the atom mass (mostly used in combination with
/// \link center_of_mass() \endlink)
void set_weighted_gradient (cvm::rvector const &grad);
/// \brief Used by a (scalar) colvar to apply its force on its \link
/// atom_group \endlink members
///
/// The (scalar) force is multiplied by the colvar gradient for each
/// atom; this should be used when a colvar with scalar \link
/// colvarvalue \endlink type is used (this is the most frequent
/// case: for colvars with a non-scalar type, the most convenient
/// solution is to sum together the Cartesian forces from all the
/// colvar components, and use apply_force() or apply_forces()). If
/// the group is being rotated to a reference frame (e.g. to express
/// the colvar independently from the solute rotation), the
/// gradients are temporarily to the original frame.
void apply_colvar_force (cvm::real const &force);
/// \brief Apply a force "to the center of mass", i.e. the force is
/// distributed on each atom according to its mass
///
/// If the group is being rotated to a reference frame (e.g. to
/// express the colvar independently from the solute rotation), the
/// force is rotated back to the original frame. Colvar gradients
/// are not used, either because they were not defined (e.g because
/// the colvar has not a scalar value) or the biases require to
/// micromanage the force.
void apply_force (cvm::rvector const &force);
/// \brief Apply an array of forces directly on the individual
/// atoms; the length of the specified vector must be the same of
/// this \link atom_group \endlink.
///
/// If the group is being rotated to a reference frame (e.g. to
/// express the colvar independently from the solute rotation), the
/// forces are rotated back to the original frame. Colvar gradients
/// are not used, either because they were not defined (e.g because
/// the colvar has not a scalar value) or the biases require to
/// micromanage the forces.
void apply_forces (std::vector<cvm::rvector> const &forces);
};
#endif
// Emacs
// Local Variables:
// mode: C++
// End:

486
lib/colvars/colvarbias.cpp Normal file
View File

@ -0,0 +1,486 @@
#include "colvarmodule.h"
#include "colvarvalue.h"
#include "colvarbias.h"
colvarbias::colvarbias (std::string const &conf, char const *key)
: colvarparse(), has_data (false)
{
cvm::log ("Initializing a new \""+std::string (key)+"\" instance.\n");
size_t rank = 1;
std::string const key_str (key);
if (to_lower_cppstr (key_str) == std::string ("abf")) {
rank = cvm::n_abf_biases+1;
}
if (to_lower_cppstr (key_str) == std::string ("harmonic")) {
rank = cvm::n_harm_biases+1;
}
if (to_lower_cppstr (key_str) == std::string ("histogram")) {
rank = cvm::n_histo_biases+1;
}
if (to_lower_cppstr (key_str) == std::string ("metadynamics")) {
rank = cvm::n_meta_biases+1;
}
get_keyval (conf, "name", name, key_str+cvm::to_str (rank));
// lookup the associated colvars
std::vector<std::string> colvars_str;
if (get_keyval (conf, "colvars", colvars_str)) {
for (size_t i = 0; i < colvars_str.size(); i++) {
add_colvar (colvars_str[i]);
}
}
if (!colvars.size()) {
cvm::fatal_error ("Error: no collective variables specified.\n");
}
}
colvarbias::colvarbias()
: colvarparse(), has_data (false)
{}
void colvarbias::add_colvar (std::string const &cv_name)
{
if (colvar *cvp = cvm::colvar_p (cv_name)) {
cvp->enable (colvar::task_gradients);
if (cvm::debug())
cvm::log ("Applying this bias to collective variable \""+
cvp->name+"\".\n");
colvars.push_back (cvp);
colvar_forces.push_back (colvarvalue (cvp->type()));
} else {
cvm::fatal_error ("Error: cannot find a colvar named \""+
cv_name+"\".\n");
}
}
void colvarbias::communicate_forces()
{
for (size_t i = 0; i < colvars.size(); i++) {
if (cvm::debug()) {
cvm::log ("Communicating a force to colvar \""+
colvars[i]->name+"\", of type \""+
colvarvalue::type_desc[colvars[i]->type()]+"\".\n");
}
colvars[i]->add_bias_force (colvar_forces[i]);
}
}
colvarbias_harmonic::colvarbias_harmonic (std::string const &conf,
char const *key)
: colvarbias (conf, key),
target_nsteps (0)
{
get_keyval (conf, "forceConstant", force_k, 1.0);
for (size_t i = 0; i < colvars.size(); i++) {
if (colvars[i]->width != 1.0)
cvm::log ("The force constant for colvar \""+colvars[i]->name+
"\" will be rescaled to "+
cvm::to_str (force_k/(colvars[i]->width*colvars[i]->width))+
" according to the specified width.\n");
}
// get the initial restraint centers
colvar_centers.resize (colvars.size());
colvar_centers_raw.resize (colvars.size());
for (size_t i = 0; i < colvars.size(); i++) {
colvar_centers[i].type (colvars[i]->type());
colvar_centers_raw[i].type (colvars[i]->type());
}
if (get_keyval (conf, "centers", colvar_centers, colvar_centers)) {
for (size_t i = 0; i < colvars.size(); i++) {
colvar_centers[i].apply_constraints();
colvar_centers_raw[i] = colvar_centers[i];
}
} else {
colvar_centers.clear();
cvm::fatal_error ("Error: must define the initial centers of the restraints.\n");
}
if (colvar_centers.size() != colvars.size())
cvm::fatal_error ("Error: number of harmonic centers does not match "
"that of collective variables.\n");
if (get_keyval (conf, "targetCenters", target_centers, colvar_centers)) {
b_chg_centers = true;
target_nstages = 0;
for (size_t i = 0; i < target_centers.size(); i++) {
target_centers[i].apply_constraints();
}
} else {
b_chg_centers = false;
target_centers.clear();
}
if (get_keyval (conf, "targetForceConstant", target_force_k, 0.0)) {
if (b_chg_centers)
cvm::fatal_error ("Error: cannot specify both targetCenters and targetForceConstant.\n");
starting_force_k = force_k;
b_chg_force_k = true;
get_keyval (conf, "targetEquilSteps", target_equil_steps, 0);
get_keyval (conf, "lambdaSchedule", lambda_schedule, lambda_schedule);
if (lambda_schedule.size()) {
// There is one more lambda-point than stages
target_nstages = lambda_schedule.size() - 1;
} else {
target_nstages = 0;
}
} else {
b_chg_force_k = false;
}
if (b_chg_centers || b_chg_force_k) {
get_keyval (conf, "targetNumSteps", target_nsteps, 0);
if (!target_nsteps)
cvm::fatal_error ("Error: targetNumSteps must be non-zero.\n");
if (get_keyval (conf, "targetNumStages", target_nstages, target_nstages) &&
lambda_schedule.size()) {
cvm::fatal_error ("Error: targetNumStages and lambdaSchedule are incompatible.\n");
}
if (target_nstages) {
// This means that either numStages of lambdaSchedule has been provided
stage = -1;
restraint_FE = 0.0;
}
if (get_keyval (conf, "targetForceExponent", force_k_exp, 1.0)) {
if (! b_chg_force_k)
cvm::log ("Warning: not changing force constant: targetForceExponent will be ignored\n");
if (force_k_exp < 1.0)
cvm::log ("Warning: for all practical purposes, targetForceExponent should be 1.0 or greater.\n");
}
}
if (cvm::debug())
cvm::log ("Done initializing a new harmonic restraint bias.\n");
}
void colvarbias::change_configuration(std::string const &conf)
{
cvm::fatal_error ("Error: change_configuration() not implemented.\n");
}
cvm::real colvarbias::energy_difference(std::string const &conf)
{
cvm::fatal_error ("Error: energy_difference() not implemented.\n");
return 0.;
}
void colvarbias_harmonic::change_configuration(std::string const &conf)
{
get_keyval (conf, "forceConstant", force_k, force_k);
if (get_keyval (conf, "centers", colvar_centers, colvar_centers)) {
for (size_t i = 0; i < colvars.size(); i++) {
colvar_centers[i].apply_constraints();
colvar_centers_raw[i] = colvar_centers[i];
}
}
}
cvm::real colvarbias_harmonic::energy_difference(std::string const &conf)
{
std::vector<colvarvalue> alt_colvar_centers;
cvm::real alt_force_k;
cvm::real alt_bias_energy = 0.0;
get_keyval (conf, "forceConstant", alt_force_k, force_k);
alt_colvar_centers.resize (colvars.size());
for (size_t i = 0; i < colvars.size(); i++) {
alt_colvar_centers[i].type (colvars[i]->type());
}
if (get_keyval (conf, "centers", alt_colvar_centers, colvar_centers)) {
for (size_t i = 0; i < colvars.size(); i++) {
colvar_centers[i].apply_constraints();
}
}
for (size_t i = 0; i < colvars.size(); i++) {
alt_bias_energy += 0.5 * alt_force_k / (colvars[i]->width * colvars[i]->width) *
colvars[i]->dist2(colvars[i]->value(), alt_colvar_centers[i]);
}
return alt_bias_energy - bias_energy;
}
cvm::real colvarbias_harmonic::update()
{
bias_energy = 0.0;
if (cvm::debug())
cvm::log ("Updating the harmonic bias \""+this->name+"\".\n");
// Setup first stage of staged variable force constant calculation
if (b_chg_force_k && target_nstages && stage == -1) {
stage = 0;
cvm::real lambda;
if (lambda_schedule.size()) {
lambda = lambda_schedule[0];
} else {
lambda = 0.0;
}
force_k = starting_force_k + (target_force_k - starting_force_k)
* std::pow (lambda, force_k_exp);
cvm::log ("Harmonic restraint " + this->name + ", stage " +
cvm::to_str(stage) + " : lambda = " + cvm::to_str(lambda));
cvm::log ("Setting force constant to " + cvm::to_str (force_k));
}
// Force and energy calculation
for (size_t i = 0; i < colvars.size(); i++) {
colvar_forces[i] =
(-0.5) * force_k /
(colvars[i]->width * colvars[i]->width) *
colvars[i]->dist2_lgrad (colvars[i]->value(),
colvar_centers[i]);
bias_energy += 0.5 * force_k / (colvars[i]->width * colvars[i]->width) *
colvars[i]->dist2(colvars[i]->value(), colvar_centers[i]);
if (cvm::debug())
cvm::log ("dist_grad["+cvm::to_str (i)+
"] = "+cvm::to_str (colvars[i]->dist2_lgrad (colvars[i]->value(),
colvar_centers[i]))+"\n");
}
if (cvm::debug())
cvm::log ("Current forces for the harmonic bias \""+
this->name+"\": "+cvm::to_str (colvar_forces)+".\n");
if (b_chg_centers) {
if (!centers_incr.size()) {
// if this is the first calculation, calculate the advancement
// at each simulation step (or stage, if applicable)
// (take current stage into account: it can be non-zero
// if we are restarting a staged calculation)
centers_incr.resize (colvars.size());
for (size_t i = 0; i < colvars.size(); i++) {
centers_incr[i].type (colvars[i]->type());
centers_incr[i] = (target_centers[i] - colvar_centers[i]) /
cvm::real ( target_nstages ? (target_nstages - stage) :
(target_nsteps - cvm::step_absolute()));
}
if (cvm::debug())
cvm::log ("Center increment for the harmonic bias \""+
this->name+"\": "+cvm::to_str (centers_incr)+".\n");
}
if (cvm::debug())
cvm::log ("Current centers for the harmonic bias \""+
this->name+"\": "+cvm::to_str (colvar_centers)+".\n");
if (target_nstages) {
if (cvm::step_absolute() > 0
&& (cvm::step_absolute() % target_nsteps) == 0
&& stage < target_nstages) {
// any per-stage calculation, e.g. free energy stuff
// should be done here
for (size_t i = 0; i < colvars.size(); i++) {
colvar_centers_raw[i] += centers_incr[i];
colvar_centers[i] = colvar_centers_raw[i];
colvars[i]->wrap(colvar_centers[i]);
colvar_centers[i].apply_constraints();
}
stage++;
cvm::log ("Moving restraint stage " + cvm::to_str(stage) +
" : setting centers to " + cvm::to_str (colvar_centers));
}
} else if (cvm::step_absolute() < target_nsteps) {
// move the restraint centers in the direction of the targets
// (slow growth)
for (size_t i = 0; i < colvars.size(); i++) {
colvar_centers_raw[i] += centers_incr[i];
colvar_centers[i] = colvar_centers_raw[i];
colvars[i]->wrap(colvar_centers[i]);
colvar_centers[i].apply_constraints();
}
}
}
if (b_chg_force_k) {
// Coupling parameter, between 0 and 1
cvm::real lambda;
if (target_nstages) {
// TI calculation: estimate free energy derivative
// need current lambda
if (lambda_schedule.size()) {
lambda = lambda_schedule[stage];
} else {
lambda = cvm::real(stage) / cvm::real(target_nstages);
}
if (target_equil_steps == 0 || cvm::step_absolute() % target_nsteps >= target_equil_steps) {
// Start averaging after equilibration period, if requested
// Square distance normalized by square colvar width
cvm::real dist_sq = 0.0;
for (size_t i = 0; i < colvars.size(); i++) {
dist_sq += colvars[i]->dist2 (colvars[i]->value(), colvar_centers[i])
/ (colvars[i]->width * colvars[i]->width);
}
restraint_FE += 0.5 * force_k_exp * std::pow(lambda, force_k_exp - 1.0)
* (target_force_k - starting_force_k) * dist_sq;
}
// Finish current stage...
if (cvm::step_absolute() % target_nsteps == 0 &&
cvm::step_absolute() > 0) {
cvm::log ("Lambda= " + cvm::to_str (lambda) + " dA/dLambda= "
+ cvm::to_str (restraint_FE / cvm::real(target_nsteps - target_equil_steps)));
// ...and move on to the next one
if (stage < target_nstages) {
restraint_FE = 0.0;
stage++;
if (lambda_schedule.size()) {
lambda = lambda_schedule[stage];
} else {
lambda = cvm::real(stage) / cvm::real(target_nstages);
}
force_k = starting_force_k + (target_force_k - starting_force_k)
* std::pow (lambda, force_k_exp);
cvm::log ("Harmonic restraint " + this->name + ", stage " +
cvm::to_str(stage) + " : lambda = " + cvm::to_str(lambda));
cvm::log ("Setting force constant to " + cvm::to_str (force_k));
}
}
} else if (cvm::step_absolute() <= target_nsteps) {
// update force constant (slow growth)
lambda = cvm::real(cvm::step_absolute()) / cvm::real(target_nsteps);
force_k = starting_force_k + (target_force_k - starting_force_k)
* std::pow (lambda, force_k_exp);
}
}
if (cvm::debug())
cvm::log ("Done updating the harmonic bias \""+this->name+"\".\n");
return bias_energy;
}
std::istream & colvarbias_harmonic::read_restart (std::istream &is)
{
size_t const start_pos = is.tellg();
cvm::log ("Restarting harmonic bias \""+
this->name+"\".\n");
std::string key, brace, conf;
if ( !(is >> key) || !(key == "harmonic") ||
!(is >> brace) || !(brace == "{") ||
!(is >> colvarparse::read_block ("configuration", conf)) ) {
cvm::log ("Error: in reading restart configuration for harmonic bias \""+
this->name+"\" at position "+
cvm::to_str (is.tellg())+" in stream.\n");
is.clear();
is.seekg (start_pos, std::ios::beg);
is.setstate (std::ios::failbit);
return is;
}
// int id = -1;
std::string name = "";
// if ( ( (colvarparse::get_keyval (conf, "id", id, -1, colvarparse::parse_silent)) &&
// (id != this->id) ) ||
if ( (colvarparse::get_keyval (conf, "name", name, std::string (""), colvarparse::parse_silent)) &&
(name != this->name) )
cvm::fatal_error ("Error: in the restart file, the "
"\"harmonic\" block has a wrong name\n");
// if ( (id == -1) && (name == "") ) {
if (name.size() == 0) {
cvm::fatal_error ("Error: \"harmonic\" block in the restart file "
"has no identifiers.\n");
}
if (b_chg_centers) {
cvm::log ("Reading the updated restraint centers from the restart.\n");
if (!get_keyval (conf, "centers", colvar_centers))
cvm::fatal_error ("Error: restraint centers are missing from the restart.\n");
if (!get_keyval (conf, "centers_raw", colvar_centers_raw))
cvm::fatal_error ("Error: \"raw\" restraint centers are missing from the restart.\n");
}
if (b_chg_force_k) {
cvm::log ("Reading the updated force constant from the restart.\n");
if (!get_keyval (conf, "forceConstant", force_k))
cvm::fatal_error ("Error: force constant is missing from the restart.\n");
}
if (target_nstages) {
cvm::log ("Reading current stage from the restart.\n");
if (!get_keyval (conf, "stage", stage))
cvm::fatal_error ("Error: current stage is missing from the restart.\n");
}
is >> brace;
if (brace != "}") {
cvm::fatal_error ("Error: corrupt restart information for harmonic bias \""+
this->name+"\": no matching brace at position "+
cvm::to_str (is.tellg())+" in the restart file.\n");
is.setstate (std::ios::failbit);
}
return is;
}
std::ostream & colvarbias_harmonic::write_restart (std::ostream &os)
{
os << "harmonic {\n"
<< " configuration {\n"
// << " id " << this->id << "\n"
<< " name " << this->name << "\n";
if (b_chg_centers) {
os << " centers ";
for (size_t i = 0; i < colvars.size(); i++) {
os << " " << colvar_centers[i];
}
os << "\n";
os << " centers_raw ";
for (size_t i = 0; i < colvars.size(); i++) {
os << " " << colvar_centers_raw[i];
}
os << "\n";
}
if (b_chg_force_k) {
os << " forceConstant "
<< std::setprecision (cvm::en_prec)
<< std::setw (cvm::en_width) << force_k << "\n";
}
if (target_nstages) {
os << " stage " << std::setw (cvm::it_width)
<< stage << "\n";
}
os << " }\n"
<< "}\n\n";
return os;
}

167
lib/colvars/colvarbias.h Normal file
View File

@ -0,0 +1,167 @@
#ifndef COLVARBIAS_H
#define COLVARBIAS_H
#include "colvar.h"
#include "colvarparse.h"
/// \brief Collective variable bias, base class
class colvarbias : public colvarparse {
public:
/// Numeric id of this bias
int id;
/// Name of this bias
std::string name;
/// Add a new collective variable to this bias
void add_colvar (std::string const &cv_name);
/// Retrieve colvar values and calculate their biasing forces
/// Return bias energy
virtual cvm::real update() = 0;
/// Load new configuration - force constant and/or centers only
virtual void change_configuration(std::string const &conf);
/// Calculate change in energy from using alternate configuration
virtual cvm::real energy_difference(std::string const &conf);
/// Perform analysis tasks
virtual inline void analyse() {}
/// Send forces to the collective variables
void communicate_forces();
/// \brief Constructor
///
/// The constructor of the colvarbias base class is protected, so
/// that it can only be called from inherited classes
colvarbias (std::string const &conf, char const *key);
/// Default constructor
colvarbias();
/// Destructor
virtual inline ~colvarbias() {}
/// Read the bias configuration from a restart file
virtual std::istream & read_restart (std::istream &is) = 0;
/// Write the bias configuration to a restart file
virtual std::ostream & write_restart (std::ostream &os) = 0;
protected:
/// \brief Pointers to collective variables to which the bias is
/// applied; current values and metric functions will be obtained
/// through each colvar object
std::vector<colvar *> colvars;
/// \brief Current forces from this bias to the colvars
std::vector<colvarvalue> colvar_forces;
/// \brief Current energy of this bias (colvar_forces should be
/// obtained by deriving this)
cvm::real bias_energy;
/// \brief Whether this bias has already accumulated information
/// (when relevant)
bool has_data;
};
/// \brief Harmonic restraint, optionally moving towards a target
/// (implementation of \link colvarbias \endlink)
class colvarbias_harmonic : public colvarbias {
public:
/// Retrieve colvar values and calculate their biasing forces
virtual cvm::real update();
/// Load new configuration - force constant and/or centers only
virtual void change_configuration(std::string const &conf);
/// Calculate change in energy from using alternate configuration
virtual cvm::real energy_difference(std::string const &conf);
/// Read the bias configuration from a restart file
virtual std::istream & read_restart (std::istream &is);
/// Write the bias configuration to a restart file
virtual std::ostream & write_restart (std::ostream &os);
/// \brief Constructor
colvarbias_harmonic (std::string const &conf, char const *key);
/// Destructor
virtual inline ~colvarbias_harmonic() {}
protected:
/// \brief Restraint centers
std::vector<colvarvalue> colvar_centers;
/// \brief Restraint centers without wrapping or constraints applied
std::vector<colvarvalue> colvar_centers_raw;
/// \brief Restraint force constant
cvm::real force_k;
/// \brief Moving target?
bool b_chg_centers;
/// \brief Changing force constant?
bool b_chg_force_k;
/// \brief Restraint force constant (target value)
cvm::real target_force_k;
/// \brief Equilibration steps for restraint FE calculation through TI
cvm::real target_equil_steps;
/// \brief Restraint force constant (starting value)
cvm::real starting_force_k;
/// \brief Lambda-schedule for custom varying force constant
std::vector<cvm::real> lambda_schedule;
/// \brief New restraint centers
std::vector<colvarvalue> target_centers;
/// \brief Amplitude of the restraint centers' increment at each step
/// (or stage) towards the new values (calculated from target_nsteps)
std::vector<colvarvalue> centers_incr;
/// \brief Exponent for varying the force constant
cvm::real force_k_exp;
/// \brief Number of steps required to reach the target force constant
/// or restraint centers
size_t target_nsteps;
/// \brief Number of stages over which to perform the change
/// If zero, perform a continuous change
size_t target_nstages;
/// \brief Number of current stage of the perturbation
size_t stage;
/// \brief Intermediate quantity to compute the restraint free energy
/// (in TI, would be the accumulating FE derivative)
cvm::real restraint_FE;
};
#endif
// Emacs
// Local Variables:
// mode: C++
// End:

View File

@ -0,0 +1,492 @@
/********************************************************************************
* Implementation of the ABF and histogram biases *
********************************************************************************/
#include "colvarmodule.h"
#include "colvar.h"
#include "colvarbias_abf.h"
/// ABF bias constructor; parses the config file
colvarbias_abf::colvarbias_abf (std::string const &conf, char const *key)
: colvarbias (conf, key),
gradients (NULL),
samples (NULL)
{
if (cvm::temperature() == 0.0)
cvm::log ("WARNING: ABF should not be run without a thermostat or at 0 Kelvin!\n");
// ************* parsing general ABF options ***********************
get_keyval (conf, "applyBias", apply_bias, true);
if (!apply_bias) cvm::log ("WARNING: ABF biases will *not* be applied!\n");
get_keyval (conf, "updateBias", update_bias, true);
if (!update_bias) cvm::log ("WARNING: ABF biases will *not* be updated!\n");
get_keyval (conf, "hideJacobian", hide_Jacobian, false);
if (hide_Jacobian) {
cvm::log ("Jacobian (geometric) forces will be handled internally.\n");
} else {
cvm::log ("Jacobian (geometric) forces will be included in reported free energy gradients.\n");
}
get_keyval (conf, "fullSamples", full_samples, 200);
if ( full_samples <= 1 ) full_samples = 1;
min_samples = full_samples / 2;
// full_samples - min_samples >= 1 is guaranteed
get_keyval (conf, "inputPrefix", input_prefix, std::vector<std::string> ());
get_keyval (conf, "outputFreq", output_freq, cvm::restart_out_freq);
get_keyval (conf, "historyFreq", history_freq, 0);
b_history_files = (history_freq > 0);
// ************* checking the associated colvars *******************
if (colvars.size() == 0) {
cvm::fatal_error ("Error: no collective variables specified for the ABF bias.\n");
}
for (size_t i = 0; i < colvars.size(); i++) {
if (colvars[i]->type() != colvarvalue::type_scalar) {
cvm::fatal_error ("Error: ABF bias can only use scalar-type variables.\n");
}
colvars[i]->enable (colvar::task_gradients);
if (update_bias) {
// Request calculation of system force (which also checks for availability)
colvars[i]->enable (colvar::task_system_force);
if (!colvars[i]->tasks[colvar::task_extended_lagrangian]) {
// request computation of Jacobian force
colvars[i]->enable (colvar::task_Jacobian_force);
// request Jacobian force as part as system force
// except if the user explicitly requires the "silent" Jacobian
// correction AND the colvar has a single component
if (hide_Jacobian) {
if (colvars[i]->n_components() > 1) {
cvm::log ("WARNING: colvar \"" + colvars[i]->name
+ "\" has multiple components; reporting its Jacobian forces\n");
colvars[i]->enable (colvar::task_report_Jacobian_force);
}
} else {
colvars[i]->enable (colvar::task_report_Jacobian_force);
}
}
}
// Here we could check for orthogonality of the Cartesian coordinates
// and make it just a warning if some parameter is set?
}
bin.assign (colvars.size(), 0);
force_bin.assign (colvars.size(), 0);
force = new cvm::real [colvars.size()];
// Construct empty grids based on the colvars
samples = new colvar_grid_count (colvars);
gradients = new colvar_grid_gradient (colvars);
gradients->samples = samples;
samples->has_parent_data = true;
// If custom grids are provided, read them
if ( input_prefix.size() > 0 ) {
read_gradients_samples ();
}
cvm::log ("Finished ABF setup.\n");
}
/// Destructor
colvarbias_abf::~colvarbias_abf()
{
if (samples) {
delete samples;
samples = NULL;
}
if (gradients) {
delete gradients;
gradients = NULL;
}
delete [] force;
}
/// Update the FE gradient, compute and apply biasing force
/// also output data to disk if needed
cvm::real colvarbias_abf::update()
{
if (cvm::debug()) cvm::log ("Updating ABF bias " + this->name);
if (cvm::step_relative() == 0) {
// At first timestep, do only:
// initialization stuff (file operations relying on n_abf_biases
// compute current value of colvars
if ( cvm::n_abf_biases == 1 && cvm::n_meta_biases == 0 ) {
// This is the only ABF bias
output_prefix = cvm::output_prefix;
} else {
output_prefix = cvm::output_prefix + "." + this->name;
}
for (size_t i=0; i<colvars.size(); i++) {
bin[i] = samples->current_bin_scalar(i);
}
} else {
for (size_t i=0; i<colvars.size(); i++) {
bin[i] = samples->current_bin_scalar(i);
}
if ( update_bias && samples->index_ok (force_bin) ) {
// Only if requested and within bounds of the grid...
for (size_t i=0; i<colvars.size(); i++) { // get forces (lagging by 1 timestep) from colvars
force[i] = colvars[i]->system_force();
}
gradients->acc_force (force_bin, force);
}
}
// save bin for next timestep
force_bin = bin;
// Reset biasing forces from previous timestep
for (size_t i=0; i<colvars.size(); i++) {
colvar_forces[i].reset();
}
// Compute and apply the new bias, if applicable
if ( apply_bias && samples->index_ok (bin) ) {
size_t count = samples->value (bin);
cvm::real fact = 1.0;
// Factor that ensures smooth introduction of the force
if ( count < full_samples ) {
fact = ( count < min_samples) ? 0.0 :
(cvm::real (count - min_samples)) / (cvm::real (full_samples - min_samples));
}
const cvm::real * grad = &(gradients->value (bin));
if ( fact != 0.0 ) {
if ( (colvars.size() == 1) && colvars[0]->periodic_boundaries() ) {
// Enforce a zero-mean bias on periodic, 1D coordinates
colvar_forces[0].real_value += fact * (grad[0] / cvm::real (count) - gradients->average ());
} else {
for (size_t i=0; i<colvars.size(); i++) {
// subtracting the mean force (opposite of the FE gradient) means adding the gradient
colvar_forces[i].real_value += fact * grad[i] / cvm::real (count);
// without .real_value, the above would do (cheap) runtime type checking
}
}
}
}
if (output_freq && (cvm::step_absolute() % output_freq) == 0) {
if (cvm::debug()) cvm::log ("ABF bias trying to write gradients and samples to disk");
write_gradients_samples (output_prefix);
}
if (b_history_files && (cvm::step_absolute() % history_freq) == 0) {
// append to existing file only if cvm::step_absolute() > 0
// otherwise, backup and replace
write_gradients_samples (output_prefix + ".hist", (cvm::step_absolute() > 0));
}
return 0.0; // TODO compute bias energy whenever possible (i.e. 1D with updateBias off)
}
void colvarbias_abf::write_gradients_samples (const std::string &prefix, bool append)
{
std::string samples_out_name = prefix + ".count";
std::string gradients_out_name = prefix + ".grad";
std::ios::openmode mode = (append ? std::ios::app : std::ios::out);
std::ofstream samples_os;
std::ofstream gradients_os;
if (!append) cvm::backup_file (samples_out_name.c_str());
samples_os.open (samples_out_name.c_str(), mode);
if (!samples_os.good()) cvm::fatal_error ("Error opening ABF samples file " + samples_out_name + " for writing");
samples->write_multicol (samples_os);
samples_os.close ();
if (!append) cvm::backup_file (gradients_out_name.c_str());
gradients_os.open (gradients_out_name.c_str(), mode);
if (!gradients_os.good()) cvm::fatal_error ("Error opening ABF gradient file " + gradients_out_name + " for writing");
gradients->write_multicol (gradients_os);
gradients_os.close ();
if (colvars.size () == 1) {
std::string pmf_out_name = prefix + ".pmf";
if (!append) cvm::backup_file (pmf_out_name.c_str());
std::ofstream pmf_os;
// Do numerical integration and output a PMF
pmf_os.open (pmf_out_name.c_str(), mode);
if (!pmf_os.good()) cvm::fatal_error ("Error opening pmf file " + pmf_out_name + " for writing");
gradients->write_1D_integral (pmf_os);
pmf_os << std::endl;
pmf_os.close ();
}
return;
}
void colvarbias_abf::read_gradients_samples ()
{
std::string samples_in_name, gradients_in_name;
for ( size_t i = 0; i < input_prefix.size(); i++ ) {
samples_in_name = input_prefix[i] + ".count";
gradients_in_name = input_prefix[i] + ".grad";
// For user-provided files, the per-bias naming scheme may not apply
std::ifstream is;
cvm::log ("Reading sample count from " + samples_in_name + " and gradients from " + gradients_in_name);
is.open (samples_in_name.c_str());
if (!is.good()) cvm::fatal_error ("Error opening ABF samples file " + samples_in_name + " for reading");
samples->read_multicol (is, true);
is.close ();
is.clear();
is.open (gradients_in_name.c_str());
if (!is.good()) cvm::fatal_error ("Error opening ABF gradient file " + gradients_in_name + " for reading");
gradients->read_multicol (is, true);
is.close ();
}
return;
}
std::ostream & colvarbias_abf::write_restart (std::ostream& os)
{
std::ios::fmtflags flags (os.flags ());
os.setf(std::ios::fmtflags (0), std::ios::floatfield); // default floating-point format
os << "abf {\n"
<< " configuration {\n"
<< " name " << this->name << "\n";
os << " }\n";
os << "samples\n";
samples->write_raw (os, 8);
os << "\ngradient\n";
gradients->write_raw (os);
os << "}\n\n";
os.flags (flags);
return os;
}
std::istream & colvarbias_abf::read_restart (std::istream& is)
{
if ( input_prefix.size() > 0 ) {
cvm::fatal_error ("ERROR: cannot provide both inputPrefix and restart information (colvarsInput)");
}
size_t const start_pos = is.tellg();
cvm::log ("Restarting ABF bias \""+
this->name+"\".\n");
std::string key, brace, conf;
if ( !(is >> key) || !(key == "abf") ||
!(is >> brace) || !(brace == "{") ||
!(is >> colvarparse::read_block ("configuration", conf)) ) {
cvm::log ("Error: in reading restart configuration for ABF bias \""+
this->name+"\" at position "+
cvm::to_str (is.tellg())+" in stream.\n");
is.clear();
is.seekg (start_pos, std::ios::beg);
is.setstate (std::ios::failbit);
return is;
}
std::string name = "";
if ( (colvarparse::get_keyval (conf, "name", name, std::string (""), colvarparse::parse_silent)) &&
(name != this->name) )
cvm::fatal_error ("Error: in the restart file, the "
"\"abf\" block has wrong name (" + name + ")\n");
if ( name == "" ) {
cvm::fatal_error ("Error: \"abf\" block in the restart file has no name.\n");
}
if ( !(is >> key) || !(key == "samples")) {
cvm::log ("Error: in reading restart configuration for ABF bias \""+
this->name+"\" at position "+
cvm::to_str (is.tellg())+" in stream.\n");
is.clear();
is.seekg (start_pos, std::ios::beg);
is.setstate (std::ios::failbit);
return is;
}
if (! samples->read_raw (is)) {
samples->read_raw_error();
}
if ( !(is >> key) || !(key == "gradient")) {
cvm::log ("Error: in reading restart configuration for ABF bias \""+
this->name+"\" at position "+
cvm::to_str (is.tellg())+" in stream.\n");
is.clear();
is.seekg (start_pos, std::ios::beg);
is.setstate (std::ios::failbit);
return is;
}
if (! gradients->read_raw (is)) {
gradients->read_raw_error();
}
is >> brace;
if (brace != "}") {
cvm::fatal_error ("Error: corrupt restart information for ABF bias \""+
this->name+"\": no matching brace at position "+
cvm::to_str (is.tellg())+" in the restart file.\n");
is.setstate (std::ios::failbit);
}
return is;
}
/// Histogram "bias" constructor
colvarbias_histogram::colvarbias_histogram (std::string const &conf, char const *key)
: colvarbias (conf, key),
grid (NULL)
{
get_keyval (conf, "outputfreq", output_freq, cvm::restart_out_freq);
if ( output_freq == 0 ) {
cvm::fatal_error ("User required histogram with zero output frequency");
}
grid = new colvar_grid_count (colvars);
bin.assign (colvars.size(), 0);
out_name = cvm::output_prefix + "." + this->name + ".dat";
cvm::log ("Histogram will be written to file " + out_name);
cvm::log ("Finished histogram setup.\n");
}
/// Destructor
colvarbias_histogram::~colvarbias_histogram()
{
if (grid_os.good()) grid_os.close();
if (grid) {
delete grid;
grid = NULL;
}
}
/// Update the grid
cvm::real colvarbias_histogram::update()
{
if (cvm::debug()) cvm::log ("Updating Grid bias " + this->name);
for (size_t i=0; i<colvars.size(); i++) {
bin[i] = grid->current_bin_scalar(i);
}
if ( grid->index_ok (bin) ) { // Only within bounds of the grid...
grid->incr_count (bin);
}
if (output_freq && (cvm::step_absolute() % output_freq) == 0) {
if (cvm::debug()) cvm::log ("Histogram bias trying to write grid to disk");
grid_os.open (out_name.c_str());
if (!grid_os.good()) cvm::fatal_error ("Error opening histogram file " + out_name + " for writing");
grid->write_multicol (grid_os);
grid_os.close ();
}
return 0.0; // no bias energy for histogram
}
std::istream & colvarbias_histogram::read_restart (std::istream& is)
{
size_t const start_pos = is.tellg();
cvm::log ("Restarting collective variable histogram \""+
this->name+"\".\n");
std::string key, brace, conf;
if ( !(is >> key) || !(key == "histogram") ||
!(is >> brace) || !(brace == "{") ||
!(is >> colvarparse::read_block ("configuration", conf)) ) {
cvm::log ("Error: in reading restart configuration for histogram \""+
this->name+"\" at position "+
cvm::to_str (is.tellg())+" in stream.\n");
is.clear();
is.seekg (start_pos, std::ios::beg);
is.setstate (std::ios::failbit);
return is;
}
int id = -1;
std::string name = "";
if ( (colvarparse::get_keyval (conf, "name", name, std::string (""), colvarparse::parse_silent)) &&
(name != this->name) )
cvm::fatal_error ("Error: in the restart file, the "
"\"histogram\" block has a wrong name: different system?\n");
if ( (id == -1) && (name == "") ) {
cvm::fatal_error ("Error: \"histogram\" block in the restart file "
"has no name.\n");
}
if ( !(is >> key) || !(key == "grid")) {
cvm::log ("Error: in reading restart configuration for histogram \""+
this->name+"\" at position "+
cvm::to_str (is.tellg())+" in stream.\n");
is.clear();
is.seekg (start_pos, std::ios::beg);
is.setstate (std::ios::failbit);
return is;
}
if (! grid->read_raw (is)) {
grid->read_raw_error();
}
is >> brace;
if (brace != "}") {
cvm::fatal_error ("Error: corrupt restart information for ABF bias \""+
this->name+"\": no matching brace at position "+
cvm::to_str (is.tellg())+" in the restart file.\n");
is.setstate (std::ios::failbit);
}
return is;
}
std::ostream & colvarbias_histogram::write_restart (std::ostream& os)
{
os << "histogram {\n"
<< " configuration {\n"
<< " name " << this->name << "\n";
os << " }\n";
os << "grid\n";
grid->write_raw (os, 8);
os << "}\n\n";
return os;
}

View File

@ -0,0 +1,95 @@
/************************************************************************
* Headers for the ABF and histogram biases *
************************************************************************/
#ifndef COLVARBIAS_ABF_H
#define COLVARBIAS_ABF_H
#include <vector>
#include <list>
#include <sstream>
#include <iomanip>
//#include <cmath>
#include "colvarbias.h"
#include "colvargrid.h"
typedef cvm::real* gradient_t;
/// ABF bias
class colvarbias_abf : public colvarbias {
public:
colvarbias_abf (std::string const &conf, char const *key);
~colvarbias_abf ();
cvm::real update ();
private:
/// Filename prefix for human-readable gradient/sample count output
std::string output_prefix;
/// Base filename(s) for reading previous gradient data (replaces data from restart file)
std::vector<std::string> input_prefix;
bool apply_bias;
bool update_bias;
bool hide_Jacobian;
size_t full_samples;
size_t min_samples;
/// frequency for updating output files (default: same as restartFreq?)
int output_freq;
/// Write combined files with a history of all output data?
bool b_history_files;
size_t history_freq;
// Internal data and methods
std::vector<int> bin, force_bin;
gradient_t force;
/// n-dim grid of free energy gradients
colvar_grid_gradient *gradients;
/// n-dim grid of number of samples
colvar_grid_count *samples;
/// Write human-readable FE gradients and sample count
void write_gradients_samples (const std::string &prefix, bool append = false);
/// Read human-readable FE gradients and sample count (if not using restart)
void read_gradients_samples ();
std::istream& read_restart (std::istream&);
std::ostream& write_restart (std::ostream&);
};
/// Histogram "bias" (does as the name says)
class colvarbias_histogram : public colvarbias {
public:
colvarbias_histogram (std::string const &conf, char const *key);
~colvarbias_histogram ();
cvm::real update ();
private:
/// n-dim histogram
colvar_grid_count *grid;
std::vector<int> bin;
std::string out_name;
int output_freq;
void write_grid ();
std::ofstream grid_os; /// Stream for writing grid to disk
std::istream& read_restart (std::istream&);
std::ostream& write_restart (std::ostream&);
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,418 @@
#ifndef COLVARBIAS_META_H
#define COLVARBIAS_META_H
#include <vector>
#include <list>
#include <sstream>
#include <fstream>
#include "colvarbias.h"
#include "colvargrid.h"
/// Metadynamics bias (implementation of \link colvarbias \endlink)
class colvarbias_meta : public colvarbias {
public:
/// Communication between different replicas
enum Communication {
/// One replica (default)
single_replica,
/// Hills added concurrently by several replicas
multiple_replicas
};
/// Communication between different replicas
Communication comm;
/// Constructor
colvarbias_meta (std::string const &conf, char const *key);
/// Default constructor
colvarbias_meta();
/// Destructor
virtual ~colvarbias_meta();
virtual cvm::real update();
virtual std::istream & read_restart (std::istream &is);
virtual std::ostream & write_restart (std::ostream &os);
virtual void write_pmf();
class hill;
typedef std::list<hill>::iterator hill_iter;
protected:
/// \brief width of a hill
///
/// The local width of each collective variable, multiplied by this
/// number, provides the hill width along that direction
cvm::real hill_width;
/// \brief Number of simulation steps between two hills
size_t new_hill_freq;
/// Write the hill logfile
bool b_hills_traj;
/// Logfile of hill management (creation and deletion)
std::ofstream hills_traj_os;
/// \brief List of hills used on this bias (total); if a grid is
/// employed, these don't need to be updated at every time step
std::list<hill> hills;
/// \brief Iterator to the first of the "newest" hills (when using
/// grids, those who haven't been mapped yet)
hill_iter new_hills_begin;
/// \brief List of hills used on this bias that are on the boundary
/// edges; these are updated regardless of whether hills are used
std::list<hill> hills_off_grid;
/// \brief Same as new_hills_begin, but for the off-grid ones
hill_iter new_hills_off_grid_begin;
/// Regenerate the hills_off_grid list
void recount_hills_off_grid (hill_iter h_first, hill_iter h_last,
colvar_grid_scalar *ge);
/// Read a hill from a file
std::istream & read_hill (std::istream &is);
/// \brief step present in a state file
///
/// When using grids and reading state files containing them
/// (multiple replicas), this is used to check whether a hill is
/// newer or older than the grids
size_t state_file_step;
/// \brief Add a new hill; if a .hills trajectory is written,
/// write it there; if there is more than one replica, communicate
/// it to the others
virtual std::list<hill>::const_iterator create_hill (hill const &h);
/// \brief Remove a previously saved hill (returns an iterator for
/// the next hill in the list)
virtual std::list<hill>::const_iterator delete_hill (hill_iter &h);
/// \brief Calculate the values of the hills, incrementing
/// bias_energy
virtual void calc_hills (hill_iter h_first,
hill_iter h_last,
cvm::real &energy,
std::vector<colvarvalue> const &values = std::vector<colvarvalue> (0));
/// \brief Calculate the forces acting on the i-th colvar,
/// incrementing colvar_forces[i]; must be called after calc_hills
/// each time the values of the colvars are changed
virtual void calc_hills_force (size_t const &i,
hill_iter h_first,
hill_iter h_last,
std::vector<colvarvalue> &forces,
std::vector<colvarvalue> const &values = std::vector<colvarvalue> (0));
/// Height of new hills
cvm::real hill_weight;
/// \brief Bin the hills on grids of energy and forces, and use them
/// to force the colvars (as opposed to deriving the hills analytically)
bool use_grids;
/// \brief Rebin the hills upon restarting
bool rebin_grids;
/// \brief Should the grids be expanded if necessary?
bool expand_grids;
/// \brief How often the hills should be projected onto the grids
size_t grids_freq;
/// \brief Whether to keep the hills in the restart file (e.g. to do
/// meaningful accurate rebinning afterwards)
bool keep_hills;
/// \brief Dump the free energy surface (.pmf file) every restartFrequency
bool dump_fes;
/// \brief Dump the free energy surface (.pmf file) every restartFrequency
/// using only the hills from this replica (only applicable to more than one replica)
bool dump_replica_fes;
/// \brief Dump the free energy surface files at different
/// time steps, appending the step number to each file
bool dump_fes_save;
/// \brief Try to read the restart information by allocating new
/// grids before replacing the current ones (used e.g. in
/// multiple_replicas)
bool safely_read_restart;
/// Hill energy, cached on a grid
colvar_grid_scalar *hills_energy;
/// Hill forces, cached on a grid
colvar_grid_gradient *hills_energy_gradients;
/// \brief Project the selected hills onto grids
void project_hills (hill_iter h_first, hill_iter h_last,
colvar_grid_scalar *ge, colvar_grid_gradient *gf);
// Multiple Replicas variables and functions
/// \brief Identifier for this replica
std::string replica_id;
/// \brief File containing the paths to the output files from this replica
std::string replica_file_name;
/// \brief Read the existing replicas on registry
virtual void update_replicas_registry();
/// \brief Read new data from replicas' files
virtual void read_replica_files();
/// \brief Write data to other replicas
virtual void write_replica_state_file();
/// \brief Additional, "mirror" metadynamics biases, to collect info
/// from the other replicas
///
/// These are supposed to be synchronized by reading data from the
/// other replicas, and not be modified by the "local" replica
std::vector<colvarbias_meta *> replicas;
/// \brief Frequency at which data the "mirror" biases are updated
size_t replica_update_freq;
/// List of replicas (and their output list files): contents are
/// copied into replicas_registry for convenience
std::string replicas_registry_file;
/// List of replicas (and their output list files)
std::string replicas_registry;
/// List of files written by this replica
std::string replica_list_file;
/// Hills energy and gradients written specifically for other
/// replica (in addition to its own restart file)
std::string replica_state_file;
/// Whether a mirror bias has read the latest version of its state file
bool replica_state_file_in_sync;
/// If there was a failure reading one of the files (because they
/// are not complete), this counter is incremented
size_t update_status;
/// Explicit hills communicated between replicas
///
/// This file becomes empty after replica_state_file is rewritten
std::string replica_hills_file;
/// \brief Output stream corresponding to replica_hills_file
std::ofstream replica_hills_os;
/// Position within replica_hills_file (when reading it)
size_t replica_hills_file_pos;
};
/// \brief A hill for the metadynamics bias
class colvarbias_meta::hill {
protected:
/// Value of the hill function (ranges between 0 and 1)
cvm::real hill_value;
/// Scale factor, which could be modified at runtime (default: 1)
cvm::real sW;
/// Maximum height in energy of the hill
cvm::real W;
/// Center of the hill in the collective variable space
std::vector<colvarvalue> centers;
/// Widths of the hill in the collective variable space
std::vector<cvm::real> widths;
public:
friend class colvarbias_meta;
/// Time step at which this hill was added
size_t it;
/// Identity of the replica who added this hill (only in multiple replica simulations)
std::string replica;
/// \brief Runtime constructor: data are read directly from
/// collective variables \param weight Weight of the hill \param
/// cv Pointer to the array of collective variables involved \param
/// replica (optional) Identity of the replica which creates the
/// hill
inline hill (cvm::real const &W_in,
std::vector<colvar *> &cv,
cvm::real const &hill_width,
std::string const &replica_in = "")
: sW (1.0),
W (W_in),
centers (cv.size()),
widths (cv.size()),
it (cvm::it),
replica (replica_in)
{
for (size_t i = 0; i < cv.size(); i++) {
centers[i].type (cv[i]->type());
centers[i] = cv[i]->value();
widths[i] = cv[i]->width * hill_width;
}
if (cvm::debug())
cvm::log ("New hill, applied to "+cvm::to_str (cv.size())+
" collective variables, with centers "+
cvm::to_str (centers)+", widths "+
cvm::to_str (widths)+" and weight "+
cvm::to_str (W)+".\n");
}
/// \brief General constructor: all data are explicitly passed as
/// arguments (used for instance when reading hills saved on a
/// file) \param it Time step of creation of the hill \param
/// weight Weight of the hill \param centers Center of the hill
/// \param widths Width of the hill around centers \param replica
/// (optional) Identity of the replica which creates the hill
inline hill (size_t const &it_in,
cvm::real const &W_in,
std::vector<colvarvalue> const &centers_in,
std::vector<cvm::real> const &widths_in,
std::string const &replica_in = "")
: sW (1.0),
W (W_in),
centers (centers_in),
widths (widths_in),
it (it_in),
replica (replica_in)
{}
/// Copy constructor
inline hill (colvarbias_meta::hill const &h)
: sW (1.0),
W (h.W),
centers (h.centers),
widths (h.widths),
it (h.it),
replica (h.replica)
{}
/// Destructor
inline ~hill()
{}
/// Get the energy
inline cvm::real energy()
{
return W * sW * hill_value;
}
/// Get the energy using another hill weight
inline cvm::real energy (cvm::real const &new_weight)
{
return new_weight * sW * hill_value;
}
/// Get the current hill value
inline cvm::real const &value()
{
return hill_value;
}
/// Set the hill value as specified
inline void value (cvm::real const &new_value)
{
hill_value = new_value;
}
/// Get the weight
inline cvm::real weight()
{
return W * sW;
}
/// Scale the weight with this factor (by default 1.0 is used)
inline void scale (cvm::real const &new_scale_fac)
{
sW = new_scale_fac;
}
/// Get the center of the hill
inline std::vector<colvarvalue> & center()
{
return centers;
}
/// Get the i-th component of the center
inline colvarvalue & center (size_t const &i)
{
return centers[i];
}
/// Comparison operator
inline friend bool operator < (hill const &h1, hill const &h2)
{
if (h1.it < h2.it) return true;
else return false;
}
/// Comparison operator
inline friend bool operator <= (hill const &h1, hill const &h2)
{
if (h1.it <= h2.it) return true;
else return false;
}
/// Comparison operator
inline friend bool operator > (hill const &h1, hill const &h2)
{
if (h1.it > h2.it) return true;
else return false;
}
/// Comparison operator
inline friend bool operator >= (hill const &h1, hill const &h2)
{
if (h1.it >= h2.it) return true;
else return false;
}
/// Comparison operator
inline friend bool operator == (hill const &h1, hill const &h2)
{
if ( (h1.it >= h2.it) && (h1.replica == h2.replica) ) return true;
else return false;
}
/// Represent the hill ina string suitable for a trajectory file
std::string output_traj();
/// Write the hill to an output stream
inline friend std::ostream & operator << (std::ostream &os,
hill const &h);
};
#endif
// Emacs
// Local Variables:
// mode: C++
// End:

View File

@ -0,0 +1,91 @@
#include "colvarmodule.h"
#include "colvarvalue.h"
#include "colvar.h"
#include "colvarcomp.h"
colvar::cvc::cvc()
: sup_coeff (1.0), sup_np (1),
b_periodic (false),
b_debug_gradients (false),
b_inverse_gradients (false),
b_Jacobian_derivative (false)
{}
colvar::cvc::cvc (std::string const &conf)
: sup_coeff (1.0), sup_np (1),
b_periodic (false),
b_debug_gradients (false),
b_inverse_gradients (false),
b_Jacobian_derivative (false)
{
if (cvm::debug())
cvm::log ("Initializing cvc base object.\n");
get_keyval (conf, "name", this->name, std::string (""), parse_silent);
get_keyval (conf, "componentCoeff", sup_coeff, 1.0);
get_keyval (conf, "componentExp", sup_np, 1);
get_keyval (conf, "period", period, 0.0);
get_keyval (conf, "wrapAround", wrap_center, 0.0);
get_keyval (conf, "debugGradients", b_debug_gradients, false, parse_silent);
if (cvm::debug())
cvm::log ("Done initializing cvc base object.\n");
}
void colvar::cvc::parse_group (std::string const &conf,
char const *group_key,
cvm::atom_group &group,
bool optional)
{
if (key_lookup (conf, group_key)) {
group.parse (conf, group_key);
} else {
if (! optional) {
cvm::fatal_error ("Error: definition for atom group \""+
std::string (group_key)+"\" not found.\n");
}
}
}
colvar::cvc::~cvc()
{}
void colvar::cvc::calc_force_invgrads()
{
cvm::fatal_error ("Error: calculation of inverse gradients is not implemented "
"for colvar components of type \""+function_type+"\".\n");
}
void colvar::cvc::calc_Jacobian_derivative()
{
cvm::fatal_error ("Error: calculation of inverse gradients is not implemented "
"for colvar components of type \""+function_type+"\".\n");
}
colvarvalue colvar::cvc::fdiff_change (cvm::atom_group &group)
{
colvarvalue change (x.type());
if (group.old_pos.size()) {
for (size_t i = 0; i < group.size(); i++) {
cvm::rvector const &pold = group.old_pos[i];
cvm::rvector const &p = group[i].pos;
change += group[i].grad * (p - pold);
}
}
// save for next step
group.old_pos = group.positions();
return change;
}

1413
lib/colvars/colvarcomp.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,377 @@
#include "colvarmodule.h"
#include "colvar.h"
#include "colvarcomp.h"
#include <cmath>
colvar::angle::angle (std::string const &conf)
: cvc (conf)
{
function_type = "angle";
b_inverse_gradients = true;
b_Jacobian_derivative = true;
parse_group (conf, "group1", group1);
parse_group (conf, "group2", group2);
parse_group (conf, "group3", group3);
atom_groups.push_back (&group1);
atom_groups.push_back (&group2);
atom_groups.push_back (&group3);
if (get_keyval (conf, "oneSiteSystemForce", b_1site_force, false)) {
cvm::log ("Computing system force on group 1 only");
}
x.type (colvarvalue::type_scalar);
}
colvar::angle::angle (cvm::atom const &a1,
cvm::atom const &a2,
cvm::atom const &a3)
: group1 (std::vector<cvm::atom> (1, a1)),
group2 (std::vector<cvm::atom> (1, a2)),
group3 (std::vector<cvm::atom> (1, a3))
{
function_type = "angle";
b_inverse_gradients = true;
b_Jacobian_derivative = true;
b_1site_force = false;
atom_groups.push_back (&group1);
atom_groups.push_back (&group2);
atom_groups.push_back (&group3);
x.type (colvarvalue::type_scalar);
}
colvar::angle::angle()
{
function_type = "angle";
x.type (colvarvalue::type_scalar);
}
void colvar::angle::calc_value()
{
group1.read_positions();
group2.read_positions();
group3.read_positions();
cvm::atom_pos const g1_pos = group1.center_of_mass();
cvm::atom_pos const g2_pos = group2.center_of_mass();
cvm::atom_pos const g3_pos = group3.center_of_mass();
r21 = cvm::position_distance (g2_pos, g1_pos);
r21l = r21.norm();
r23 = cvm::position_distance (g2_pos, g3_pos);
r23l = r23.norm();
cvm::real const cos_theta = (r21*r23)/(r21l*r23l);
x.real_value = (180.0/PI) * std::acos (cos_theta);
}
void colvar::angle::calc_gradients()
{
cvm::real const cos_theta = (r21*r23)/(r21l*r23l);
cvm::real const dxdcos = -1.0 / std::sqrt (1.0 - cos_theta*cos_theta);
dxdr1 = (180.0/PI) * dxdcos *
(1.0/r21l) * ( r23/r23l + (-1.0) * cos_theta * r21/r21l );
dxdr3 = (180.0/PI) * dxdcos *
(1.0/r23l) * ( r21/r21l + (-1.0) * cos_theta * r23/r23l );
for (size_t i = 0; i < group1.size(); i++) {
group1[i].grad = (group1[i].mass/group1.total_mass) *
(dxdr1);
}
for (size_t i = 0; i < group2.size(); i++) {
group2[i].grad = (group2[i].mass/group2.total_mass) *
(dxdr1 + dxdr3) * (-1.0);
}
for (size_t i = 0; i < group3.size(); i++) {
group3[i].grad = (group3[i].mass/group3.total_mass) *
(dxdr3);
}
}
void colvar::angle::calc_force_invgrads()
{
// This uses a force measurement on groups 1 and 3 only
// to keep in line with the implicit variable change used to
// evaluate the Jacobian term (essentially polar coordinates
// centered on group2, which means group2 is kept fixed
// when propagating changes in the angle)
if (b_1site_force) {
group1.read_system_forces();
cvm::real norm_fact = 1.0 / dxdr1.norm2();
ft.real_value = norm_fact * dxdr1 * group1.system_force();
} else {
group1.read_system_forces();
group3.read_system_forces();
cvm::real norm_fact = 1.0 / (dxdr1.norm2() + dxdr3.norm2());
ft.real_value = norm_fact * ( dxdr1 * group1.system_force()
+ dxdr3 * group3.system_force());
}
return;
}
void colvar::angle::calc_Jacobian_derivative()
{
// det(J) = (2 pi) r^2 * sin(theta)
// hence Jd = cot(theta)
const cvm::real theta = x.real_value * PI / 180.0;
jd = PI / 180.0 * (theta != 0.0 ? std::cos(theta) / std::sin(theta) : 0.0);
}
void colvar::angle::apply_force (colvarvalue const &force)
{
if (!group1.noforce)
group1.apply_colvar_force (force.real_value);
if (!group2.noforce)
group2.apply_colvar_force (force.real_value);
if (!group3.noforce)
group3.apply_colvar_force (force.real_value);
}
colvar::dihedral::dihedral (std::string const &conf)
: cvc (conf)
{
function_type = "dihedral";
period = 360.0;
b_periodic = true;
b_inverse_gradients = true;
b_Jacobian_derivative = true;
if (get_keyval (conf, "oneSiteSystemForce", b_1site_force, false)) {
cvm::log ("Computing system force on group 1 only");
}
parse_group (conf, "group1", group1);
parse_group (conf, "group2", group2);
parse_group (conf, "group3", group3);
parse_group (conf, "group4", group4);
atom_groups.push_back (&group1);
atom_groups.push_back (&group2);
atom_groups.push_back (&group3);
atom_groups.push_back (&group4);
x.type (colvarvalue::type_scalar);
}
colvar::dihedral::dihedral (cvm::atom const &a1,
cvm::atom const &a2,
cvm::atom const &a3,
cvm::atom const &a4)
: group1 (std::vector<cvm::atom> (1, a1)),
group2 (std::vector<cvm::atom> (1, a2)),
group3 (std::vector<cvm::atom> (1, a3)),
group4 (std::vector<cvm::atom> (1, a4))
{
if (cvm::debug())
cvm::log ("Initializing dihedral object from atom groups.\n");
function_type = "dihedral";
period = 360.0;
b_periodic = true;
b_inverse_gradients = true;
b_Jacobian_derivative = true;
b_1site_force = false;
atom_groups.push_back (&group1);
atom_groups.push_back (&group2);
atom_groups.push_back (&group3);
atom_groups.push_back (&group4);
x.type (colvarvalue::type_scalar);
if (cvm::debug())
cvm::log ("Done initializing dihedral object from atom groups.\n");
}
colvar::dihedral::dihedral()
{
function_type = "dihedral";
period = 360.0;
b_periodic = true;
b_inverse_gradients = true;
b_Jacobian_derivative = true;
x.type (colvarvalue::type_scalar);
}
void colvar::dihedral::calc_value()
{
group1.read_positions();
group2.read_positions();
group3.read_positions();
group4.read_positions();
cvm::atom_pos const g1_pos = group1.center_of_mass();
cvm::atom_pos const g2_pos = group2.center_of_mass();
cvm::atom_pos const g3_pos = group3.center_of_mass();
cvm::atom_pos const g4_pos = group4.center_of_mass();
// Usual sign convention: r12 = r2 - r1
r12 = cvm::position_distance (g1_pos, g2_pos);
r23 = cvm::position_distance (g2_pos, g3_pos);
r34 = cvm::position_distance (g3_pos, g4_pos);
cvm::rvector const n1 = cvm::rvector::outer (r12, r23);
cvm::rvector const n2 = cvm::rvector::outer (r23, r34);
cvm::real const cos_phi = n1 * n2;
cvm::real const sin_phi = n1 * r34 * r23.norm();
x.real_value = (180.0/PI) * std::atan2 (sin_phi, cos_phi);
this->wrap (x);
}
void colvar::dihedral::calc_gradients()
{
cvm::rvector A = cvm::rvector::outer (r12, r23);
cvm::real rA = A.norm();
cvm::rvector B = cvm::rvector::outer (r23, r34);
cvm::real rB = B.norm();
cvm::rvector C = cvm::rvector::outer (r23, A);
cvm::real rC = C.norm();
cvm::real const cos_phi = (A*B)/(rA*rB);
cvm::real const sin_phi = (C*B)/(rC*rB);
cvm::rvector f1, f2, f3;
rB = 1.0/rB;
B *= rB;
if (std::fabs (sin_phi) > 0.1) {
rA = 1.0/rA;
A *= rA;
cvm::rvector const dcosdA = rA*(cos_phi*A-B);
cvm::rvector const dcosdB = rB*(cos_phi*B-A);
rA = 1.0;
cvm::real const K = (1.0/sin_phi) * (180.0/PI);
f1 = K * cvm::rvector::outer (r23, dcosdA);
f3 = K * cvm::rvector::outer (dcosdB, r23);
f2 = K * (cvm::rvector::outer (dcosdA, r12)
+ cvm::rvector::outer (r34, dcosdB));
}
else {
rC = 1.0/rC;
C *= rC;
cvm::rvector const dsindC = rC*(sin_phi*C-B);
cvm::rvector const dsindB = rB*(sin_phi*B-C);
rC = 1.0;
cvm::real const K = (-1.0/cos_phi) * (180.0/PI);
f1.x = K*((r23.y*r23.y + r23.z*r23.z)*dsindC.x
- r23.x*r23.y*dsindC.y
- r23.x*r23.z*dsindC.z);
f1.y = K*((r23.z*r23.z + r23.x*r23.x)*dsindC.y
- r23.y*r23.z*dsindC.z
- r23.y*r23.x*dsindC.x);
f1.z = K*((r23.x*r23.x + r23.y*r23.y)*dsindC.z
- r23.z*r23.x*dsindC.x
- r23.z*r23.y*dsindC.y);
f3 = cvm::rvector::outer (dsindB, r23);
f3 *= K;
f2.x = K*(-(r23.y*r12.y + r23.z*r12.z)*dsindC.x
+(2.0*r23.x*r12.y - r12.x*r23.y)*dsindC.y
+(2.0*r23.x*r12.z - r12.x*r23.z)*dsindC.z
+dsindB.z*r34.y - dsindB.y*r34.z);
f2.y = K*(-(r23.z*r12.z + r23.x*r12.x)*dsindC.y
+(2.0*r23.y*r12.z - r12.y*r23.z)*dsindC.z
+(2.0*r23.y*r12.x - r12.y*r23.x)*dsindC.x
+dsindB.x*r34.z - dsindB.z*r34.x);
f2.z = K*(-(r23.x*r12.x + r23.y*r12.y)*dsindC.z
+(2.0*r23.z*r12.x - r12.z*r23.x)*dsindC.x
+(2.0*r23.z*r12.y - r12.z*r23.y)*dsindC.y
+dsindB.y*r34.x - dsindB.x*r34.y);
}
for (size_t i = 0; i < group1.size(); i++)
group1[i].grad = (group1[i].mass/group1.total_mass) * (-f1);
for (size_t i = 0; i < group2.size(); i++)
group2[i].grad = (group2[i].mass/group2.total_mass) * (-f2 + f1);
for (size_t i = 0; i < group3.size(); i++)
group3[i].grad = (group3[i].mass/group3.total_mass) * (-f3 + f2);
for (size_t i = 0; i < group4.size(); i++)
group4[i].grad = (group4[i].mass/group4.total_mass) * (f3);
}
void colvar::dihedral::calc_force_invgrads()
{
cvm::rvector const u12 = r12.unit();
cvm::rvector const u23 = r23.unit();
cvm::rvector const u34 = r34.unit();
cvm::real const d12 = r12.norm();
cvm::real const d34 = r34.norm();
cvm::rvector const cross1 = (cvm::rvector::outer (u23, u12)).unit();
cvm::rvector const cross4 = (cvm::rvector::outer (u23, u34)).unit();
cvm::real const dot1 = u23 * u12;
cvm::real const dot4 = u23 * u34;
cvm::real const fact1 = d12 * std::sqrt (1.0 - dot1 * dot1);
cvm::real const fact4 = d34 * std::sqrt (1.0 - dot4 * dot4);
group1.read_system_forces();
if ( b_1site_force ) {
// This is only measuring the force on group 1
ft.real_value = PI/180.0 * fact1 * (cross1 * group1.system_force());
} else {
// Default case: use groups 1 and 4
group4.read_system_forces();
ft.real_value = PI/180.0 * 0.5 * (fact1 * (cross1 * group1.system_force())
+ fact4 * (cross4 * group4.system_force()));
}
}
void colvar::dihedral::calc_Jacobian_derivative()
{
// With this choice of inverse gradient ("internal coordinates"), Jacobian correction is 0
jd = 0.0;
}
void colvar::dihedral::apply_force (colvarvalue const &force)
{
if (!group1.noforce)
group1.apply_colvar_force (force.real_value);
if (!group2.noforce)
group2.apply_colvar_force (force.real_value);
if (!group3.noforce)
group3.apply_colvar_force (force.real_value);
if (!group4.noforce)
group4.apply_colvar_force (force.real_value);
}

View File

@ -0,0 +1,404 @@
#include <cmath>
#include "colvarmodule.h"
#include "colvarparse.h"
#include "colvaratoms.h"
#include "colvarvalue.h"
#include "colvar.h"
#include "colvarcomp.h"
template<bool calculate_gradients>
cvm::real colvar::coordnum::switching_function (cvm::real const &r0,
int const &en,
int const &ed,
cvm::atom &A1,
cvm::atom &A2)
{
cvm::rvector const diff = cvm::position_distance (A1.pos, A2.pos);
cvm::real const l2 = diff.norm2()/(r0*r0);
// Assume en and ed are even integers, and avoid sqrt in the following
int const en2 = en/2;
int const ed2 = ed/2;
cvm::real const xn = std::pow (l2, en2);
cvm::real const xd = std::pow (l2, ed2);
cvm::real const func = (1.0-xn)/(1.0-xd);
if (calculate_gradients) {
cvm::real const dFdl2 = (1.0/(1.0-xd))*(en2*(xn/l2) - func*ed2*(xd/l2))*(-1.0);
cvm::rvector const dl2dx = (2.0/(r0*r0))*diff;
A1.grad += (-1.0)*dFdl2*dl2dx;
A2.grad += dFdl2*dl2dx;
}
return func;
}
template<bool calculate_gradients>
cvm::real colvar::coordnum::switching_function (cvm::rvector const &r0_vec,
int const &en,
int const &ed,
cvm::atom &A1,
cvm::atom &A2)
{
cvm::rvector const diff = cvm::position_distance (A1.pos, A2.pos);
cvm::rvector const scal_diff (diff.x/r0_vec.x, diff.y/r0_vec.y, diff.z/r0_vec.z);
cvm::real const l2 = scal_diff.norm2();
// Assume en and ed are even integers, and avoid sqrt in the following
int const en2 = en/2;
int const ed2 = ed/2;
cvm::real const xn = std::pow (l2, en2);
cvm::real const xd = std::pow (l2, ed2);
cvm::real const func = (1.0-xn)/(1.0-xd);
if (calculate_gradients) {
cvm::real const dFdl2 = (1.0/(1.0-xd))*(en2*(xn/l2) - func*ed2*(xd/l2))*(-1.0);
cvm::rvector const dl2dx ((2.0/(r0_vec.x*r0_vec.x))*diff.x,
(2.0/(r0_vec.y*r0_vec.y))*diff.y,
(2.0/(r0_vec.z*r0_vec.z))*diff.z);
A1.grad += (-1.0)*dFdl2*dl2dx;
A2.grad += dFdl2*dl2dx;
}
return func;
}
colvar::coordnum::coordnum (std::string const &conf)
: distance (conf), b_anisotropic (false), b_group2_center_only (false)
{
function_type = "coordnum";
x.type (colvarvalue::type_scalar);
// group1 and group2 are already initialized by distance()
// need to specify this explicitly because the distance() constructor
// has set it to true
b_inverse_gradients = false;
bool const b_scale = get_keyval (conf, "cutoff", r0,
cvm::real (4.0 * cvm::unit_angstrom()));
if (get_keyval (conf, "cutoff3", r0_vec,
cvm::rvector (4.0, 4.0, 4.0), parse_silent)) {
if (b_scale)
cvm::fatal_error ("Error: cannot specify \"scale\" and "
"\"scale3\" at the same time.\n");
b_anisotropic = true;
// remove meaningless negative signs
if (r0_vec.x < 0.0) r0_vec.x *= -1.0;
if (r0_vec.y < 0.0) r0_vec.y *= -1.0;
if (r0_vec.z < 0.0) r0_vec.z *= -1.0;
}
get_keyval (conf, "expNumer", en, int (6) );
get_keyval (conf, "expDenom", ed, int (12));
if ( (en%2) || (ed%2) ) {
cvm::fatal_error ("Error: odd exponents provided, can only use even ones.\n");
}
get_keyval (conf, "group2CenterOnly", b_group2_center_only, false);
}
colvar::coordnum::coordnum()
: b_anisotropic (false), b_group2_center_only (false)
{
function_type = "coordnum";
x.type (colvarvalue::type_scalar);
}
void colvar::coordnum::calc_value()
{
x.real_value = 0.0;
// these are necessary: for each atom, gradients are summed together
// by multiple calls to switching_function()
group1.reset_atoms_data();
group2.reset_atoms_data();
group1.read_positions();
group2.read_positions();
if (b_group2_center_only) {
// create a fake atom to hold the group2 com coordinates
cvm::atom group2_com_atom (group2[0]);
group2_com_atom.pos = group2.center_of_mass();
if (b_anisotropic) {
for (cvm::atom_iter ai1 = group1.begin(); ai1 != group1.end(); ai1++)
x.real_value += switching_function<false> (r0_vec, en, ed, *ai1, group2_com_atom);
} else {
for (cvm::atom_iter ai1 = group1.begin(); ai1 != group1.end(); ai1++)
x.real_value += switching_function<false> (r0, en, ed, *ai1, group2_com_atom);
}
} else {
if (b_anisotropic) {
for (cvm::atom_iter ai1 = group1.begin(); ai1 != group1.end(); ai1++)
for (cvm::atom_iter ai2 = group2.begin(); ai2 != group2.end(); ai2++) {
x.real_value += switching_function<false> (r0_vec, en, ed, *ai1, *ai2);
}
} else {
for (cvm::atom_iter ai1 = group1.begin(); ai1 != group1.end(); ai1++)
for (cvm::atom_iter ai2 = group2.begin(); ai2 != group2.end(); ai2++) {
x.real_value += switching_function<false> (r0, en, ed, *ai1, *ai2);
}
}
}
}
void colvar::coordnum::calc_gradients()
{
if (b_group2_center_only) {
// create a fake atom to hold the group2 com coordinates
cvm::atom group2_com_atom (group2[0]);
group2_com_atom.pos = group2.center_of_mass();
if (b_anisotropic) {
for (cvm::atom_iter ai1 = group1.begin(); ai1 != group1.end(); ai1++)
switching_function<true> (r0_vec, en, ed, *ai1, group2_com_atom);
} else {
for (cvm::atom_iter ai1 = group1.begin(); ai1 != group1.end(); ai1++)
switching_function<true> (r0, en, ed, *ai1, group2_com_atom);
}
group2.set_weighted_gradient (group2_com_atom.grad);
} else {
if (b_anisotropic) {
for (cvm::atom_iter ai1 = group1.begin(); ai1 != group1.end(); ai1++)
for (cvm::atom_iter ai2 = group2.begin(); ai2 != group2.end(); ai2++) {
switching_function<true> (r0_vec, en, ed, *ai1, *ai2);
}
} else {
for (cvm::atom_iter ai1 = group1.begin(); ai1 != group1.end(); ai1++)
for (cvm::atom_iter ai2 = group2.begin(); ai2 != group2.end(); ai2++) {
switching_function<true> (r0, en, ed, *ai1, *ai2);
}
}
}
// if (cvm::debug()) {
// for (size_t i = 0; i < group1.size(); i++) {
// cvm::log ("atom["+cvm::to_str (group1[i].id+1)+"] gradient: "+
// cvm::to_str (group1[i].grad)+"\n");
// }
// for (size_t i = 0; i < group2.size(); i++) {
// cvm::log ("atom["+cvm::to_str (group2[i].id+1)+"] gradient: "+
// cvm::to_str (group2[i].grad)+"\n");
// }
// }
}
void colvar::coordnum::apply_force (colvarvalue const &force)
{
if (!group1.noforce)
group1.apply_colvar_force (force.real_value);
if (!group2.noforce)
group2.apply_colvar_force (force.real_value);
}
// h_bond member functions
colvar::h_bond::h_bond (std::string const &conf)
: cvc (conf)
{
if (cvm::debug())
cvm::log ("Initializing h_bond object.\n");
function_type = "h_bond";
x.type (colvarvalue::type_scalar);
int a_num, d_num;
get_keyval (conf, "acceptor", a_num, -1);
get_keyval (conf, "donor", d_num, -1);
if ( (a_num == -1) || (d_num == -1) ) {
cvm::fatal_error ("Error: either acceptor or donor undefined.\n");
}
acceptor = cvm::atom (a_num);
donor = cvm::atom (d_num);
atom_groups.push_back (new cvm::atom_group);
atom_groups[0]->add_atom (acceptor);
atom_groups[0]->add_atom (donor);
get_keyval (conf, "cutoff", r0, (3.3 * cvm::unit_angstrom()));
get_keyval (conf, "expNumer", en, 6);
get_keyval (conf, "expDenom", ed, 8);
if ( (en%2) || (ed%2) ) {
cvm::fatal_error ("Error: odd exponents provided, can only use even ones.\n");
}
if (cvm::debug())
cvm::log ("Done initializing h_bond object.\n");
}
colvar::h_bond::h_bond (cvm::atom const &acceptor_i,
cvm::atom const &donor_i,
cvm::real r0_i, int en_i, int ed_i)
: acceptor (acceptor_i),
donor (donor_i),
r0 (r0_i), en (en_i), ed (ed_i)
{
function_type = "h_bond";
x.type (colvarvalue::type_scalar);
atom_groups.push_back (new cvm::atom_group);
atom_groups[0]->add_atom (acceptor);
atom_groups[0]->add_atom (donor);
}
colvar::h_bond::h_bond()
: cvc ()
{
function_type = "h_bond";
x.type (colvarvalue::type_scalar);
}
colvar::h_bond::~h_bond()
{
for (int i=0; i<atom_groups.size(); i++) {
delete atom_groups[i];
}
}
void colvar::h_bond::calc_value()
{
// this is necessary, because switching_function() will sum the new
// gradient to the current one
acceptor.reset_data();
donor.reset_data();
acceptor.read_position();
donor.read_position();
x.real_value = colvar::coordnum::switching_function<false> (r0, en, ed, acceptor, donor);
}
void colvar::h_bond::calc_gradients()
{
colvar::coordnum::switching_function<true> (r0, en, ed, acceptor, donor);
(*atom_groups[0])[0].grad = acceptor.grad;
(*atom_groups[0])[1].grad = donor.grad;
}
void colvar::h_bond::apply_force (colvarvalue const &force)
{
acceptor.apply_force (force.real_value * acceptor.grad);
donor.apply_force (force.real_value * donor.grad);
}
// Self-coordination number for a group
template<bool calculate_gradients>
cvm::real colvar::selfcoordnum::switching_function (cvm::real const &r0,
int const &en,
int const &ed,
cvm::atom &A1,
cvm::atom &A2)
{
cvm::rvector const diff = cvm::position_distance (A1.pos, A2.pos);
cvm::real const l2 = diff.norm2()/(r0*r0);
// Assume en and ed are even integers, and avoid sqrt in the following
int const en2 = en/2;
int const ed2 = ed/2;
cvm::real const xn = std::pow (l2, en2);
cvm::real const xd = std::pow (l2, ed2);
cvm::real const func = (1.0-xn)/(1.0-xd);
if (calculate_gradients) {
cvm::real const dFdl2 = (1.0/(1.0-xd))*(en2*(xn/l2) - func*ed2*(xd/l2))*(-1.0);
cvm::rvector const dl2dx = (2.0/(r0*r0))*diff;
A1.grad += (-1.0)*dFdl2*dl2dx;
A2.grad += dFdl2*dl2dx;
}
return func;
}
colvar::selfcoordnum::selfcoordnum (std::string const &conf)
: distance (conf, false)
{
function_type = "selfcoordnum";
x.type (colvarvalue::type_scalar);
// group1 is already initialized by distance()
// need to specify this explicitly because the distance() constructor
// has set it to true
b_inverse_gradients = false;
get_keyval (conf, "cutoff", r0, cvm::real (4.0 * cvm::unit_angstrom()));
get_keyval (conf, "expNumer", en, int (6) );
get_keyval (conf, "expDenom", ed, int (12));
if ( (en%2) || (ed%2) ) {
cvm::fatal_error ("Error: odd exponents provided, can only use even ones.\n");
}
}
colvar::selfcoordnum::selfcoordnum()
{
function_type = "selfcoordnum";
x.type (colvarvalue::type_scalar);
}
void colvar::selfcoordnum::calc_value()
{
x.real_value = 0.0;
// for each atom, gradients are summed by multiple calls to switching_function()
group1.reset_atoms_data();
group1.read_positions();
for (size_t i = 0; i < group1.size() - 1; i++)
for (size_t j = i + 1; j < group1.size(); j++)
x.real_value += switching_function<false> (r0, en, ed, group1[i], group1[j]);
}
void colvar::selfcoordnum::calc_gradients()
{
for (size_t i = 0; i < group1.size() - 1; i++)
for (size_t j = i + 1; j < group1.size(); j++)
switching_function<true> (r0, en, ed, group1[i], group1[j]);
}
void colvar::selfcoordnum::apply_force (colvarvalue const &force)
{
if (!group1.noforce)
group1.apply_colvar_force (force.real_value);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,385 @@
#include <cmath>
#include "colvarmodule.h"
#include "colvarvalue.h"
#include "colvarparse.h"
#include "colvar.h"
#include "colvarcomp.h"
//////////////////////////////////////////////////////////////////////
// alpha component
//////////////////////////////////////////////////////////////////////
colvar::alpha_angles::alpha_angles (std::string const &conf)
: cvc (conf)
{
if (cvm::debug())
cvm::log ("Initializing alpha_angles object.\n");
function_type = "alpha_angles";
x.type (colvarvalue::type_scalar);
std::string segment_id;
get_keyval (conf, "psfSegID", segment_id, std::string ("MAIN"));
std::vector<int> residues;
{
std::string residues_conf = "";
key_lookup (conf, "residueRange", residues_conf);
if (residues_conf.size()) {
std::istringstream is (residues_conf);
int initial, final;
char dash;
if ( (is >> initial) && (initial > 0) &&
(is >> dash) && (dash == '-') &&
(is >> final) && (final > 0) ) {
for (int rnum = initial; rnum <= final; rnum++) {
residues.push_back (rnum);
}
}
} else {
cvm::fatal_error ("Error: no residues defined in \"residueRange\".\n");
}
}
if (residues.size() < 5) {
cvm::fatal_error ("Error: not enough residues defined in \"residueRange\".\n");
}
std::string const &sid = segment_id;
std::vector<int> const &r = residues;
get_keyval (conf, "hBondCoeff", hb_coeff, 0.5);
if ( (hb_coeff < 0.0) || (hb_coeff > 1.0) ) {
cvm::fatal_error ("Error: hBondCoeff must be defined between 0 and 1.\n");
}
get_keyval (conf, "angleRef", theta_ref, 88.0);
get_keyval (conf, "angleTol", theta_tol, 15.0);
if (hb_coeff < 1.0) {
for (size_t i = 0; i < residues.size()-2; i++) {
theta.push_back (new colvar::angle (cvm::atom (r[i ], "CA", sid),
cvm::atom (r[i+1], "CA", sid),
cvm::atom (r[i+2], "CA", sid)));
}
} else {
cvm::log ("The hBondCoeff specified will disable the Calpha-Calpha-Calpha angle terms.\n");
}
{
cvm::real r0;
size_t en, ed;
get_keyval (conf, "hBondCutoff", r0, (3.3 * cvm::unit_angstrom()));
get_keyval (conf, "hBondExpNumer", en, 6);
get_keyval (conf, "hBondExpDenom", ed, 8);
if (hb_coeff > 0.0) {
for (size_t i = 0; i < residues.size()-4; i++) {
hb.push_back (new colvar::h_bond (cvm::atom (r[i ], "O", sid),
cvm::atom (r[i+4], "N", sid),
r0, en, ed));
}
} else {
cvm::log ("The hBondCoeff specified will disable the hydrogen bond terms.\n");
}
}
if (cvm::debug())
cvm::log ("Done initializing alpha_angles object.\n");
}
colvar::alpha_angles::alpha_angles()
: cvc ()
{
function_type = "alpha_angles";
x.type (colvarvalue::type_scalar);
}
colvar::alpha_angles::~alpha_angles()
{
while (theta.size() != 0) {
delete theta.back();
theta.pop_back();
}
while (hb.size() != 0) {
delete hb.back();
hb.pop_back();
}
}
void colvar::alpha_angles::calc_value()
{
x.real_value = 0.0;
if (theta.size()) {
cvm::real const theta_norm =
(1.0-hb_coeff) / cvm::real (theta.size());
for (size_t i = 0; i < theta.size(); i++) {
(theta[i])->calc_value();
cvm::real const t = ((theta[i])->value().real_value-theta_ref)/theta_tol;
cvm::real const f = ( (1.0 - std::pow (t, (int) 2)) /
(1.0 - std::pow (t, (int) 4)) );
x.real_value += theta_norm * f;
if (cvm::debug())
cvm::log ("Calpha-Calpha angle no. "+cvm::to_str (i+1)+" in \""+
this->name+"\" has a value of "+
(cvm::to_str ((theta[i])->value().real_value))+
" degrees, f = "+cvm::to_str (f)+".\n");
}
}
if (hb.size()) {
cvm::real const hb_norm =
hb_coeff / cvm::real (hb.size());
for (size_t i = 0; i < hb.size(); i++) {
(hb[i])->calc_value();
x.real_value += hb_norm * (hb[i])->value().real_value;
if (cvm::debug())
cvm::log ("Hydrogen bond no. "+cvm::to_str (i+1)+" in \""+
this->name+"\" has a value of "+
(cvm::to_str ((hb[i])->value().real_value))+".\n");
}
}
}
void colvar::alpha_angles::calc_gradients()
{
for (size_t i = 0; i < theta.size(); i++)
(theta[i])->calc_gradients();
for (size_t i = 0; i < hb.size(); i++)
(hb[i])->calc_gradients();
}
void colvar::alpha_angles::apply_force (colvarvalue const &force)
{
if (theta.size()) {
cvm::real const theta_norm =
(1.0-hb_coeff) / cvm::real (theta.size());
for (size_t i = 0; i < theta.size(); i++) {
cvm::real const t = ((theta[i])->value().real_value-theta_ref)/theta_tol;
cvm::real const f = ( (1.0 - std::pow (t, (int) 2)) /
(1.0 - std::pow (t, (int) 4)) );
cvm::real const dfdt =
1.0/(1.0 - std::pow (t, (int) 4)) *
( (-2.0 * t) + (-1.0*f)*(-4.0 * std::pow (t, (int) 3)) );
(theta[i])->apply_force (theta_norm *
dfdt * (1.0/theta_tol) *
force.real_value );
}
}
if (hb.size()) {
cvm::real const hb_norm =
hb_coeff / cvm::real (hb.size());
for (size_t i = 0; i < hb.size(); i++) {
(hb[i])->apply_force (0.5 * hb_norm * force.real_value);
}
}
}
//////////////////////////////////////////////////////////////////////
// dihedral principal component
//////////////////////////////////////////////////////////////////////
// FIXME: this will not make collect_gradients work
// because gradients in individual atom groups
// are those of the sub-cvcs (angle, hb), not those
// of this cvc (alpha)
// This is true of all cvcs with sub-cvcs, and those
// that do not calculate explicit gradients
// SO: we need a flag giving the availability of
// atomic gradients
colvar::dihedPC::dihedPC (std::string const &conf)
: cvc (conf)
{
if (cvm::debug())
cvm::log ("Initializing dihedral PC object.\n");
function_type = "dihedPC";
x.type (colvarvalue::type_scalar);
std::string segment_id;
get_keyval (conf, "psfSegID", segment_id, std::string ("MAIN"));
std::vector<int> residues;
{
std::string residues_conf = "";
key_lookup (conf, "residueRange", residues_conf);
if (residues_conf.size()) {
std::istringstream is (residues_conf);
int initial, final;
char dash;
if ( (is >> initial) && (initial > 0) &&
(is >> dash) && (dash == '-') &&
(is >> final) && (final > 0) ) {
for (int rnum = initial; rnum <= final; rnum++) {
residues.push_back (rnum);
}
}
} else {
cvm::fatal_error ("Error: no residues defined in \"residueRange\".\n");
}
}
if (residues.size() < 2) {
cvm::fatal_error ("Error: dihedralPC requires at least two residues.\n");
}
std::string const &sid = segment_id;
std::vector<int> const &r = residues;
std::string vecFileName;
int vecNumber;
if (get_keyval (conf, "vectorFile", vecFileName, vecFileName)) {
get_keyval (conf, "vectorNumber", vecNumber, 0);
if (vecNumber < 1)
cvm::fatal_error ("A positive value of vectorNumber is required.");
std::ifstream vecFile;
vecFile.open (vecFileName.c_str());
if (!vecFile.good())
cvm::fatal_error ("Error opening dihedral PCA vector file " + vecFileName + " for reading");
// TODO: adapt to different formats by setting this flag
bool eigenvectors_as_columns = true;
if (eigenvectors_as_columns) {
// Carma-style dPCA file
std::string line;
cvm::real c;
while (vecFile.good()) {
getline(vecFile, line);
if (line.length() < 2) break;
std::istringstream ls (line);
for (int i=0; i<vecNumber; i++) ls >> c;
coeffs.push_back(c);
}
}
/* TODO Uncomment this when different formats are recognized
else {
// Eigenvectors as lines
// Skip to the right line
for (int i = 1; i<vecNumber; i++)
vecFile.ignore(999999, '\n');
if (!vecFile.good())
cvm::fatal_error ("Error reading dihedral PCA vector file " + vecFileName);
std::string line;
getline(vecFile, line);
std::istringstream ls (line);
cvm::real c;
while (ls.good()) {
ls >> c;
coeffs.push_back(c);
}
}
*/
vecFile.close();
} else {
get_keyval (conf, "vector", coeffs, coeffs);
}
if ( coeffs.size() != 4 * (residues.size() - 1)) {
cvm::fatal_error ("Error: wrong number of coefficients: " +
cvm::to_str(coeffs.size()) + ". Expected " +
cvm::to_str(4 * (residues.size() - 1)) +
" (4 coeffs per residue, minus one residue).\n");
}
for (size_t i = 0; i < residues.size()-1; i++) {
// Psi
theta.push_back (new colvar::dihedral (cvm::atom (r[i ], "N", sid),
cvm::atom (r[i ], "CA", sid),
cvm::atom (r[i ], "C", sid),
cvm::atom (r[i+1], "N", sid)));
// Phi (next res)
theta.push_back (new colvar::dihedral (cvm::atom (r[i ], "C", sid),
cvm::atom (r[i+1], "N", sid),
cvm::atom (r[i+1], "CA", sid),
cvm::atom (r[i+1], "C", sid)));
}
if (cvm::debug())
cvm::log ("Done initializing dihedPC object.\n");
}
colvar::dihedPC::dihedPC()
: cvc ()
{
function_type = "dihedPC";
x.type (colvarvalue::type_scalar);
}
colvar::dihedPC::~dihedPC()
{
while (theta.size() != 0) {
delete theta.back();
theta.pop_back();
}
}
void colvar::dihedPC::calc_value()
{
x.real_value = 0.0;
for (size_t i = 0; i < theta.size(); i++) {
theta[i]->calc_value();
cvm::real const t = (PI / 180.) * theta[i]->value().real_value;
x.real_value += coeffs[2*i ] * std::cos(t)
+ coeffs[2*i+1] * std::sin(t);
}
}
void colvar::dihedPC::calc_gradients()
{
for (size_t i = 0; i < theta.size(); i++) {
theta[i]->calc_gradients();
}
}
void colvar::dihedPC::apply_force (colvarvalue const &force)
{
for (size_t i = 0; i < theta.size(); i++) {
cvm::real const t = (PI / 180.) * theta[i]->value().real_value;
cvm::real const dcosdt = - (PI / 180.) * std::sin(t);
cvm::real const dsindt = (PI / 180.) * std::cos(t);
theta[i]->apply_force((coeffs[2*i ] * dcosdt +
coeffs[2*i+1] * dsindt) * force);
}
}

View File

@ -0,0 +1,371 @@
#include <cmath>
#include "colvarmodule.h"
#include "colvarvalue.h"
#include "colvarparse.h"
#include "colvar.h"
#include "colvarcomp.h"
colvar::orientation::orientation (std::string const &conf)
: cvc (conf)
{
function_type = "orientation";
parse_group (conf, "atoms", atoms);
atom_groups.push_back (&atoms);
x.type (colvarvalue::type_quaternion);
ref_pos.reserve (atoms.size());
if (get_keyval (conf, "refPositions", ref_pos, ref_pos)) {
cvm::log ("Using reference positions from input file.\n");
if (ref_pos.size() != atoms.size()) {
cvm::fatal_error ("Error: reference positions do not "
"match the number of requested atoms.\n");
}
}
{
std::string file_name;
if (get_keyval (conf, "refPositionsFile", file_name)) {
std::string file_col;
double file_col_value;
if (get_keyval (conf, "refPositionsCol", file_col, std::string (""))) {
// use PDB flags if column is provided
bool found = get_keyval (conf, "refPositionsColValue", file_col_value, 0.0);
if (found && !file_col_value)
cvm::fatal_error ("Error: refPositionsColValue, "
"if provided, must be non-zero.\n");
} else {
// if not, use atom indices
atoms.create_sorted_ids();
}
ref_pos.resize (atoms.size());
cvm::load_coords (file_name.c_str(), ref_pos, atoms.sorted_ids, file_col, file_col_value);
}
}
if (!ref_pos.size()) {
cvm::fatal_error ("Error: must define a set of "
"reference coordinates.\n");
}
cvm::log ("Centering the reference coordinates: it is "
"assumed that each atom is the closest "
"periodic image to the center of geometry.\n");
cvm::rvector cog (0.0, 0.0, 0.0);
for (size_t i = 0; i < ref_pos.size(); i++) {
cog += ref_pos[i];
}
cog /= cvm::real (ref_pos.size());
for (size_t i = 0; i < ref_pos.size(); i++) {
ref_pos[i] -= cog;
}
get_keyval (conf, "closestToQuaternion", ref_quat, cvm::quaternion (1.0, 0.0, 0.0, 0.0));
// initialize rot member data
if (!atoms.noforce) {
rot.request_group2_gradients (atoms.size());
}
rot.b_debug_gradients = this->b_debug_gradients;
}
colvar::orientation::orientation()
: cvc ()
{
function_type = "orientation";
x.type (colvarvalue::type_quaternion);
}
void colvar::orientation::calc_value()
{
atoms.reset_atoms_data();
atoms.read_positions();
atoms_cog = atoms.center_of_geometry();
rot.calc_optimal_rotation (ref_pos, atoms.positions_shifted (-1.0 * atoms_cog));
if ((rot.q).inner (ref_quat) >= 0.0) {
x.quaternion_value = rot.q;
} else {
x.quaternion_value = -1.0 * rot.q;
}
}
void colvar::orientation::calc_gradients()
{
// gradients have already been calculated and stored within the
// member object "rot"; we're not using the "grad" member of each
// atom object, because it only can represent the gradient of a
// scalar colvar
}
void colvar::orientation::apply_force (colvarvalue const &force)
{
cvm::quaternion const &FQ = force.quaternion_value;
if (!atoms.noforce) {
for (size_t ia = 0; ia < atoms.size(); ia++) {
for (size_t i = 0; i < 4; i++) {
atoms[ia].apply_force (FQ[i] * rot.dQ0_2[ia][i]);
}
}
}
}
colvar::orientation_angle::orientation_angle (std::string const &conf)
: orientation (conf)
{
function_type = "orientation_angle";
x.type (colvarvalue::type_scalar);
}
colvar::orientation_angle::orientation_angle()
: orientation()
{
function_type = "orientation_angle";
x.type (colvarvalue::type_scalar);
}
void colvar::orientation_angle::calc_value()
{
atoms.reset_atoms_data();
atoms.read_positions();
atoms_cog = atoms.center_of_geometry();
rot.calc_optimal_rotation (ref_pos, atoms.positions_shifted (-1.0 * atoms_cog));
if ((rot.q).q0 >= 0.0) {
x.real_value = (180.0/PI) * 2.0 * std::acos ((rot.q).q0);
} else {
x.real_value = (180.0/PI) * 2.0 * std::acos (-1.0 * (rot.q).q0);
}
}
void colvar::orientation_angle::calc_gradients()
{
cvm::real const dxdq0 =
( ((rot.q).q0 * (rot.q).q0 < 1.0) ?
((180.0 / PI) * (-2.0) / std::sqrt (1.0 - ((rot.q).q0 * (rot.q).q0))) :
0.0 );
for (size_t ia = 0; ia < atoms.size(); ia++) {
atoms[ia].grad = (dxdq0 * (rot.dQ0_2[ia])[0]);
}
}
void colvar::orientation_angle::apply_force (colvarvalue const &force)
{
cvm::real const &fw = force.real_value;
if (!atoms.noforce) {
atoms.apply_colvar_force (fw);
}
}
colvar::tilt::tilt (std::string const &conf)
: orientation (conf)
{
function_type = "tilt";
get_keyval (conf, "axis", axis, cvm::rvector (0.0, 0.0, 1.0));
if (axis.norm2() != 1.0) {
axis /= axis.norm();
cvm::log ("Normalizing rotation axis to "+cvm::to_str (axis)+".\n");
}
x.type (colvarvalue::type_scalar);
}
colvar::tilt::tilt()
: orientation()
{
function_type = "tilt";
x.type (colvarvalue::type_scalar);
}
void colvar::tilt::calc_value()
{
atoms.reset_atoms_data();
atoms.read_positions();
atoms_cog = atoms.center_of_geometry();
rot.calc_optimal_rotation (ref_pos, atoms.positions_shifted (-1.0 * atoms_cog));
x.real_value = rot.cos_theta (axis);
}
void colvar::tilt::calc_gradients()
{
cvm::quaternion const dxdq = rot.dcos_theta_dq (axis);
for (size_t ia = 0; ia < atoms.size(); ia++) {
atoms[ia].grad = cvm::rvector (0.0, 0.0, 0.0);
for (size_t iq = 0; iq < 4; iq++) {
atoms[ia].grad += (dxdq[iq] * (rot.dQ0_2[ia])[iq]);
}
}
if (cvm::debug()) {
std::vector<cvm::atom_pos> pos_test (atoms.positions_shifted (-1.0 * atoms_cog));
for (size_t comp = 0; comp < 3; comp++) {
// correct this cartesian component for the "new" center of geometry
for (size_t ia = 0; ia < atoms.size(); ia++) {
pos_test[ia][comp] -=
cvm::debug_gradients_step_size / cvm::real (atoms.size());
}
for (size_t ia = 0; ia < atoms.size(); ia++) {
pos_test[ia][comp] += cvm::debug_gradients_step_size;
rot.calc_optimal_rotation (ref_pos, pos_test);
pos_test[ia][comp] -= cvm::debug_gradients_step_size;
cvm::log ("|dx(real) - dx(pred)|/dx(real) = "+
cvm::to_str (std::fabs (rot.cos_theta (axis) - x.real_value -
cvm::debug_gradients_step_size * atoms[ia].grad[comp]) /
std::fabs (rot.cos_theta (axis) - x.real_value),
cvm::cv_width, cvm::cv_prec)+".\n");
}
for (size_t ia = 0; ia < atoms.size(); ia++) {
pos_test[ia][comp] +=
cvm::debug_gradients_step_size / cvm::real (atoms.size());
}
}
}
}
void colvar::tilt::apply_force (colvarvalue const &force)
{
cvm::real const &fw = force.real_value;
if (!atoms.noforce) {
atoms.apply_colvar_force (fw);
}
}
colvar::spin_angle::spin_angle (std::string const &conf)
: orientation (conf)
{
function_type = "spin_angle";
get_keyval (conf, "axis", axis, cvm::rvector (0.0, 0.0, 1.0));
if (axis.norm2() != 1.0) {
axis /= axis.norm();
cvm::log ("Normalizing rotation axis to "+cvm::to_str (axis)+".\n");
}
period = 360.0;
b_periodic = true;
x.type (colvarvalue::type_scalar);
}
colvar::spin_angle::spin_angle()
: orientation()
{
function_type = "spin_angle";
period = 360.0;
b_periodic = true;
x.type (colvarvalue::type_scalar);
}
void colvar::spin_angle::calc_value()
{
atoms.reset_atoms_data();
atoms.read_positions();
atoms_cog = atoms.center_of_geometry();
rot.calc_optimal_rotation (ref_pos, atoms.positions_shifted (-1.0 * atoms_cog));
x.real_value = rot.spin_angle (axis);
this->wrap (x);
}
void colvar::spin_angle::calc_gradients()
{
cvm::quaternion const dxdq = rot.dspin_angle_dq (axis);
for (size_t ia = 0; ia < atoms.size(); ia++) {
atoms[ia].grad = cvm::rvector (0.0, 0.0, 0.0);
for (size_t iq = 0; iq < 4; iq++) {
atoms[ia].grad += (dxdq[iq] * (rot.dQ0_2[ia])[iq]);
}
}
if (cvm::debug()) {
std::vector<cvm::atom_pos> pos_test (atoms.positions_shifted (-1.0 * atoms_cog));
for (size_t comp = 0; comp < 3; comp++) {
// correct this cartesian component for the "new" center of geometry
for (size_t ia = 0; ia < atoms.size(); ia++) {
pos_test[ia][comp] -=
cvm::debug_gradients_step_size / cvm::real (atoms.size());
}
for (size_t ia = 0; ia < atoms.size(); ia++) {
pos_test[ia][comp] += cvm::debug_gradients_step_size;
rot.calc_optimal_rotation (ref_pos, pos_test);
pos_test[ia][comp] -= cvm::debug_gradients_step_size;
cvm::log ("|dx(real) - dx(pred)|/dx(real) = "+
cvm::to_str (std::fabs (rot.spin_angle (axis) - x.real_value -
cvm::debug_gradients_step_size * atoms[ia].grad[comp]) /
std::fabs (rot.spin_angle (axis) - x.real_value),
cvm::cv_width, cvm::cv_prec)+".\n");
}
for (size_t ia = 0; ia < atoms.size(); ia++) {
pos_test[ia][comp] +=
cvm::debug_gradients_step_size / cvm::real (atoms.size());
}
}
}
}
void colvar::spin_angle::apply_force (colvarvalue const &force)
{
cvm::real const &fw = force.real_value;
if (!atoms.noforce) {
atoms.apply_colvar_force (fw);
}
}

217
lib/colvars/colvargrid.cpp Normal file
View File

@ -0,0 +1,217 @@
// -*- c++ -*-
#include "colvarmodule.h"
#include "colvarvalue.h"
#include "colvarparse.h"
#include "colvar.h"
#include "colvarcomp.h"
#include "colvargrid.h"
colvar_grid_count::colvar_grid_count()
: colvar_grid<size_t>()
{}
colvar_grid_count::colvar_grid_count (std::vector<int> const &nx_i,
size_t const &def_count)
: colvar_grid<size_t> (nx_i, def_count)
{}
colvar_grid_count::colvar_grid_count (std::vector<colvar *> &colvars,
size_t const &def_count)
: colvar_grid<size_t> (colvars, def_count)
{}
std::istream & colvar_grid_count::read_restart (std::istream &is)
{
size_t const start_pos = is.tellg();
std::string key, conf;
if ((is >> key) && (key == std::string ("grid_parameters"))) {
is.seekg (start_pos, std::ios::beg);
is >> colvarparse::read_block ("grid_parameters", conf);
parse_params (conf);
} else {
cvm::log ("Grid parameters are missing in the restart file, using those from the configuration.\n");
is.seekg (start_pos, std::ios::beg);
}
read_raw (is);
return is;
}
std::ostream & colvar_grid_count::write_restart (std::ostream &os)
{
write_params (os);
write_raw (os);
return os;
}
colvar_grid_scalar::colvar_grid_scalar()
: colvar_grid<cvm::real>(), samples (NULL), grad (NULL)
{}
colvar_grid_scalar::colvar_grid_scalar (colvar_grid_scalar const &g)
: colvar_grid<cvm::real> (g), samples (NULL), grad (NULL)
{
grad = new cvm::real[nd];
}
colvar_grid_scalar::colvar_grid_scalar (std::vector<int> const &nx_i)
: colvar_grid<cvm::real> (nx_i, 0.0, 1), samples (NULL)
{
grad = new cvm::real[nd];
}
colvar_grid_scalar::colvar_grid_scalar (std::vector<colvar *> &colvars, bool margin)
: colvar_grid<cvm::real> (colvars, 0.0, 1, margin), samples (NULL)
{
grad = new cvm::real[nd];
}
colvar_grid_scalar::~colvar_grid_scalar()
{
if (grad) {
delete [] grad;
grad = NULL;
}
}
std::istream & colvar_grid_scalar::read_restart (std::istream &is)
{
size_t const start_pos = is.tellg();
std::string key, conf;
if ((is >> key) && (key == std::string ("grid_parameters"))) {
is.seekg (start_pos, std::ios::beg);
is >> colvarparse::read_block ("grid_parameters", conf);
parse_params (conf);
} else {
cvm::log ("Grid parameters are missing in the restart file, using those from the configuration.\n");
is.seekg (start_pos, std::ios::beg);
}
read_raw (is);
return is;
}
std::ostream & colvar_grid_scalar::write_restart (std::ostream &os)
{
write_params (os);
write_raw (os);
return os;
}
colvar_grid_gradient::colvar_grid_gradient()
: colvar_grid<cvm::real>(), samples (NULL)
{}
colvar_grid_gradient::colvar_grid_gradient (std::vector<int> const &nx_i)
: colvar_grid<cvm::real> (nx_i, 0.0, nx_i.size()), samples (NULL)
{}
colvar_grid_gradient::colvar_grid_gradient (std::vector<colvar *> &colvars)
: colvar_grid<cvm::real> (colvars, 0.0, colvars.size()), samples (NULL)
{}
std::istream & colvar_grid_gradient::read_restart (std::istream &is)
{
size_t const start_pos = is.tellg();
std::string key, conf;
if ((is >> key) && (key == std::string ("grid_parameters"))) {
is.seekg (start_pos, std::ios::beg);
is >> colvarparse::read_block ("grid_parameters", conf);
parse_params (conf);
} else {
cvm::log ("Grid parameters are missing in the restart file, using those from the configuration.\n");
is.seekg (start_pos, std::ios::beg);
}
read_raw (is);
return is;
}
std::ostream & colvar_grid_gradient::write_restart (std::ostream &os)
{
write_params (os);
write_raw (os);
return os;
}
void colvar_grid_gradient::write_1D_integral (std::ostream &os)
{
cvm::real bin, min, integral;
std::vector<cvm::real> int_vals;
os << "# xi A(xi)\n";
if ( cv.size() != 1 ) {
cvm::fatal_error ("Cannot write integral for multi-dimensional gradient grids.");
}
integral = 0.0;
int_vals.push_back ( 0.0 );
bin = 0.0;
min = 0.0;
// correction for periodic colvars, so that the PMF is periodic
cvm::real corr;
if ( periodic[0] ) {
corr = average();
} else {
corr = 0.0;
}
for (std::vector<int> ix = new_index(); index_ok (ix); incr (ix), bin += 1.0 ) {
if (samples) {
size_t const samples_here = samples->value (ix);
if (samples_here)
integral += (value (ix) / cvm::real (samples_here) - corr) * cv[0]->width;
} else {
integral += (value (ix) - corr) * cv[0]->width;
}
if ( integral < min ) min = integral;
int_vals.push_back ( integral );
}
bin = 0.0;
for ( int i = 0; i < nx[0]; i++, bin += 1.0 ) {
os << std::setw (10) << cv[0]->lower_boundary.real_value + cv[0]->width * bin << " "
<< std::setw (cvm::cv_width)
<< std::setprecision (cvm::cv_prec)
<< int_vals[i] - min << "\n";
}
os << std::setw (10) << cv[0]->lower_boundary.real_value + cv[0]->width * bin << " "
<< std::setw (cvm::cv_width)
<< std::setprecision (cvm::cv_prec)
<< int_vals[nx[0]] - min << "\n";
return;
}
// quaternion_grid::quaternion_grid (std::vector<colvar *> const &cv_i,
// std::vector<std::string> const &grid_str)
// {
// cv = cv_i;
// std::istringstream is (grid_str[0]);
// is >> grid_size;
// min.assign (3, -1.0);
// max.assign (3, 1.0);
// np.assign (3, grid_size);
// dx.assign (3, 2.0/(cvm::real (grid_size)));
// // assumes a uniform grid in the three directions; change
// // get_value() if you want to use different sizes
// cvm::log ("Allocating quaternion grid ("+cvm::to_str (np.size())+" dimensional)...");
// data.create (np, 0.0);
// cvm::log ("done.\n");
// if (cvm::debug()) cvm::log ("Grid size = "+data.size());
// }

1163
lib/colvars/colvargrid.h Normal file

File diff suppressed because it is too large Load Diff

1360
lib/colvars/colvarmodule.cpp Normal file

File diff suppressed because it is too large Load Diff

493
lib/colvars/colvarmodule.h Normal file
View File

@ -0,0 +1,493 @@
#ifndef COLVARMODULE_H
#define COLVARMODULE_H
#ifndef COLVARS_VERSION
#define COLVARS_VERSION "2012-03-23"
#endif
#ifndef COLVARS_DEBUG
#define COLVARS_DEBUG false
#endif
/// \file colvarmodule.h
/// \brief Collective variables main module
///
/// This file declares the main class for defining and manipulating
/// collective variables: there should be only one instance of this
/// class, because several variables are made static (i.e. they are
/// shared between all object instances) to be accessed from other
/// objects.
#include <iostream>
#include <iomanip>
#include <string>
#include <sstream>
#include <fstream>
#include <cmath>
#include <vector>
#include <list>
class colvarparse;
class colvar;
class colvarbias;
class colvarproxy;
/// \brief Collective variables module (main class)
///
/// Class to control the collective variables calculation. An object
/// (usually one) of this class is spawned from the MD program,
/// containing all i/o routines and general interface.
///
/// At initialization, the colvarmodule object creates a proxy object
/// to provide a transparent interface between the MD program and the
/// child objects
class colvarmodule {
private:
/// Impossible to initialize the main object without arguments
colvarmodule();
public:
friend class colvarproxy;
/// Defining an abstract real number allows to switch precision
typedef double real;
/// Residue identifier
typedef int residue_id;
class rvector;
template <class T,
size_t const length> class vector1d;
template <class T,
size_t const outer_length,
size_t const inner_length> class matrix2d;
class quaternion;
class rotation;
/// \brief Atom position (different type name from rvector, to make
/// possible future PBC-transparent implementations)
typedef rvector atom_pos;
/// \brief 3x3 matrix of real numbers
class rmatrix;
// allow these classes to access protected data
class atom;
class atom_group;
friend class atom;
friend class atom_group;
typedef std::vector<atom>::iterator atom_iter;
typedef std::vector<atom>::const_iterator atom_const_iter;
/// Current step number
static size_t it;
/// Starting step number for this run
static size_t it_restart;
/// Return the current step number from the beginning of this run
static inline size_t step_relative()
{
return it - it_restart;
}
/// Return the current step number from the beginning of the whole
/// calculation
static inline size_t step_absolute()
{
return it;
}
/// If true, get it_restart from the state file; if set to false,
/// the MD program is providing it
bool it_restart_from_state_file;
/// \brief Finite difference step size (if there is no dynamics, or
/// if gradients need to be tested independently from the size of
/// dt)
static real debug_gradients_step_size;
/// Prefix for all output files for this run
static std::string output_prefix;
/// Prefix for files from a previous run (including restart/output)
static std::string input_prefix;
/// input restart file name (determined from input_prefix)
static std::string restart_in_name;
/// Array of collective variables
static std::vector<colvar *> colvars;
/* TODO: implement named CVCs
/// Array of named (reusable) collective variable components
static std::vector<cvc *> cvcs;
/// Named cvcs register themselves at initialization time
inline void register_cvc (cvc *p) {
cvcs.push_back(p);
}
*/
/// Array of collective variable biases
static std::vector<colvarbias *> biases;
/// \brief Number of ABF biases initialized (in normal conditions
/// should be 1)
static size_t n_abf_biases;
/// \brief Number of metadynamics biases initialized (in normal
/// conditions should be 1)
static size_t n_meta_biases;
/// \brief Number of harmonic biases initialized (no limit on the
/// number)
static size_t n_harm_biases;
/// \brief Number of histograms initialized (no limit on the
/// number)
static size_t n_histo_biases;
/// \brief Whether debug output should be enabled (compile-time option)
static inline bool debug()
{
return COLVARS_DEBUG;
}
/// \brief Constructor \param config_name Configuration file name
/// \param restart_name (optional) Restart file name
colvarmodule (char const *config_name,
colvarproxy *proxy_in);
/// Destructor
~colvarmodule();
/// Initialize collective variables
void init_colvars (std::string const &conf);
/// Initialize collective variable biases
void init_biases (std::string const &conf);
/// Load new configuration - force constant and/or centers only
void change_configuration(std::string const &name, std::string const &conf);
/// Calculate change in energy from using alternate configuration
real energy_difference(std::string const &name, std::string const &conf);
/// Calculate collective variables and biases
void calc();
/// Read the input restart file
std::istream & read_restart (std::istream &is);
/// Write the output restart file
std::ostream & write_restart (std::ostream &os);
/// Write all output files (called by the proxy)
void write_output_files();
/// \brief Call colvarproxy::backup_file()
static void backup_file (char const *filename);
/// Perform analysis
void analyze();
/// \brief Read a collective variable trajectory (post-processing
/// only, not called at runtime)
bool read_traj (char const *traj_filename,
size_t traj_read_begin,
size_t traj_read_end);
/// Get the pointer of a colvar from its name (returns NULL if not found)
static colvar * colvar_p (std::string const &name);
/// Quick conversion of an object to a string
template<typename T> static std::string to_str (T const &x,
size_t const &width = 0,
size_t const &prec = 0);
/// Quick conversion of a vector of objects to a string
template<typename T> static std::string to_str (std::vector<T> const &x,
size_t const &width = 0,
size_t const &prec = 0);
/// Reduce the number of characters in a string
static inline std::string wrap_string (std::string const &s,
size_t const &nchars)
{
if (!s.size())
return std::string (nchars, ' ');
else
return ( (s.size() <= size_t (nchars)) ?
(s+std::string (nchars-s.size(), ' ')) :
(std::string (s, 0, nchars)) );
}
/// Number of characters to represent a time step
static size_t const it_width;
/// Number of digits to represent a collective variables value(s)
static size_t const cv_prec;
/// Number of characters to represent a collective variables value(s)
static size_t const cv_width;
/// Number of digits to represent the collective variables energy
static size_t const en_prec;
/// Number of characters to represent the collective variables energy
static size_t const en_width;
/// Line separator in the log output
static std::string const line_marker;
// proxy functions
/// \brief Value of the unit for atomic coordinates with respect to
/// angstroms (used by some variables for hard-coded default values)
static real unit_angstrom();
/// \brief Boltmann constant
static real boltzmann();
/// \brief Temperature of the simulation (K)
static real temperature();
/// \brief Time step of MD integrator (fs)
static real dt();
/// Request calculation of system force from MD engine
static void request_system_force();
/// Print a message to the main log
static void log (std::string const &message);
/// Print a message to the main log and exit with error code
static void fatal_error (std::string const &message);
/// Print a message to the main log and exit normally
static void exit (std::string const &message);
/// \brief Get the distance between two atomic positions with pbcs handled
/// correctly
static rvector position_distance (atom_pos const &pos1,
atom_pos const &pos2);
/// \brief Get the square distance between two positions (with
/// periodic boundary conditions handled transparently)
///
/// Note: in the case of periodic boundary conditions, this provides
/// an analytical square distance (while taking the square of
/// position_distance() would produce leads to a cusp)
static real position_dist2 (atom_pos const &pos1,
atom_pos const &pos2);
/// \brief Get the closest periodic image to a reference position
/// \param pos The position to look for the closest periodic image
/// \param ref_pos (optional) The reference position
static void select_closest_image (atom_pos &pos,
atom_pos const &ref_pos);
/// \brief Perform select_closest_image() on a set of atomic positions
///
/// After that, distance vectors can then be calculated directly,
/// without using position_distance()
static void select_closest_images (std::vector<atom_pos> &pos,
atom_pos const &ref_pos);
/// \brief Create atoms from a file \param filename name of the file
/// (usually a PDB) \param atoms array of the atoms to be allocated
/// \param pdb_field (optiona) if "filename" is a PDB file, use this
/// field to determine which are the atoms to be set
static void load_atoms (char const *filename,
std::vector<atom> &atoms,
std::string const &pdb_field,
double const pdb_field_value = 0.0);
/// \brief Load the coordinates for a group of atoms from a file
/// (usually a PDB); the number of atoms in "filename" must match
/// the number of elements in "pos"
static void load_coords (char const *filename,
std::vector<atom_pos> &pos,
const std::vector<int> &indices,
std::string const &pdb_field,
double const pdb_field_value = 0.0);
/// Frequency for collective variables trajectory output
static size_t cv_traj_freq;
/// \brief True if only analysis is performed and not a run
static bool b_analysis;
/// Frequency for saving output restarts
static size_t restart_out_freq;
/// Output restart file name
std::string restart_out_name;
/// Pseudo-random number with Gaussian distribution
static real rand_gaussian (void);
protected:
/// Configuration file
std::ifstream config_s;
/// Configuration file parser object
colvarparse *parse;
/// Name of the trajectory file
std::string cv_traj_name;
/// Collective variables output trajectory file
std::ofstream cv_traj_os;
/// Appending to the existing trajectory file?
bool cv_traj_append;
/// Output restart file
std::ofstream restart_out_os;
/// \brief Pointer to the proxy object, used to retrieve atomic data
/// from the hosting program; it is static in order to be accessible
/// from static functions in the colvarmodule class
static colvarproxy *proxy;
/// \brief Counter for the current depth in the object hierarchy (useg e.g. in outpu
static size_t depth;
public:
/// Increase the depth (number of indentations in the output)
static void increase_depth();
/// Decrease the depth (number of indentations in the output)
static void decrease_depth();
};
/// Shorthand for the frequently used type prefix
typedef colvarmodule cvm;
#include "colvartypes.h"
std::ostream & operator << (std::ostream &os, cvm::rvector const &v);
std::istream & operator >> (std::istream &is, cvm::rvector &v);
template<typename T> std::string cvm::to_str (T const &x,
size_t const &width,
size_t const &prec) {
std::ostringstream os;
if (width) os.width (width);
if (prec) {
os.setf (std::ios::scientific, std::ios::floatfield);
os.precision (prec);
}
os << x;
return os.str();
}
template<typename T> std::string cvm::to_str (std::vector<T> const &x,
size_t const &width,
size_t const &prec) {
if (!x.size()) return std::string ("");
std::ostringstream os;
if (prec) {
os.setf (std::ios::scientific, std::ios::floatfield);
}
os << "{ ";
if (width) os.width (width);
if (prec) os.precision (prec);
os << x[0];
for (size_t i = 1; i < x.size(); i++) {
os << ", ";
if (width) os.width (width);
if (prec) os.precision (prec);
os << x[i];
}
os << " }";
return os.str();
}
#include "colvarproxy.h"
inline cvm::real cvm::unit_angstrom()
{
return proxy->unit_angstrom();
}
inline cvm::real cvm::boltzmann()
{
return proxy->boltzmann();
}
inline cvm::real cvm::temperature()
{
return proxy->temperature();
}
inline cvm::real cvm::dt()
{
return proxy->dt();
}
inline void cvm::request_system_force()
{
proxy->request_system_force (true);
}
inline void cvm::select_closest_image (atom_pos &pos,
atom_pos const &ref_pos)
{
proxy->select_closest_image (pos, ref_pos);
}
inline void cvm::select_closest_images (std::vector<atom_pos> &pos,
atom_pos const &ref_pos)
{
proxy->select_closest_images (pos, ref_pos);
}
inline cvm::rvector cvm::position_distance (atom_pos const &pos1,
atom_pos const &pos2)
{
return proxy->position_distance (pos1, pos2);
}
inline cvm::real cvm::position_dist2 (cvm::atom_pos const &pos1,
cvm::atom_pos const &pos2)
{
return proxy->position_dist2 (pos1, pos2);
}
inline void cvm::load_atoms (char const *file_name,
std::vector<cvm::atom> &atoms,
std::string const &pdb_field,
double const pdb_field_value)
{
proxy->load_atoms (file_name, atoms, pdb_field, pdb_field_value);
}
inline void cvm::load_coords (char const *file_name,
std::vector<cvm::atom_pos> &pos,
const std::vector<int> &indices,
std::string const &pdb_field,
double const pdb_field_value)
{
proxy->load_coords (file_name, pos, indices, pdb_field, pdb_field_value);
}
inline void cvm::backup_file (char const *filename)
{
proxy->backup_file (filename);
}
inline cvm::real cvm::rand_gaussian (void)
{
return proxy->rand_gaussian();
}
#endif
// Emacs
// Local Variables:
// mode: C++
// End:

636
lib/colvars/colvarparse.cpp Normal file
View File

@ -0,0 +1,636 @@
#include <sstream>
#include <iostream>
#include "colvarmodule.h"
#include "colvarvalue.h"
#include "colvarparse.h"
// space & tab
std::string const colvarparse::white_space = " \t";
std::string colvarparse::dummy_string = "";
size_t colvarparse::dummy_pos = 0;
// definition of single-value keyword parsers
#define _get_keyval_scalar_string_(TYPE) \
\
bool colvarparse::get_keyval (std::string const &conf, \
char const *key, \
TYPE &value, \
TYPE const &def_value, \
Parse_Mode const parse_mode) \
{ \
std::string data; \
bool b_found = false, b_found_any = false; \
size_t save_pos = 0, found_count = 0; \
\
do { \
std::string data_this = ""; \
b_found = key_lookup (conf, key, data_this, save_pos); \
if (b_found) { \
if (!b_found_any) \
b_found_any = true; \
found_count++; \
data = data_this; \
} \
} while (b_found); \
\
if (found_count > 1) \
cvm::log ("Warning: found more than one instance of \""+ \
std::string (key)+"\".\n"); \
\
if (data.size()) { \
std::istringstream is (data); \
TYPE x (def_value); \
if (is >> x) \
value = x; \
else \
cvm::fatal_error ("Error: in parsing \""+ \
std::string (key)+"\".\n"); \
if (parse_mode != parse_silent) { \
cvm::log ("# "+std::string (key)+" = "+ \
cvm::to_str (value)+"\n"); \
} \
} else { \
\
if (b_found_any) \
cvm::fatal_error ("Error: improper or missing value " \
"for \""+std::string (key)+"\".\n"); \
value = def_value; \
if (parse_mode != parse_silent) { \
cvm::log ("# "+std::string (key)+" = \""+ \
cvm::to_str (def_value)+"\" [default]\n"); \
} \
} \
\
return b_found_any; \
}
#define _get_keyval_scalar_(TYPE) \
\
bool colvarparse::get_keyval (std::string const &conf, \
char const *key, \
TYPE &value, \
TYPE const &def_value, \
Parse_Mode const parse_mode) \
{ \
std::string data; \
bool b_found = false, b_found_any = false; \
size_t save_pos = 0, found_count = 0; \
\
do { \
std::string data_this = ""; \
b_found = key_lookup (conf, key, data_this, save_pos); \
if (b_found) { \
if (!b_found_any) \
b_found_any = true; \
found_count++; \
data = data_this; \
} \
} while (b_found); \
\
if (found_count > 1) \
cvm::log ("Warning: found more than one instance of \""+ \
std::string (key)+"\".\n"); \
\
if (data.size()) { \
std::istringstream is (data); \
TYPE x (def_value); \
if (is >> x) \
value = x; \
else \
cvm::fatal_error ("Error: in parsing \""+ \
std::string (key)+"\".\n"); \
if (parse_mode != parse_silent) { \
cvm::log ("# "+std::string (key)+" = "+ \
cvm::to_str (value)+"\n"); \
} \
} else { \
\
if (b_found_any) \
cvm::fatal_error ("Error: improper or missing value " \
"for \""+std::string (key)+"\".\n"); \
value = def_value; \
if (parse_mode != parse_silent) { \
cvm::log ("# "+std::string (key)+" = "+ \
cvm::to_str (def_value)+" [default]\n"); \
} \
} \
\
return b_found_any; \
}
// definition of multiple-value keyword parsers
#define _get_keyval_vector_(TYPE) \
\
bool colvarparse::get_keyval (std::string const &conf, \
char const *key, \
std::vector<TYPE> &values, \
std::vector<TYPE> const &def_values, \
Parse_Mode const parse_mode) \
{ \
std::string data; \
bool b_found = false, b_found_any = false; \
size_t save_pos = 0, found_count = 0; \
\
do { \
std::string data_this = ""; \
b_found = key_lookup (conf, key, data_this, save_pos); \
if (b_found) { \
if (!b_found_any) \
b_found_any = true; \
found_count++; \
data = data_this; \
} \
} while (b_found); \
\
if (found_count > 1) \
cvm::log ("Warning: found more than one instance of \""+ \
std::string (key)+"\".\n"); \
\
if (data.size()) { \
std::istringstream is (data); \
\
if (values.size() == 0) { \
\
std::vector<TYPE> x; \
if (def_values.size()) \
x = def_values; \
else \
x.assign (1, TYPE()); \
\
for (size_t i = 0; \
( is >> x[ ((i<x.size()) ? i : x.size()-1) ] ); \
i++) { \
values.push_back (x[ ((i<x.size()) ? i : x.size()-1) ]); \
} \
\
} else { \
\
size_t i = 0; \
for ( ; i < values.size(); i++) { \
TYPE x (values[i]); \
if (is >> x) \
values[i] = x; \
else \
cvm::fatal_error ("Error: in parsing \""+ \
std::string (key)+"\".\n"); \
} \
} \
\
if (parse_mode != parse_silent) { \
cvm::log ("# "+std::string (key)+" = "+ \
cvm::to_str (values)+"\n"); \
} \
\
} else { \
\
if (b_found_any) \
cvm::fatal_error ("Error: improper or missing values for \""+ \
std::string (key)+"\".\n"); \
\
for (size_t i = 0; i < values.size(); i++) \
values[i] = def_values[ (i > def_values.size()) ? 0 : i ]; \
\
if (parse_mode != parse_silent) { \
cvm::log ("# "+std::string (key)+" = "+ \
cvm::to_str (def_values)+" [default]\n"); \
} \
} \
\
return b_found_any; \
}
// single-value keyword parsers
_get_keyval_scalar_ (int);
_get_keyval_scalar_ (size_t);
_get_keyval_scalar_string_ (std::string);
_get_keyval_scalar_ (cvm::real);
_get_keyval_scalar_ (cvm::rvector);
_get_keyval_scalar_ (cvm::quaternion);
_get_keyval_scalar_ (colvarvalue);
// multiple-value keyword parsers
_get_keyval_vector_ (int);
_get_keyval_vector_ (size_t);
_get_keyval_vector_ (std::string);
_get_keyval_vector_ (cvm::real);
_get_keyval_vector_ (cvm::rvector);
_get_keyval_vector_ (cvm::quaternion);
_get_keyval_vector_ (colvarvalue);
bool colvarparse::get_keyval (std::string const &conf,
char const *key,
bool &value,
bool const &def_value,
Parse_Mode const parse_mode)
{
std::string data;
bool b_found = false, b_found_any = false;
size_t save_pos = 0, found_count = 0;
do {
std::string data_this = "";
b_found = key_lookup (conf, key, data_this, save_pos);
if (b_found) {
if (!b_found_any)
b_found_any = true;
found_count++;
data = data_this;
}
} while (b_found);
if (found_count > 1)
cvm::log ("Warning: found more than one instance of \""+
std::string (key)+"\".\n");
if (data.size()) {
std::istringstream is (data);
if ( (data == std::string ("on")) ||
(data == std::string ("yes")) ||
(data == std::string ("true")) ) {
value = true;
} else if ( (data == std::string ("off")) ||
(data == std::string ("no")) ||
(data == std::string ("false")) ) {
value = false;
} else
cvm::fatal_error ("Error: boolean values only are allowed "
"for \""+std::string (key)+"\".\n");
if (parse_mode != parse_silent) {
cvm::log ("# "+std::string (key)+" = "+
(value ? "on" : "off")+"\n");
}
} else {
if (b_found_any) {
if (parse_mode != parse_silent) {
cvm::log ("# "+std::string (key)+" = on\n");
}
value = true;
} else {
value = def_value;
if (parse_mode != parse_silent) {
cvm::log ("# "+std::string (key)+" = "+
(def_value ? "on" : "off")+" [default]\n");
}
}
}
return b_found_any;
}
void colvarparse::add_keyword (char const *key)
{
for (std::list<std::string>::iterator ki = allowed_keywords.begin();
ki != allowed_keywords.end(); ki++) {
if (to_lower_cppstr (std::string (key)) == *ki)
return;
}
// not found in the list
// if (cvm::debug())
// cvm::log ("Registering a new keyword, \""+std::string (key)+"\".\n");
allowed_keywords.push_back (to_lower_cppstr (std::string (key)));
}
void colvarparse::strip_values (std::string &conf)
{
size_t offset = 0;
data_begin_pos.sort();
data_end_pos.sort();
std::list<size_t>::iterator data_begin = data_begin_pos.begin();
std::list<size_t>::iterator data_end = data_end_pos.begin();
for ( ; (data_begin != data_begin_pos.end()) &&
(data_end != data_end_pos.end()) ;
data_begin++, data_end++) {
// std::cerr << "data_begin, data_end "
// << *data_begin << ", " << *data_end
// << "\n";
size_t const nchars = *data_end-*data_begin;
// std::cerr << "conf[data_begin:data_end] = \""
// << std::string (conf, *data_begin - offset, nchars)
// << "\"\n";
conf.erase (*data_begin - offset, nchars);
offset += nchars;
// std::cerr << ("Stripped config = \"\n"+conf+"\"\n");
}
}
void colvarparse::check_keywords (std::string &conf, char const *key)
{
if (cvm::debug())
cvm::log ("Configuration string for \""+std::string (key)+
"\": \"\n"+conf+"\".\n");
strip_values (conf);
// after stripping, the config string has either empty lines, or
// lines beginning with a keyword
std::string line;
std::istringstream is (conf);
while (std::getline (is, line)) {
if (line.size() == 0)
continue;
if (line.find_first_not_of (white_space) ==
std::string::npos)
continue;
std::string uk;
std::istringstream line_is (line);
line_is >> uk;
if (cvm::debug())
cvm::log ("Checking the validity of \""+uk+"\" from line:\n" + line);
uk = to_lower_cppstr (uk);
bool found_keyword = false;
for (std::list<std::string>::iterator ki = allowed_keywords.begin();
ki != allowed_keywords.end(); ki++) {
if (uk == *ki) {
found_keyword = true;
break;
}
}
if (!found_keyword)
cvm::fatal_error ("Error: keyword \""+uk+"\" is not supported, "
"or not recognized in this context.\n");
}
}
std::istream & colvarparse::getline_nocomments (std::istream &is,
std::string &line,
char const delim)
{
std::getline (is, line, delim);
size_t const comment = line.find ('#');
if (comment != std::string::npos) {
line.erase (comment);
}
return is;
}
bool colvarparse::key_lookup (std::string const &conf,
char const *key_in,
std::string &data,
size_t &save_pos)
{
// add this keyword to the register (in its camelCase version)
add_keyword (key_in);
// use the lowercase version from now on
std::string const key (to_lower_cppstr (key_in));
// "conf_lower" is only used to lookup the keyword, but its value
// will be read from "conf", in order not to mess up file names
std::string const conf_lower (to_lower_cppstr (conf));
// by default, there is no value, unless we found one
data = "";
// when the function is invoked without save_pos, ensure that we
// start from zero
colvarparse::dummy_pos = 0;
// start from the first occurrence of key
size_t pos = conf_lower.find (key, save_pos);
// iterate over all instances until it finds the isolated keyword
while (true) {
if (pos == std::string::npos) {
// no valid instance of the keyword has been found
return false;
}
bool b_isolated_left = true, b_isolated_right = true;
if (pos > 0) {
if ( std::string ("\n"+white_space+
"}").find (conf[pos-1]) ==
std::string::npos ) {
// none of the valid delimiting characters is on the left of key
b_isolated_left = false;
}
}
if (pos < conf.size()-key.size()-1) {
if ( std::string ("\n"+white_space+
"{").find (conf[pos+key.size()]) ==
std::string::npos ) {
// none of the valid delimiting characters is on the right of key
b_isolated_right = false;
}
}
// check that there are matching braces between here and the end of conf
bool const b_not_within_block = brace_check (conf, pos);
bool const b_isolated = (b_isolated_left && b_isolated_right &&
b_not_within_block);
if (b_isolated) {
// found it
break;
} else {
// try the next occurrence of key
pos = conf_lower.find (key, pos+key.size());
}
}
// check it is not between quotes
// if ( (conf.find_last_of ("\"",
// conf.find_last_of (white_space, pos)) !=
// std::string::npos) &&
// (conf.find_first_of ("\"",
// conf.find_first_of (white_space, pos)) !=
// std::string::npos) )
// return false;
// save the pointer for a future call (when iterating over multiple
// valid instances of the same keyword)
save_pos = pos + key.size();
// get the remainder of the line
size_t pl = conf.rfind ("\n", pos);
size_t line_begin = (pl == std::string::npos) ? 0 : pos;
size_t nl = conf.find ("\n", pos);
size_t line_end = (nl == std::string::npos) ? conf.size() : nl;
std::string line (conf, line_begin, (line_end-line_begin));
size_t data_begin = (to_lower_cppstr (line)).find (key) + key.size();
data_begin = line.find_first_not_of (white_space, data_begin+1);
// size_t data_begin_absolute = data_begin + line_begin;
// size_t data_end_absolute = data_begin;
if (data_begin != std::string::npos) {
size_t data_end = line.find_last_not_of (white_space) + 1;
data_end = (data_end == std::string::npos) ? line.size() : data_end;
// data_end_absolute = data_end + line_begin;
if (line.find ('{', data_begin) != std::string::npos) {
size_t brace_count = 1;
size_t brace = line.find ('{', data_begin); // start from the first opening brace
while (brace_count > 0) {
// find the matching closing brace
brace = line.find_first_of ("{}", brace+1);
while (brace == std::string::npos) {
// add a new line
if (line_end >= conf.size()) {
cvm::fatal_error ("Parse error: reached the end while "
"looking for closing brace; until now "
"the following was parsed: \"\n"+
line+"\".\n");
return false;
}
size_t const old_end = line.size();
// data_end_absolute += old_end+1;
line_begin = line_end;
nl = conf.find ('\n', line_begin+1);
if (nl == std::string::npos)
line_end = conf.size();
else
line_end = nl;
line.append (conf, line_begin, (line_end-line_begin));
brace = line.find_first_of ("{}", old_end);
}
if (line[brace] == '{') brace_count++;
if (line[brace] == '}') brace_count--;
}
// set data_begin afterthe opening brace
data_begin = line.find_first_of ('{', data_begin) + 1;
data_begin = line.find_first_not_of (white_space,
data_begin);
// set data_end before the closing brace
data_end = brace;
data_end = line.find_last_not_of (white_space+"}",
data_end) + 1;
// data_end_absolute = line_end;
if (data_end > line.size())
data_end = line.size();
}
data.append (line, data_begin, (data_end-data_begin));
if (data.size() && save_delimiters) {
data_begin_pos.push_back (conf.find (data, pos+key.size()));
data_end_pos.push_back (data_begin_pos.back()+data.size());
// std::cerr << "key = " << key << ", data = \""
// << data << "\", data_begin, data_end = "
// << data_begin_pos.back() << ", " << data_end_pos.back()
// << "\n";
}
}
save_pos = line_end;
return true;
}
std::istream & operator>> (std::istream &is, colvarparse::read_block const &rb)
{
size_t start_pos = is.tellg();
std::string read_key, next;
if ( !(is >> read_key) || !(read_key == rb.key) ||
!(is >> next) ) {
// the requested keyword has not been found, or it is not possible
// to read data after it
is.clear();
is.seekg (start_pos, std::ios::beg);
is.setstate (std::ios::failbit);
return is;
}
if (next != "{") {
(*rb.data) = next;
return is;
}
size_t brace_count = 1;
std::string line;
while (colvarparse::getline_nocomments (is, line)) {
size_t br = 0, br_old;
while ( (br = line.find_first_of ("{}", br)) != std::string::npos) {
if (line[br] == '{') brace_count++;
if (line[br] == '}') brace_count--;
br_old = br;
br++;
}
if (brace_count) (*rb.data).append (line + "\n");
else {
(*rb.data).append (line, 0, br_old);
break;
}
}
if (brace_count) {
// end-of-file reached
// restore initial position
is.clear();
is.seekg (start_pos, std::ios::beg);
is.setstate (std::ios::failbit);
}
return is;
}
bool colvarparse::brace_check (std::string const &conf,
size_t const start_pos)
{
size_t brace_count = 0;
size_t brace = start_pos;
while ( (brace = conf.find_first_of ("{}", brace)) != std::string::npos) {
if (conf[brace] == '{') brace_count++;
if (conf[brace] == '}') brace_count--;
brace++;
}
if (brace_count != 0)
return false;
else
return true;
}
// Emacs
// Local Variables:
// mode: C++
// End:

201
lib/colvars/colvarparse.h Normal file
View File

@ -0,0 +1,201 @@
#ifndef COLVARPARSE_H
#define COLVARPARSE_H
#include <cstring>
#include <string>
#include "colvarmodule.h"
#include "colvarvalue.h"
/// \file colvarparse.h Parsing functions for collective variables
/// \brief Base class containing parsing functions; all objects which
/// need to parse input inherit from this
class colvarparse {
protected:
/// \brief List of legal keywords for this object: this is updated
/// by each call to colvarparse::get_keyval() or
/// colvarparse::key_lookup()
std::list<std::string> allowed_keywords;
/// \brief List of delimiters for the values of each keyword in the
/// configuration string; all keywords will be stripped of their
/// values before the keyword check is performed
std::list<size_t> data_begin_pos;
/// \brief List of delimiters for the values of each keyword in the
/// configuration string; all keywords will be stripped of their
/// values before the keyword check is performed
std::list<size_t> data_end_pos;
/// \brief Whether or not to accumulate data_begin_pos and
/// data_end_pos in key_lookup(); it may be useful to disable
/// this after the constructor is called, because other files may be
/// read (e.g. restart) that would mess up with the registry; in any
/// case, nothing serious happens until check_keywords() is invoked
/// (which should happen only right after construction)
bool save_delimiters;
/// \brief Add a new valid keyword to the list
void add_keyword (char const *key);
/// \brief Remove all the values from the config string
void strip_values (std::string &conf);
public:
inline colvarparse()
: save_delimiters (true)
{}
/// How a keyword is parsed in a string
enum Parse_Mode {
/// \brief (default) Read the first instance of a keyword (if
/// any), report its value, and print a warning when there is more
/// than one
parse_normal,
/// \brief Like parse_normal, but don't send any message to the log
/// (useful e.g. in restart files when such messages are very
/// numerous and redundant)
parse_silent
};
/// \fn get_keyval bool const get_keyval (std::string const &conf,
/// char const *key, _type_ &value, _type_ const &def_value,
/// Parse_Mode const parse_mode) \brief Helper function to parse
/// keywords in the configuration and get their values
///
/// In normal circumstances, you should use either version the
/// get_keyval function. Both of them look for the C string "key"
/// in the C++ string "conf", and assign the corresponding value (if
/// available) to the variable "value" (first version), or assign as
/// many values as found to the vector "values" (second version).
///
/// If "key" is found but no value is associated to it, the default
/// value is provided (either one copy or as many copies as the
/// current length of the vector "values" specifies). A message
/// will print, unless parse_mode is equal to parse_silent. The
/// return value of both forms of get_keyval is true if "key" is
/// found (with or without value), and false when "key" is absent in
/// the string "conf". If there is more than one instance of the
/// keyword, a warning will be raised; instead, to loop over
/// multiple instances key_lookup() should be invoked directly.
///
/// If you introduce a new data type, add two new instances of this
/// functions, or insert this type in the \link colvarvalue \endlink
/// wrapper class (colvarvalue.h).
#define _get_keyval_scalar_proto_(_type_,_def_value_) \
bool get_keyval (std::string const &conf, \
char const *key, \
_type_ &value, \
_type_ const &def_value = _def_value_, \
Parse_Mode const parse_mode = parse_normal)
_get_keyval_scalar_proto_ (int, (int)0);
_get_keyval_scalar_proto_ (size_t, (size_t)0);
_get_keyval_scalar_proto_ (std::string, std::string (""));
_get_keyval_scalar_proto_ (cvm::real, (cvm::real)0.0);
_get_keyval_scalar_proto_ (cvm::rvector, cvm::rvector());
_get_keyval_scalar_proto_ (cvm::quaternion, cvm::quaternion());
_get_keyval_scalar_proto_ (colvarvalue, colvarvalue (colvarvalue::type_notset));
_get_keyval_scalar_proto_ (bool, false);
#define _get_keyval_vector_proto_(_type_,_def_value_) \
bool get_keyval (std::string const &conf, \
char const *key, \
std::vector<_type_> &values, \
std::vector<_type_> const &def_values = \
std::vector<_type_> (0, static_cast<_type_>(_def_value_)), \
Parse_Mode const parse_mode = parse_normal)
_get_keyval_vector_proto_ (int, 0);
_get_keyval_vector_proto_ (size_t, 0);
_get_keyval_vector_proto_ (std::string, std::string (""));
_get_keyval_vector_proto_ (cvm::real, 0.0);
_get_keyval_vector_proto_ (cvm::rvector, cvm::rvector());
_get_keyval_vector_proto_ (cvm::quaternion, cvm::quaternion());
_get_keyval_vector_proto_ (colvarvalue, colvarvalue (colvarvalue::type_notset));
/// \brief Check that all the keywords within "conf" are in the list
/// of allowed keywords; this will invoke strip_values() first and
/// then loop over all words
void check_keywords (std::string &conf, char const *key);
/// \brief Return a lowercased copy of the string
static inline std::string to_lower_cppstr (std::string const &in)
{
std::string out = "";
for (size_t i = 0; i < in.size(); i++) {
out.append (1, (char) ::tolower (in[i]) );
}
return out;
}
/// \brief Helper class to read a block of the type "key { ... }"
/// from a stream and store it in a string
///
/// Useful on restarts, where the file is too big to be loaded in a
/// string by key_lookup; it can only check that the keyword is
/// correct and the block is properly delimited by braces, not
/// skipping other blocks
class read_block {
std::string const key;
std::string * const data;
public:
inline read_block (std::string const &key_in, std::string &data_in)
: key (key_in), data (&data_in)
{}
inline ~read_block() {}
friend std::istream & operator >> (std::istream &is, read_block const &rb);
};
/// Accepted white space delimiters, used in key_lookup()
static std::string const white_space;
/// \brief Low-level function for parsing configuration strings;
/// automatically adds the requested keywords to the list of valid
/// ones. \param conf the content of the configuration file or one
/// of its blocks \param key the keyword to search in "conf" \param
/// data (optional) holds the string provided after "key", if any
/// \param save_pos (optional) stores the position of the keyword
/// within "conf", useful when doing multiple calls \param
/// save_delimiters (optional)
bool key_lookup (std::string const &conf,
char const *key,
std::string &data = dummy_string,
size_t &save_pos = dummy_pos);
/// Used as a default argument by key_lookup
static std::string dummy_string;
/// Used as a default argument by key_lookup
static size_t dummy_pos;
/// \brief Works as std::getline() but also removes everything
/// between a comment character and the following newline
static std::istream & getline_nocomments (std::istream &is,
std::string &s,
char const delim = '\n');
/// Check if the content of the file has matching braces
bool brace_check (std::string const &conf,
size_t const start_pos = 0);
};
#endif
// Emacs
// Local Variables:
// mode: C++
// End:

137
lib/colvars/colvarproxy.h Normal file
View File

@ -0,0 +1,137 @@
#ifndef COLVARPROXY_H
#define COLVARPROXY_H
#include "colvarmodule.h"
/// \brief Interface class between the collective variables module and
/// the simulation program
class colvarproxy {
public:
/// Pointer to the instance of colvarmodule
colvarmodule *colvars;
/// \brief Value of the unit for atomic coordinates with respect to
/// angstroms (used by some variables for hard-coded default values)
virtual cvm::real unit_angstrom() = 0;
/// \brief Boltzmann constant
virtual cvm::real boltzmann() = 0;
/// \brief Temperature of the simulation (K)
virtual cvm::real temperature() = 0;
/// \brief Time step of the simulation (fs)
virtual cvm::real dt() = 0;
/// Pass restraint energy value for current timestep to MD engine
virtual void add_energy (cvm::real energy) = 0;
/// Tell the proxy whether system forces are needed
virtual void request_system_force (bool yesno) = 0;
/// Print a message to the main log
virtual void log (std::string const &message) = 0;
/// Print a message to the main log and exit with error code
virtual void fatal_error (std::string const &message) = 0;
/// Print a message to the main log and exit normally
virtual void exit (std::string const &message) = 0;
/// \brief Prefix to be used for input files (restarts, not
/// configuration)
virtual std::string input_prefix() = 0;
/// \brief Prefix to be used for output restart files
virtual std::string restart_output_prefix() = 0;
/// \brief Prefix to be used for output files (final system
/// configuration)
virtual std::string output_prefix() = 0;
/// \brief Restarts will be fritten each time this number of steps has passed
virtual size_t restart_frequency() = 0;
// **************** PERIODIC BOUNDARY CONDITIONS ****************
/// \brief Get the simple distance vector between two positions
/// (with periodic boundary conditions handled transparently)
virtual cvm::rvector position_distance (cvm::atom_pos const &pos1,
cvm::atom_pos const &pos2) = 0;
/// \brief Get the square distance between two positions (with
/// periodic boundary conditions handled transparently)
///
/// Note: in the case of periodic boundary conditions, this provides
/// an analytical square distance (while taking the square of
/// position_distance() would produce leads to a cusp)
virtual cvm::real position_dist2 (cvm::atom_pos const &pos1,
cvm::atom_pos const &pos2) = 0;
/// \brief Get the closest periodic image to a reference position
/// \param pos The position to look for the closest periodic image
/// \param ref_pos The reference position
virtual void select_closest_image (cvm::atom_pos &pos,
cvm::atom_pos const &ref_pos) = 0;
/// \brief Perform select_closest_image() on a set of atomic positions
///
/// After that, distance vectors can then be calculated directly,
/// without using position_distance()
void select_closest_images (std::vector<cvm::atom_pos> &pos,
cvm::atom_pos const &ref_pos);
/// \brief Read atom identifiers from a file \param filename name of
/// the file (usually a PDB) \param atoms array to which atoms read
/// from "filename" will be appended \param pdb_field (optiona) if
/// "filename" is a PDB file, use this field to determine which are
/// the atoms to be set
virtual void load_atoms (char const *filename,
std::vector<cvm::atom> &atoms,
std::string const pdb_field,
double const pdb_field_value = 0.0) = 0;
/// \brief Load the coordinates for a group of atoms from a file
/// (usually a PDB); if "pos" is already allocated, the number of its
/// elements must match the number of atoms in "filename"
virtual void load_coords (char const *filename,
std::vector<cvm::atom_pos> &pos,
const std::vector<int> &indices,
std::string const pdb_field,
double const pdb_field_value = 0.0) = 0;
/// \brief Rename the given file, under the convention provided by
/// the MD program
virtual void backup_file (char const *filename) = 0;
/// \brief Pseudo-random number with Gaussian distribution
virtual cvm::real rand_gaussian (void) = 0;
virtual inline ~colvarproxy() {}
};
inline void colvarproxy::select_closest_images (std::vector<cvm::atom_pos> &pos,
cvm::atom_pos const &ref_pos)
{
for (std::vector<cvm::atom_pos>::iterator pi = pos.begin();
pi != pos.end(); pi++) {
select_closest_image (*pi, ref_pos);
}
}
#endif
// Emacs
// Local Variables:
// mode: C++
// End:

1110
lib/colvars/colvartypes.h Normal file

File diff suppressed because it is too large Load Diff

261
lib/colvars/colvarvalue.cpp Normal file
View File

@ -0,0 +1,261 @@
#include <vector>
#include "colvarmodule.h"
#include "colvarvalue.h"
std::string const colvarvalue::type_desc[colvarvalue::type_quaternion+1] =
{ "not_set",
"scalar number",
"3-dimensional vector",
"3-dimensional unit vector",
"4-dimensional unit vector" };
size_t const colvarvalue::dof_num[ colvarvalue::type_quaternion+1] =
{ 0, 1, 3, 2, 3 };
void colvarvalue::undef_op() const
{
cvm::fatal_error ("Error: Undefined operation on a colvar of type \""+
colvarvalue::type_desc[this->value_type]+"\".\n");
}
void colvarvalue::error_rside
(colvarvalue::Type const &vt) const
{
cvm::fatal_error ("Trying to assign a colvar value with type \""+
type_desc[this->value_type]+"\" to one with type \""+
type_desc[vt]+"\".\n");
}
void colvarvalue::error_lside
(colvarvalue::Type const &vt) const
{
cvm::fatal_error ("Trying to use a colvar value with type \""+
type_desc[vt]+"\" as one of type \""+
type_desc[this->value_type]+"\".\n");
}
void colvarvalue::inner_opt (colvarvalue const &x,
std::vector<colvarvalue>::iterator &xv,
std::vector<colvarvalue>::iterator const &xv_end,
std::vector<cvm::real>::iterator &inner)
{
// doing type check only once, here
colvarvalue::check_types (x, *xv);
std::vector<colvarvalue>::iterator &xvi = xv;
std::vector<cvm::real>::iterator &ii = inner;
switch (x.value_type) {
case colvarvalue::type_scalar:
while (xvi != xv_end) {
*(ii++) += (xvi++)->real_value * x.real_value;
}
break;
case colvarvalue::type_vector:
case colvarvalue::type_unitvector:
while (xvi != xv_end) {
*(ii++) += (xvi++)->rvector_value * x.rvector_value;
}
break;
case colvarvalue::type_quaternion:
while (xvi != xv_end) {
*(ii++) += ((xvi++)->quaternion_value).cosine (x.quaternion_value);
}
break;
default:
x.undef_op();
};
}
void colvarvalue::inner_opt (colvarvalue const &x,
std::list<colvarvalue>::iterator &xv,
std::list<colvarvalue>::iterator const &xv_end,
std::vector<cvm::real>::iterator &inner)
{
// doing type check only once, here
colvarvalue::check_types (x, *xv);
std::list<colvarvalue>::iterator &xvi = xv;
std::vector<cvm::real>::iterator &ii = inner;
switch (x.value_type) {
case colvarvalue::type_scalar:
while (xvi != xv_end) {
*(ii++) += (xvi++)->real_value * x.real_value;
}
break;
case colvarvalue::type_vector:
case colvarvalue::type_unitvector:
while (xvi != xv_end) {
*(ii++) += (xvi++)->rvector_value * x.rvector_value;
}
break;
case colvarvalue::type_quaternion:
while (xvi != xv_end) {
*(ii++) += ((xvi++)->quaternion_value).cosine (x.quaternion_value);
}
break;
default:
x.undef_op();
};
}
void colvarvalue::p2leg_opt (colvarvalue const &x,
std::vector<colvarvalue>::iterator &xv,
std::vector<colvarvalue>::iterator const &xv_end,
std::vector<cvm::real>::iterator &inner)
{
// doing type check only once, here
colvarvalue::check_types (x, *xv);
std::vector<colvarvalue>::iterator &xvi = xv;
std::vector<cvm::real>::iterator &ii = inner;
switch (x.value_type) {
case colvarvalue::type_scalar:
cvm::fatal_error ("Error: cannot calculate Legendre polynomials "
"for scalar variables.\n");
break;
case colvarvalue::type_vector:
while (xvi != xv_end) {
cvm::real const cosine =
((xvi)->rvector_value * x.rvector_value) /
((xvi)->rvector_value.norm() * x.rvector_value.norm());
xvi++;
*(ii++) += 1.5*cosine*cosine - 0.5;
}
break;
case colvarvalue::type_unitvector:
while (xvi != xv_end) {
cvm::real const cosine = (xvi++)->rvector_value * x.rvector_value;
*(ii++) += 1.5*cosine*cosine - 0.5;
}
break;
case colvarvalue::type_quaternion:
while (xvi != xv_end) {
cvm::real const cosine = (xvi++)->quaternion_value.cosine (x.quaternion_value);
*(ii++) += 1.5*cosine*cosine - 0.5;
}
break;
default:
x.undef_op();
};
}
void colvarvalue::p2leg_opt (colvarvalue const &x,
std::list<colvarvalue>::iterator &xv,
std::list<colvarvalue>::iterator const &xv_end,
std::vector<cvm::real>::iterator &inner)
{
// doing type check only once, here
colvarvalue::check_types (x, *xv);
std::list<colvarvalue>::iterator &xvi = xv;
std::vector<cvm::real>::iterator &ii = inner;
switch (x.value_type) {
case colvarvalue::type_scalar:
cvm::fatal_error ("Error: cannot calculate Legendre polynomials "
"for scalar variables.\n");
break;
case colvarvalue::type_vector:
while (xvi != xv_end) {
cvm::real const cosine =
((xvi)->rvector_value * x.rvector_value) /
((xvi)->rvector_value.norm() * x.rvector_value.norm());
xvi++;
*(ii++) += 1.5*cosine*cosine - 0.5;
}
break;
case colvarvalue::type_unitvector:
while (xvi != xv_end) {
cvm::real const cosine = (xvi++)->rvector_value * x.rvector_value;
*(ii++) += 1.5*cosine*cosine - 0.5;
}
break;
case colvarvalue::type_quaternion:
while (xvi != xv_end) {
cvm::real const cosine = (xvi++)->quaternion_value.cosine (x.quaternion_value);
*(ii++) += 1.5*cosine*cosine - 0.5;
}
break;
default:
x.undef_op();
};
}
std::ostream & operator << (std::ostream &os, colvarvalue const &x)
{
switch (x.type()) {
case colvarvalue::type_scalar:
os << x.real_value; break;
case colvarvalue::type_vector:
case colvarvalue::type_unitvector:
os << x.rvector_value; break;
case colvarvalue::type_quaternion:
os << x.quaternion_value; break;
case colvarvalue::type_notset:
os << "not set"; break;
}
return os;
}
std::ostream & operator << (std::ostream &os, std::vector<colvarvalue> const &v)
{
for (size_t i = 0; i < v.size(); i++) os << v[i];
return os;
}
std::istream & operator >> (std::istream &is, colvarvalue &x)
{
if (x.type() == colvarvalue::type_notset) {
cvm::fatal_error ("Trying to read from a stream a colvarvalue, "
"which has not yet been assigned a data type.\n");
}
switch (x.type()) {
case colvarvalue::type_scalar:
is >> x.real_value;
break;
case colvarvalue::type_vector:
case colvarvalue::type_unitvector:
is >> x.rvector_value;
break;
case colvarvalue::type_quaternion:
is >> x.quaternion_value;
break;
default:
x.undef_op();
}
x.apply_constraints();
return is;
}
size_t colvarvalue::output_width (size_t const &real_width)
{
switch (this->value_type) {
case colvarvalue::type_scalar:
return real_width;
case colvarvalue::type_vector:
case colvarvalue::type_unitvector:
return cvm::rvector::output_width (real_width);
case colvarvalue::type_quaternion:
return cvm::quaternion::output_width (real_width);
case colvarvalue::type_notset:
default:
return 0;
}
}

727
lib/colvars/colvarvalue.h Normal file
View File

@ -0,0 +1,727 @@
#ifndef COLVARVALUE_H
#define COLVARVALUE_H
#include "colvarmodule.h"
/// \brief Value of a collective variable: this is a metatype which
/// can be set at runtime. By default it is set to be a scalar
/// number, and can be treated like that in all operations (this is
/// done by most \link cvc \endlink implementations).
///
/// \link colvarvalue \endlink allows \link colvar \endlink to be
/// treat different data types. By default, a \link colvarvalue
/// \endlink variable is a scalar number. If you want to use it as
/// another type, you should declare and initialize a variable as
/// \code colvarvalue x (colvarvalue::type_xxx); \endcode where
/// type_xxx is a value within the \link Type \endlink enum.
/// Alternatively, initialize x with \link x.type
/// (colvarvalue::type_xxx) \endlink at a later stage.
///
/// Given a colvarvalue variable x which is not yet assigned (and
/// thus has not yet a type) it is also possible to correctly assign
/// the type with \code x = y; \endcode if y is correctly set.
/// Otherwise, an error will be raised if the \link Type \endlink of x
/// is different from the \link Type \endlink of y.
///
/// Also, every operator (either unary or binary) on a \link
/// colvarvalue \endlink object performs one or more checks on the
/// \link Type \endlink to avoid errors such as initializing a
/// three-dimensional vector with a scalar number (legal otherwise).
///
/// \b Special \b case: when reading from a stream, there is no way to
/// detect the \link Type \endlink and safely handle errors at the
/// same time. Hence, when using \code is >> x; \endcode x \b MUST
/// already have a type correcly set up for properly parsing the
/// stream. An error will be raised otherwise. Usually this is not
/// the problem, because \link colvarvalue \endlink objects are first
/// initialized in the configuration, and the stream operation will be
/// performed only when reading restart files.
///
/// No problem of course with the output streams: \code os << x;
/// \endcode will print a different output according to the value of
/// colvarvalue::value_type, and the width of such output is returned
/// by colvarvalue::output_width()
///
/// \em Note \em on \em performance: at every operation between two
/// \link colvarvalue \endlink objects, their two \link Type \endlink
/// flags will be checked for a match. In a long array of \link
/// colvarvalue \endlink objects this is time consuming: a few static
/// functions are defined ("xxx_opt" functions) within the \link
/// colvarvalue \endlink class, which only check the matching once for
/// a large array, and execute different loops according to the type.
/// You should do the same for every time consuming loop involving
/// operations on \link colvarvalue \endlink objects if you want
/// e.g. to optimize your colvar bias.
class colvarvalue {
public:
/// \brief Possible types of value
///
/// These three cover most possibilities of data type one can
/// devise. If you need to implement a new colvar with a very
/// complex data type, it's better to put an allocatable class here
enum Type {
/// Undefined type
type_notset,
/// Scalar number, implemented as \link colvarmodule::real \endlink (default)
type_scalar,
/// 3-dimensional vector, implemented as \link colvarmodule::rvector \endlink
type_vector,
/// 3-dimensional unit vector, implemented as \link colvarmodule::rvector \endlink
type_unitvector,
/// 4-dimensional unit vector representing a rotation, implemented as \link colvarmodule::quaternion \endlink
type_quaternion
};
/// Runtime description of value types
std::string static const type_desc[colvarvalue::type_quaternion+1];
/// Number of degrees of freedom for each type
size_t static const dof_num[ colvarvalue::type_quaternion+1];
/// \brief Real data member
cvm::real real_value;
/// \brief Vector data member
cvm::rvector rvector_value;
/// \brief Quaternion data member
cvm::quaternion quaternion_value;
/// Current type of this colvarvalue object
Type value_type;
static inline bool type_checking()
{
return true;
}
/// \brief Default constructor: this class defaults to a scalar
/// number and always behaves like it unless you change its type
inline colvarvalue()
: real_value (0.0), value_type (type_scalar)
{}
/// Constructor from a type specification
inline colvarvalue (Type const &vti)
: value_type (vti)
{
reset();
}
/// Copy constructor from real base type
inline colvarvalue (cvm::real const &x)
: real_value (x), value_type (type_scalar)
{}
/// \brief Copy constructor from rvector base type (Note: this sets
/// automatically a type \link type_vector \endlink , if you want a
/// \link type_unitvector \endlink you must set it explicitly)
inline colvarvalue (cvm::rvector const &v)
: rvector_value (v), value_type (type_vector)
{}
/// \brief Copy constructor from rvector base type (additional
/// argument to make possible to choose a \link type_unitvector
/// \endlink
inline colvarvalue (cvm::rvector const &v, Type const &vti)
: rvector_value (v), value_type (vti)
{}
/// \brief Copy constructor from quaternion base type
inline colvarvalue (cvm::quaternion const &q)
: quaternion_value (q), value_type (type_quaternion)
{}
/// Copy constructor from another \link colvarvalue \endlink
inline colvarvalue (colvarvalue const &x)
: value_type (x.value_type)
{
reset();
switch (x.value_type) {
case type_scalar:
real_value = x.real_value;
break;
case type_vector:
case type_unitvector:
rvector_value = x.rvector_value;
break;
case type_quaternion:
quaternion_value = x.quaternion_value;
break;
case type_notset:
default:
break;
}
}
/// Set to the null value for the data type currently defined
void reset();
/// \brief If the variable has constraints (e.g. unitvector or
/// quaternion), transform it to satisfy them; use it when the \link
/// colvarvalue \endlink is not calculated from \link cvc
/// \endlink objects, but manipulated by you
void apply_constraints();
/// Get the current type
inline Type type() const
{
return value_type;
}
/// Set the type explicitly
inline void type (Type const &vti)
{
reset();
value_type = vti;
reset();
}
/// Set the type after another \link colvarvalue \endlink
inline void type (colvarvalue const &x)
{
reset();
value_type = x.value_type;
reset();
}
/// Square norm of this colvarvalue
cvm::real norm2() const;
/// Norm of this colvarvalue
inline cvm::real norm() const
{
return std::sqrt (this->norm2());
}
/// \brief Return the value whose scalar product with this value is
/// 1
inline colvarvalue inverse() const;
/// Square distance between this \link colvarvalue \endlink and another
cvm::real dist2 (colvarvalue const &x2) const;
/// Derivative with respect to this \link colvarvalue \endlink of the square distance
colvarvalue dist2_grad (colvarvalue const &x2) const;
/// Assignment operator (type of x is checked)
colvarvalue & operator = (colvarvalue const &x);
void operator += (colvarvalue const &x);
void operator -= (colvarvalue const &x);
void operator *= (cvm::real const &a);
void operator /= (cvm::real const &a);
// Casting operator
inline operator cvm::real() const
{
if (value_type != type_scalar) error_rside (type_scalar);
return real_value;
}
// Casting operator
inline operator cvm::rvector() const
{
if ( (value_type != type_vector) && (value_type != type_unitvector))
error_rside (type_vector);
return rvector_value;
}
// Casting operator
inline operator cvm::quaternion() const
{
if (value_type != type_quaternion) error_rside (type_quaternion);
return quaternion_value;
}
/// Special case when the variable is a real number, and all operations are defined
inline bool is_scalar()
{
return (value_type == type_scalar);
}
/// Ensure that the two types are the same within a binary operator
void static check_types (colvarvalue const &x1, colvarvalue const &x2);
/// Undefined operation
void undef_op() const;
/// Trying to assign this \link colvarvalue \endlink object to
/// another object set with a different type
void error_lside (Type const &vt) const;
/// Trying to assign another \link colvarvalue \endlink object set
/// with a different type to this object
void error_rside (Type const &vt) const;
///<2F>Give the number of characters required to output this
/// colvarvalue, given the current type assigned and the number of
/// characters for a real number
size_t output_width (size_t const &real_width);
// optimized routines for operations with an array; xv and inner as
// vectors are assumed to have the same number of elements (i.e. the
// end for inner comes together with xv_end in the loop)
/// \brief Optimized routine for the inner product of one collective
/// variable with an array
void static inner_opt (colvarvalue const &x,
std::vector<colvarvalue>::iterator &xv,
std::vector<colvarvalue>::iterator const &xv_end,
std::vector<cvm::real>::iterator &inner);
/// \brief Optimized routine for the inner product of one collective
/// variable with an array
void static inner_opt (colvarvalue const &x,
std::list<colvarvalue>::iterator &xv,
std::list<colvarvalue>::iterator const &xv_end,
std::vector<cvm::real>::iterator &inner);
/// \brief Optimized routine for the second order Legendre
/// polynomial, (3cos^2(w)-1)/2, of one collective variable with an
/// array
void static p2leg_opt (colvarvalue const &x,
std::vector<colvarvalue>::iterator &xv,
std::vector<colvarvalue>::iterator const &xv_end,
std::vector<cvm::real>::iterator &inner);
/// \brief Optimized routine for the second order Legendre
/// polynomial of one collective variable with an array
void static p2leg_opt (colvarvalue const &x,
std::list<colvarvalue>::iterator &xv,
std::list<colvarvalue>::iterator const &xv_end,
std::vector<cvm::real>::iterator &inner);
};
inline void colvarvalue::reset()
{
switch (value_type) {
case colvarvalue::type_scalar:
real_value = cvm::real (0.0);
break;
case colvarvalue::type_vector:
case colvarvalue::type_unitvector:
rvector_value = cvm::rvector (0.0, 0.0, 0.0);
break;
case colvarvalue::type_quaternion:
quaternion_value = cvm::quaternion (0.0, 0.0, 0.0, 0.0);
break;
case colvarvalue::type_notset:
default:
break;
}
}
inline void colvarvalue::apply_constraints()
{
switch (value_type) {
case colvarvalue::type_scalar:
break;
case colvarvalue::type_vector:
break;
case colvarvalue::type_unitvector:
rvector_value /= std::sqrt (rvector_value.norm2());
break;
case colvarvalue::type_quaternion:
quaternion_value /= std::sqrt (quaternion_value.norm2());
break;
case colvarvalue::type_notset:
default:
break;
}
}
inline cvm::real colvarvalue::norm2() const
{
switch (value_type) {
case colvarvalue::type_scalar:
return (this->real_value)*(this->real_value);
case colvarvalue::type_vector:
case colvarvalue::type_unitvector:
return (this->rvector_value).norm2();
case colvarvalue::type_quaternion:
return (this->quaternion_value).norm2();
case colvarvalue::type_notset:
default:
return 0.0;
}
}
inline colvarvalue colvarvalue::inverse() const
{
switch (value_type) {
case colvarvalue::type_scalar:
return colvarvalue (1.0/real_value);
case colvarvalue::type_vector:
return colvarvalue (cvm::rvector (1.0/rvector_value.x,
1.0/rvector_value.y,
1.0/rvector_value.z));
case colvarvalue::type_quaternion:
return colvarvalue (cvm::quaternion (1.0/quaternion_value.q0,
1.0/quaternion_value.q1,
1.0/quaternion_value.q2,
1.0/quaternion_value.q3));
case colvarvalue::type_notset:
default:
undef_op();
}
return colvarvalue();
}
inline colvarvalue & colvarvalue::operator = (colvarvalue const &x)
{
if (this->value_type != type_notset)
if (this->value_type != x.value_type)
error_lside (x.value_type);
this->value_type = x.value_type;
switch (this->value_type) {
case colvarvalue::type_scalar:
this->real_value = x.real_value;
break;
case colvarvalue::type_vector:
case colvarvalue::type_unitvector:
this->rvector_value = x.rvector_value;
break;
case colvarvalue::type_quaternion:
this->quaternion_value = x.quaternion_value;
break;
case colvarvalue::type_notset:
default:
undef_op();
break;
}
return *this;
}
inline void colvarvalue::operator += (colvarvalue const &x)
{
if (colvarvalue::type_checking())
colvarvalue::check_types (*this, x);
switch (this->value_type) {
case colvarvalue::type_scalar:
this->real_value += x.real_value;
break;
case colvarvalue::type_vector:
case colvarvalue::type_unitvector:
this->rvector_value += x.rvector_value;
break;
case colvarvalue::type_quaternion:
this->quaternion_value += x.quaternion_value;
break;
case colvarvalue::type_notset:
default:
undef_op();
}
}
inline void colvarvalue::operator -= (colvarvalue const &x)
{
if (colvarvalue::type_checking())
colvarvalue::check_types (*this, x);
switch (value_type) {
case colvarvalue::type_scalar:
real_value -= x.real_value; break;
case colvarvalue::type_vector:
case colvarvalue::type_unitvector:
rvector_value -= x.rvector_value; break;
case colvarvalue::type_quaternion:
quaternion_value -= x.quaternion_value; break;
case colvarvalue::type_notset:
default:
undef_op();
}
}
inline void colvarvalue::operator *= (cvm::real const &a)
{
switch (value_type) {
case colvarvalue::type_scalar:
real_value *= a;
break;
case colvarvalue::type_vector:
case colvarvalue::type_unitvector:
rvector_value *= a;
break;
case colvarvalue::type_quaternion:
quaternion_value *= a;
break;
case colvarvalue::type_notset:
default:
undef_op();
}
}
inline void colvarvalue::operator /= (cvm::real const &a)
{
switch (value_type) {
case colvarvalue::type_scalar:
real_value /= a; break;
case colvarvalue::type_vector:
case colvarvalue::type_unitvector:
rvector_value /= a; break;
case colvarvalue::type_quaternion:
quaternion_value /= a; break;
case colvarvalue::type_notset:
default:
undef_op();
}
}
// binary operations between two colvarvalues
inline colvarvalue operator + (colvarvalue const &x1,
colvarvalue const &x2)
{
if (colvarvalue::type_checking())
colvarvalue::check_types (x1, x2);
switch (x1.value_type) {
case colvarvalue::type_scalar:
return colvarvalue (x1.real_value + x2.real_value);
case colvarvalue::type_vector:
return colvarvalue (x1.rvector_value + x2.rvector_value);
case colvarvalue::type_unitvector:
return colvarvalue (x1.rvector_value + x2.rvector_value,
colvarvalue::type_unitvector);
case colvarvalue::type_quaternion:
return colvarvalue (x1.quaternion_value + x2.quaternion_value);
case colvarvalue::type_notset:
default:
x1.undef_op();
return colvarvalue (colvarvalue::type_notset);
};
}
inline colvarvalue operator - (colvarvalue const &x1,
colvarvalue const &x2)
{
if (colvarvalue::type_checking())
colvarvalue::check_types (x1, x2);
switch (x1.value_type) {
case colvarvalue::type_scalar:
return colvarvalue (x1.real_value - x2.real_value);
case colvarvalue::type_vector:
return colvarvalue (x1.rvector_value - x2.rvector_value);
case colvarvalue::type_unitvector:
return colvarvalue (x1.rvector_value - x2.rvector_value,
colvarvalue::type_unitvector);
case colvarvalue::type_quaternion:
return colvarvalue (x1.quaternion_value - x2.quaternion_value);
default:
x1.undef_op();
return colvarvalue (colvarvalue::type_notset);
};
}
// binary operations with real numbers
inline colvarvalue operator * (cvm::real const &a,
colvarvalue const &x)
{
switch (x.value_type) {
case colvarvalue::type_scalar:
return colvarvalue (a * x.real_value);
case colvarvalue::type_vector:
return colvarvalue (a * x.rvector_value);
case colvarvalue::type_unitvector:
return colvarvalue (a * x.rvector_value,
colvarvalue::type_unitvector);
case colvarvalue::type_quaternion:
return colvarvalue (a * x.quaternion_value);
case colvarvalue::type_notset:
default:
x.undef_op();
return colvarvalue (colvarvalue::type_notset);
}
}
inline colvarvalue operator * (colvarvalue const &x,
cvm::real const &a)
{
switch (x.value_type) {
case colvarvalue::type_scalar:
return colvarvalue (x.real_value * a);
case colvarvalue::type_vector:
return colvarvalue (x.rvector_value * a);
case colvarvalue::type_unitvector:
return colvarvalue (x.rvector_value * a,
colvarvalue::type_unitvector);
case colvarvalue::type_quaternion:
return colvarvalue (x.quaternion_value * a);
case colvarvalue::type_notset:
default:
x.undef_op();
return colvarvalue (colvarvalue::type_notset);
}
}
inline colvarvalue operator / (colvarvalue const &x,
cvm::real const &a)
{
switch (x.value_type) {
case colvarvalue::type_scalar:
return colvarvalue (x.real_value / a);
case colvarvalue::type_vector:
return colvarvalue (x.rvector_value / a);
case colvarvalue::type_unitvector:
return colvarvalue (x.rvector_value / a,
colvarvalue::type_unitvector);
case colvarvalue::type_quaternion:
return colvarvalue (x.quaternion_value / a);
case colvarvalue::type_notset:
default:
x.undef_op();
return colvarvalue (colvarvalue::type_notset);
}
}
// inner product between two colvarvalues
inline cvm::real operator * (colvarvalue const &x1,
colvarvalue const &x2)
{
if (colvarvalue::type_checking())
colvarvalue::check_types (x1, x2);
switch (x1.value_type) {
case colvarvalue::type_scalar:
return (x1.real_value * x2.real_value);
case colvarvalue::type_vector:
case colvarvalue::type_unitvector:
return (x1.rvector_value * x2.rvector_value);
case colvarvalue::type_quaternion:
// the "*" product is the quaternion product, here the inner
// member function is used instead
return (x1.quaternion_value.inner (x2.quaternion_value));
case colvarvalue::type_notset:
default:
x1.undef_op();
return 0.0;
};
}
inline cvm::real colvarvalue::dist2 (colvarvalue const &x2) const
{
if (colvarvalue::type_checking())
colvarvalue::check_types (*this, x2);
switch (this->value_type) {
case colvarvalue::type_scalar:
return std::pow (this->real_value - x2.real_value, int (2));
case colvarvalue::type_vector:
return (this->rvector_value - x2.rvector_value).norm2();
case colvarvalue::type_unitvector:
// angle between (*this) and x2 is the distance
return std::pow (std::acos (this->rvector_value * x2.rvector_value), int (2));
case colvarvalue::type_quaternion:
// angle between (*this) and x2 is the distance, the quaternion
// object has it implemented internally
return this->quaternion_value.dist2 (x2.quaternion_value);
case colvarvalue::type_notset:
default:
this->undef_op();
return 0.0;
};
}
inline colvarvalue colvarvalue::dist2_grad (colvarvalue const &x2) const
{
if (colvarvalue::type_checking())
colvarvalue::check_types (*this, x2);
switch (this->value_type) {
case colvarvalue::type_scalar:
return 2.0 * (this->real_value - x2.real_value);
case colvarvalue::type_vector:
return 2.0 * (this->rvector_value - x2.rvector_value);
case colvarvalue::type_unitvector:
{
cvm::rvector const &v1 = this->rvector_value;
cvm::rvector const &v2 = x2.rvector_value;
cvm::real const cos_t = v1 * v2;
cvm::real const sin_t = std::sqrt (1.0 - cos_t*cos_t);
return colvarvalue ( 2.0 * sin_t *
cvm::rvector ( (-1.0) * sin_t * v2.x +
cos_t/sin_t * (v1.x - cos_t*v2.x),
(-1.0) * sin_t * v2.y +
cos_t/sin_t * (v1.y - cos_t*v2.y),
(-1.0) * sin_t * v2.z +
cos_t/sin_t * (v1.z - cos_t*v2.z)
),
colvarvalue::type_unitvector );
}
case colvarvalue::type_quaternion:
return this->quaternion_value.dist2_grad (x2.quaternion_value);
case colvarvalue::type_notset:
default:
this->undef_op();
return colvarvalue (colvarvalue::type_notset);
};
}
inline void colvarvalue::check_types (colvarvalue const &x1,
colvarvalue const &x2)
{
if (x1.value_type != x2.value_type) {
cvm::log ("x1 type = "+cvm::to_str (x1.value_type)+
", x2 type = "+cvm::to_str (x2.value_type)+"\n");
cvm::fatal_error ("Performing an operation between two colvar "
"values with different types, \""+
colvarvalue::type_desc[x1.value_type]+
"\" and \""+
colvarvalue::type_desc[x2.value_type]+
"\".\n");
}
}
std::ostream & operator << (std::ostream &os, colvarvalue const &x);
std::ostream & operator << (std::ostream &os, std::vector<colvarvalue> const &v);
std::istream & operator >> (std::istream &is, colvarvalue &x);
#endif
// Emacs
// Local Variables:
// mode: C++
// End: