lammps/lib/colvars/colvar.cpp

1673 lines
51 KiB
C++

// -*- c++ -*-
#include "colvarmodule.h"
#include "colvarvalue.h"
#include "colvarparse.h"
#include "colvar.h"
#include "colvarcomp.h"
#include <algorithm>
// XX TODO make the acf code handle forces as well as values and velocities
colvar::colvar (std::string const &conf)
{
cvm::log ("Initializing a new collective variable.\n");
get_keyval (conf, "name", this->name,
(std::string ("colvar")+cvm::to_str (cvm::colvars.size()+1)));
for (std::vector<colvar *>::iterator cvi = cvm::colvars.begin();
cvi < cvm::colvars.end();
cvi++) {
if ((*cvi)->name == this->name)
cvm::fatal_error ("Error: this colvar cannot have the same name, \""+this->name+
"\", as another colvar.\n");
}
// all tasks disabled by default
for (size_t i = 0; i < task_ntot; i++) {
tasks[i] = false;
}
kinetic_energy = 0.0;
potential_energy = 0.0;
// read the configuration and set up corresponding instances, for
// each type of component implemented
#define initialize_components(def_desc,def_config_key,def_class_name) \
{ \
size_t def_count = 0; \
std::string def_conf = ""; \
size_t pos = 0; \
while ( this->key_lookup (conf, \
def_config_key, \
def_conf, \
pos) ) { \
if (!def_conf.size()) continue; \
cvm::log ("Initializing " \
"a new \""+std::string (def_config_key)+"\" component"+ \
(cvm::debug() ? ", with configuration:\n"+def_conf \
: ".\n")); \
cvm::increase_depth(); \
cvc *cvcp = new colvar::def_class_name (def_conf); \
if (cvcp != NULL) { \
cvcs.push_back (cvcp); \
cvcp->check_keywords (def_conf, def_config_key); \
cvm::decrease_depth(); \
} else { \
cvm::fatal_error ("Error: in allocating component \"" \
def_config_key"\".\n"); \
} \
if ( (cvcp->period != 0.0) || (cvcp->wrap_center != 0.0) ) { \
if ( (cvcp->function_type != std::string ("distance_z")) && \
(cvcp->function_type != std::string ("dihedral")) && \
(cvcp->function_type != std::string ("spin_angle")) ) { \
cvm::fatal_error ("Error: invalid use of period and/or " \
"wrapAround in a \""+ \
std::string (def_config_key)+ \
"\" component.\n"+ \
"Period: "+cvm::to_str(cvcp->period) + \
" wrapAround: "+cvm::to_str(cvcp->wrap_center));\
} \
} \
if ( ! cvcs.back()->name.size()) \
cvcs.back()->name = std::string (def_config_key)+ \
(cvm::to_str (++def_count)); \
if (cvm::debug()) \
cvm::log ("Done initializing a \""+ \
std::string (def_config_key)+ \
"\" component"+ \
(cvm::debug() ? \
", named \""+cvcs.back()->name+"\"" \
: "")+".\n"); \
def_conf = ""; \
if (cvm::debug()) \
cvm::log ("Parsed "+cvm::to_str (cvcs.size())+ \
" components at this time.\n"); \
} \
}
initialize_components ("distance", "distance", distance);
initialize_components ("distance vector", "distanceVec", distance_vec);
initialize_components ("distance vector "
"direction", "distanceDir", distance_dir);
initialize_components ("distance projection "
"on an axis", "distanceZ", distance_z);
initialize_components ("distance projection "
"on a plane", "distanceXY", distance_xy);
initialize_components ("average distance weighted by inverse power",
"distanceInv", distance_inv);
initialize_components ("coordination "
"number", "coordNum", coordnum);
initialize_components ("self-coordination "
"number", "selfCoordNum", selfcoordnum);
initialize_components ("angle", "angle", angle);
initialize_components ("dihedral", "dihedral", dihedral);
initialize_components ("hydrogen bond", "hBond", h_bond);
// initialize_components ("alpha helix", "alphaDihedrals", alpha_dihedrals);
initialize_components ("alpha helix", "alpha", alpha_angles);
initialize_components ("dihedral principal "
"component", "dihedralPC", dihedPC);
initialize_components ("orientation", "orientation", orientation);
initialize_components ("orientation "
"angle", "orientationAngle",orientation_angle);
initialize_components ("tilt", "tilt", tilt);
initialize_components ("spin angle", "spinAngle", spin_angle);
initialize_components ("RMSD", "rmsd", rmsd);
// initialize_components ("logarithm of MSD", "logmsd", logmsd);
initialize_components ("radius of "
"gyration", "gyration", gyration);
initialize_components ("moment of "
"inertia", "inertia", inertia);
initialize_components ("moment of inertia around an axis",
"inertiaZ", inertia_z);
initialize_components ("eigenvector", "eigenvector", eigenvector);
if (!cvcs.size())
cvm::fatal_error ("Error: no valid components were provided "
"for this collective variable.\n");
cvm::log ("All components initialized.\n");
// this is set false if any of the components has an exponent
// different from 1 in the polynomial
b_linear = true;
// these will be set to false if any of the cvcs has them false
b_inverse_gradients = true;
b_Jacobian_force = true;
// Decide whether the colvar is periodic
// Used to wrap extended DOF if extendedLagrangian is on
if (cvcs.size() == 1 && (cvcs[0])->b_periodic && (cvcs[0])->sup_np == 1
&& (cvcs[0])->sup_coeff == 1.0 ) {
this->b_periodic = true;
this->period = (cvcs[0])->period;
// TODO write explicit wrap() function for colvars to allow for
// sup_coeff different from 1
// this->period = (cvcs[0])->period * (cvcs[0])->sup_coeff;
} else {
this->b_periodic = false;
this->period = 0.0;
}
// check the available features of each cvc
for (size_t i = 0; i < cvcs.size(); i++) {
if ((cvcs[i])->sup_np != 1) {
if (cvm::debug() && b_linear)
cvm::log ("Warning: You are using a non-linear polynomial "
"combination to define this collective variable, "
"some biasing methods may be unavailable.\n");
b_linear = false;
if ((cvcs[i])->sup_np < 0) {
cvm::log ("Warning: you chose a negative exponent in the combination; "
"if you apply forces, the simulation may become unstable "
"when the component \""+
(cvcs[i])->function_type+"\" approaches zero.\n");
}
}
if ((cvcs[i])->b_periodic && !b_periodic) {
cvm::log ("Warning: although this component is periodic, the colvar will "
"not be treated as periodic, either because the exponent is not "
"1, or because multiple components are present. Make sure that "
"you know what you are doing!");
}
if (! (cvcs[i])->b_inverse_gradients)
b_inverse_gradients = false;
if (! (cvcs[i])->b_Jacobian_derivative)
b_Jacobian_force = false;
for (size_t j = i; j < cvcs.size(); j++) {
if ( (cvcs[i])->type() != (cvcs[j])->type() ) {
cvm::fatal_error ("ERROR: you are definining this collective variable "
"by using components of different types, \""+
colvarvalue::type_desc[(cvcs[i])->type()]+
"\" and \""+
colvarvalue::type_desc[(cvcs[j])->type()]+
"\". "
"You must use the same type in order to "
" sum them together.\n");
}
}
}
{
colvarvalue::Type const value_type = (cvcs[0])->type();
if (cvm::debug())
cvm::log ("This collective variable is a "+
colvarvalue::type_desc[value_type]+", corresponding to "+
cvm::to_str (colvarvalue::dof_num[value_type])+
" internal degrees of freedom.\n");
x.type (value_type);
x_reported.type (value_type);
}
get_keyval (conf, "width", width, 1.0);
if (width <= 0.0)
cvm::fatal_error ("Error: \"width\" must be positive.\n");
lower_boundary.type (this->type());
lower_wall.type (this->type());
upper_boundary.type (this->type());
upper_wall.type (this->type());
if (this->type() == colvarvalue::type_scalar) {
if (get_keyval (conf, "lowerBoundary", lower_boundary, lower_boundary)) {
enable (task_lower_boundary);
}
get_keyval (conf, "lowerWallConstant", lower_wall_k, 0.0);
if (lower_wall_k > 0.0) {
get_keyval (conf, "lowerWall", lower_wall, lower_boundary);
enable (task_lower_wall);
}
if (get_keyval (conf, "upperBoundary", upper_boundary, upper_boundary)) {
enable (task_upper_boundary);
}
get_keyval (conf, "upperWallConstant", upper_wall_k, 0.0);
if (upper_wall_k > 0.0) {
get_keyval (conf, "upperWall", upper_wall, upper_boundary);
enable (task_upper_wall);
}
}
if (tasks[task_lower_boundary]) {
get_keyval (conf, "hardLowerBoundary", hard_lower_boundary, false);
}
if (tasks[task_upper_boundary]) {
get_keyval (conf, "hardUpperBoundary", hard_upper_boundary, false);
}
// consistency checks for boundaries and walls
if (tasks[task_lower_boundary] && tasks[task_upper_boundary]) {
if (lower_boundary >= upper_boundary) {
cvm::fatal_error ("Error: the upper boundary, "+
cvm::to_str (upper_boundary)+
", is not higher than the lower boundary, "+
cvm::to_str (lower_boundary)+".\n");
}
}
if (tasks[task_lower_wall] && tasks[task_upper_wall]) {
if (lower_wall >= upper_wall) {
cvm::fatal_error ("Error: the upper wall, "+
cvm::to_str (upper_wall)+
", is not higher than the lower wall, "+
cvm::to_str (lower_wall)+".\n");
}
if (dist2 (lower_wall, upper_wall) < 1.0E-12) {
cvm::log ("Lower wall and upper wall are equal "
"in the periodic domain of the colvar: disabling walls.\n");
disable (task_lower_wall);
disable (task_upper_wall);
}
}
get_keyval (conf, "expandBoundaries", expand_boundaries, false);
if (expand_boundaries && periodic_boundaries()) {
cvm::fatal_error ("Error: trying to expand boundaries that already "
"cover a whole period of a periodic colvar.\n");
}
if (expand_boundaries && hard_lower_boundary && hard_upper_boundary) {
cvm::fatal_error ("Error: inconsistent configuration "
"(trying to expand boundaries with both "
"hardLowerBoundary and hardUpperBoundary enabled).\n");
}
{
bool b_extended_lagrangian;
get_keyval (conf, "extendedLagrangian", b_extended_lagrangian, false);
if (b_extended_lagrangian) {
cvm::real temp, tolerance, period;
cvm::log ("Enabling the extended Lagrangian term for colvar \""+
this->name+"\".\n");
enable (task_extended_lagrangian);
xr.type (this->type());
vr.type (this->type());
fr.type (this->type());
const bool found = get_keyval (conf, "extendedTemp", temp, cvm::temperature());
if (temp <= 0.0) {
if (found)
cvm::fatal_error ("Error: \"extendedTemp\" must be positive.\n");
else
cvm::fatal_error ("Error: a positive temperature must be provided, either "
"by enabling a thermostat, or through \"extendedTemp\".\n");
}
get_keyval (conf, "extendedFluctuation", tolerance, width);
if (tolerance <= 0.0)
cvm::fatal_error ("Error: \"extendedFluctuation\" must be positive.\n");
ext_force_k = cvm::boltzmann() * temp / (tolerance * tolerance);
cvm::log ("Computed extended system force constant: " + cvm::to_str(ext_force_k) + " kcal/mol/U^2");
get_keyval (conf, "extendedTimeConstant", period, 200.0);
if (period <= 0.0)
cvm::fatal_error ("Error: \"extendedTimeConstant\" must be positive.\n");
ext_mass = (cvm::boltzmann() * temp * period * period)
/ (4.0 * PI * PI * tolerance * tolerance);
cvm::log ("Computed fictitious mass: " + cvm::to_str(ext_mass) + " kcal/mol/(U/fs)^2 (U: colvar unit)");
{
bool b_output_energy;
get_keyval (conf, "outputEnergy", b_output_energy, false);
if (b_output_energy) {
enable (task_output_energy);
}
}
get_keyval (conf, "extendedLangevinDamping", ext_gamma, 1.0);
if (ext_gamma < 0.0)
cvm::fatal_error ("Error: \"extendedLangevinDamping\" may not be negative.\n");
if (ext_gamma != 0.0) {
enable (task_langevin);
ext_gamma *= 1.0e-3; // convert from ps-1 to fs-1
ext_sigma = std::sqrt(2.0 * cvm::boltzmann() * temp * ext_gamma * ext_mass / cvm::dt());
}
}
}
{
bool b_output_value;
get_keyval (conf, "outputValue", b_output_value, true);
if (b_output_value) {
enable (task_output_value);
}
}
{
bool b_output_velocity;
get_keyval (conf, "outputVelocity", b_output_velocity, false);
if (b_output_velocity) {
enable (task_output_velocity);
}
}
{
bool b_output_system_force;
get_keyval (conf, "outputSystemForce", b_output_system_force, false);
if (b_output_system_force) {
enable (task_output_system_force);
}
}
{
bool b_output_applied_force;
get_keyval (conf, "outputAppliedForce", b_output_applied_force, false);
if (b_output_applied_force) {
enable (task_output_applied_force);
}
}
if (cvm::b_analysis)
parse_analysis (conf);
if (cvm::debug())
cvm::log ("Done initializing collective variable \""+this->name+"\".\n");
}
void colvar::build_atom_list (void)
{
// If atomic gradients are requested, build full list of atom ids from all cvcs
std::list<int> temp_id_list;
for (size_t i = 0; i < cvcs.size(); i++) {
for (size_t j = 0; j < cvcs[i]->atom_groups.size(); j++) {
for (size_t k = 0; k < cvcs[i]->atom_groups[j]->size(); k++) {
temp_id_list.push_back (cvcs[i]->atom_groups[j]->at(k).id);
}
}
}
temp_id_list.sort();
temp_id_list.unique();
atom_ids = std::vector<int> (temp_id_list.begin(), temp_id_list.end());
temp_id_list.clear();
atomic_gradients.resize (atom_ids.size());
if (atom_ids.size()) {
if (cvm::debug())
cvm::log ("Colvar: created atom list with " + cvm::to_str(atom_ids.size()) + " atoms.\n");
} else {
cvm::log ("Warning: colvar components communicated no atom IDs.\n");
}
}
void colvar::parse_analysis (std::string const &conf)
{
// if (cvm::debug())
// cvm::log ("Parsing analysis flags for collective variable \""+
// this->name+"\".\n");
runave_length = 0;
bool b_runave = false;
if (get_keyval (conf, "runAve", b_runave) && b_runave) {
enable (task_runave);
get_keyval (conf, "runAveLength", runave_length, 1000);
get_keyval (conf, "runAveStride", runave_stride, 1);
if ((cvm::restart_out_freq % runave_stride) != 0)
cvm::fatal_error ("Error: runAveStride must be commensurate with the restart frequency.\n");
std::string runave_outfile;
get_keyval (conf, "runAveOutputFile", runave_outfile,
std::string (cvm::output_prefix+"."+
this->name+".runave.traj"));
size_t const this_cv_width = x.output_width (cvm::cv_width);
runave_os.open (runave_outfile.c_str());
runave_os << "# " << cvm::wrap_string ("step", cvm::it_width-2)
<< " "
<< cvm::wrap_string ("running average", this_cv_width)
<< " "
<< cvm::wrap_string ("running stddev", this_cv_width)
<< "\n";
}
acf_length = 0;
bool b_acf = false;
if (get_keyval (conf, "corrFunc", b_acf) && b_acf) {
enable (task_corrfunc);
std::string acf_colvar_name;
get_keyval (conf, "corrFuncWithColvar", acf_colvar_name, this->name);
if (acf_colvar_name == this->name) {
cvm::log ("Calculating auto-correlation function.\n");
} else {
cvm::log ("Calculating correlation function with \""+
this->name+"\".\n");
}
std::string acf_type_str;
get_keyval (conf, "corrFuncType", acf_type_str, to_lower_cppstr (std::string ("velocity")));
if (acf_type_str == to_lower_cppstr (std::string ("coordinate"))) {
acf_type = acf_coor;
} else if (acf_type_str == to_lower_cppstr (std::string ("velocity"))) {
acf_type = acf_vel;
enable (task_fdiff_velocity);
if (acf_colvar_name.size())
(cvm::colvar_p (acf_colvar_name))->enable (task_fdiff_velocity);
} else if (acf_type_str == to_lower_cppstr (std::string ("coordinate_p2"))) {
acf_type = acf_p2coor;
} else {
cvm::fatal_error ("Unknown type of correlation function, \""+
acf_type_str+"\".\n");
}
get_keyval (conf, "corrFuncOffset", acf_offset, 0);
get_keyval (conf, "corrFuncLength", acf_length, 1000);
get_keyval (conf, "corrFuncStride", acf_stride, 1);
if ((cvm::restart_out_freq % acf_stride) != 0)
cvm::fatal_error ("Error: corrFuncStride must be commensurate with the restart frequency.\n");
get_keyval (conf, "corrFuncNormalize", acf_normalize, true);
get_keyval (conf, "corrFuncOutputFile", acf_outfile,
std::string (cvm::output_prefix+"."+this->name+
".corrfunc.dat"));
}
}
void colvar::enable (colvar::task const &t)
{
switch (t) {
case task_output_system_force:
enable (task_system_force);
break;
case task_report_Jacobian_force:
enable (task_Jacobian_force);
enable (task_system_force);
if (cvm::debug())
cvm::log ("Adding the Jacobian force to the system force, "
"rather than applying its opposite silently.\n");
break;
case task_Jacobian_force:
// checks below do not apply to extended-system colvars
if ( !tasks[task_extended_lagrangian] ) {
enable (task_gradients);
if (!b_Jacobian_force)
cvm::fatal_error ("Error: colvar \""+this->name+
"\" does not have Jacobian forces implemented.\n");
if (!b_linear)
cvm::fatal_error ("Error: colvar \""+this->name+
"\" must be defined as a linear combination "
"to calculate the Jacobian force.\n");
if (cvm::debug())
cvm::log ("Enabling calculation of the Jacobian force "
"on this colvar.\n");
}
fj.type (this->type());
break;
case task_system_force:
if (!tasks[task_extended_lagrangian]) {
if (!b_inverse_gradients) {
cvm::fatal_error ("Error: one or more of the components of "
"colvar \""+this->name+
"\" does not support system force calculation.\n");
}
cvm::request_system_force();
}
ft.type (this->type());
ft_reported.type (this->type());
break;
case task_output_applied_force:
case task_lower_wall:
case task_upper_wall:
// all of the above require gradients
enable (task_gradients);
break;
case task_fdiff_velocity:
x_old.type (this->type());
v_fdiff.type (this->type());
v_reported.type (this->type());
break;
case task_output_velocity:
enable (task_fdiff_velocity);
break;
case task_grid:
if (this->type() != colvarvalue::type_scalar)
cvm::fatal_error ("Cannot calculate a grid for collective variable, \""+
this->name+"\", because its value is not a scalar number.\n");
break;
case task_extended_lagrangian:
enable (task_gradients);
v_reported.type (this->type());
break;
case task_lower_boundary:
case task_upper_boundary:
if (this->type() != colvarvalue::type_scalar) {
cvm::fatal_error ("Error: this colvar is not a scalar value "
"and cannot produce a grid.\n");
}
break;
case task_output_value:
case task_runave:
case task_corrfunc:
case task_ntot:
break;
case task_gradients:
f.type (this->type());
fb.type (this->type());
break;
case task_collect_gradients:
if (this->type() != colvarvalue::type_scalar)
cvm::fatal_error ("Collecting atomic gradients for non-scalar collective variable \""+
this->name+"\" is not yet implemented.\n");
enable (task_gradients);
if (atom_ids.size() == 0) {
build_atom_list();
}
break;
}
tasks[t] = true;
}
void colvar::disable (colvar::task const &t)
{
// check dependencies
switch (t) {
case task_gradients:
disable (task_upper_wall);
disable (task_lower_wall);
disable (task_output_applied_force);
disable (task_system_force);
disable (task_Jacobian_force);
break;
case task_system_force:
disable (task_output_system_force);
break;
case task_Jacobian_force:
disable (task_report_Jacobian_force);
break;
case task_fdiff_velocity:
disable (task_output_velocity);
break;
case task_lower_boundary:
case task_upper_boundary:
disable (task_grid);
break;
case task_extended_lagrangian:
case task_report_Jacobian_force:
case task_output_value:
case task_output_velocity:
case task_output_applied_force:
case task_output_system_force:
case task_runave:
case task_corrfunc:
case task_grid:
case task_lower_wall:
case task_upper_wall:
case task_ntot:
break;
}
tasks[t] = false;
}
colvar::~colvar()
{
for (size_t i = 0; i < cvcs.size(); i++) {
delete cvcs[i];
}
}
// ******************** CALC FUNCTIONS ********************
void colvar::calc()
{
if (cvm::debug())
cvm::log ("Calculating colvar \""+this->name+"\".\n");
// prepare atom groups for calculation
for (size_t i = 0; i < cvcs.size(); i++) {
for (size_t ig = 0; ig < cvcs[i]->atom_groups.size(); ig++) {
cvm::atom_group &atoms = *(cvcs[i]->atom_groups[ig]);
atoms.reset_atoms_data();
atoms.read_positions();
if (atoms.b_center || atoms.b_rotate) {
atoms.calc_apply_roto_translation();
}
// each atom group will take care of its own ref_pos_group, if defined
}
}
if (tasks[task_output_velocity]) {
for (size_t i = 0; i < cvcs.size(); i++) {
for (size_t ig = 0; ig < cvcs[i]->atom_groups.size(); ig++) {
cvcs[i]->atom_groups[ig]->read_velocities();
}
}
}
if (tasks[task_system_force]) {
for (size_t i = 0; i < cvcs.size(); i++) {
for (size_t ig = 0; ig < cvcs[i]->atom_groups.size(); ig++) {
cvcs[i]->atom_groups[ig]->read_system_forces();
}
}
}
// calculate the value of the colvar
x.reset();
if (x.type() == colvarvalue::type_scalar) {
// polynomial combination allowed
for (size_t i = 0; i < cvcs.size(); i++) {
cvm::increase_depth();
(cvcs[i])->calc_value();
cvm::decrease_depth();
if (cvm::debug())
cvm::log ("Colvar component no. "+cvm::to_str (i+1)+
" within colvar \""+this->name+"\" has value "+
cvm::to_str ((cvcs[i])->value(),
cvm::cv_width, cvm::cv_prec)+".\n");
x += (cvcs[i])->sup_coeff *
( ((cvcs[i])->sup_np != 1) ?
std::pow ((cvcs[i])->value().real_value, (cvcs[i])->sup_np) :
(cvcs[i])->value().real_value );
}
} else {
// only linear combination allowed
for (size_t i = 0; i < cvcs.size(); i++) {
cvm::increase_depth();
(cvcs[i])->calc_value();
cvm::decrease_depth();
if (cvm::debug())
cvm::log ("Colvar component no. "+cvm::to_str (i+1)+
" within colvar \""+this->name+"\" has value "+
cvm::to_str ((cvcs[i])->value(),
cvm::cv_width, cvm::cv_prec)+".\n");
x += (cvcs[i])->sup_coeff * (cvcs[i])->value();
}
}
if (cvm::debug())
cvm::log ("Colvar \""+this->name+"\" has value "+
cvm::to_str (x, cvm::cv_width, cvm::cv_prec)+".\n");
if (tasks[task_gradients]) {
if (cvm::debug())
cvm::log ("Calculating gradients of colvar \""+this->name+"\".\n");
for (size_t i = 0; i < cvcs.size(); i++) {
// calculate the gradients of each component
cvm::increase_depth();
(cvcs[i])->calc_gradients();
// if requested, propagate (via chain rule) the gradients above
// to the atoms used to define the roto-translation
for (size_t ig = 0; ig < cvcs[i]->atom_groups.size(); ig++) {
if (cvcs[i]->atom_groups[ig]->b_fit_gradients)
cvcs[i]->atom_groups[ig]->calc_fit_gradients();
}
cvm::decrease_depth();
}
if (cvm::debug())
cvm::log ("Done calculating gradients of colvar \""+this->name+"\".\n");
if (tasks[task_collect_gradients]) {
// Collect the atomic gradients inside colvar object
for (int a = 0; a < atomic_gradients.size(); a++) {
atomic_gradients[a].reset();
}
for (size_t i = 0; i < cvcs.size(); i++) {
// Coefficient: d(a * x^n) = a * n * x^(n-1) * dx
cvm::real coeff = (cvcs[i])->sup_coeff * cvm::real ((cvcs[i])->sup_np) *
std::pow ((cvcs[i])->value().real_value, (cvcs[i])->sup_np-1);
for (size_t j = 0; j < cvcs[i]->atom_groups.size(); j++) {
// If necessary, apply inverse rotation to get atomic
// gradient in the laboratory frame
if (cvcs[i]->atom_groups[j]->b_rotate) {
cvm::rotation const rot_inv = cvcs[i]->atom_groups[j]->rot.inverse();
for (size_t k = 0; k < cvcs[i]->atom_groups[j]->size(); k++) {
int a = std::lower_bound (atom_ids.begin(), atom_ids.end(),
cvcs[i]->atom_groups[j]->at(k).id) - atom_ids.begin();
atomic_gradients[a] += coeff *
rot_inv.rotate (cvcs[i]->atom_groups[j]->at(k).grad);
}
} else {
for (size_t k = 0; k < cvcs[i]->atom_groups[j]->size(); k++) {
int a = std::lower_bound (atom_ids.begin(), atom_ids.end(),
cvcs[i]->atom_groups[j]->at(k).id) - atom_ids.begin();
atomic_gradients[a] += coeff * cvcs[i]->atom_groups[j]->at(k).grad;
}
}
}
}
}
}
if (tasks[task_system_force]) {
if (cvm::debug())
cvm::log ("Calculating system force of colvar \""+this->name+"\".\n");
ft.reset();
if(!tasks[task_extended_lagrangian] && (cvm::step_relative() > 0)) {
// get from the cvcs the system forces from the PREVIOUS step
for (size_t i = 0; i < cvcs.size(); i++) {
(cvcs[i])->calc_force_invgrads();
// linear combination is assumed
cvm::increase_depth();
ft += (cvcs[i])->system_force() / ((cvcs[i])->sup_coeff * cvm::real (cvcs.size()));
cvm::decrease_depth();
}
}
if (tasks[task_report_Jacobian_force]) {
// add the Jacobian force to the system force, and don't apply any silent
// correction internally: biases such as colvarbias_abf will handle it
ft += fj;
}
if (cvm::debug())
cvm::log ("Done calculating system force of colvar \""+this->name+"\".\n");
}
if (tasks[task_fdiff_velocity]) {
// calculate the velocity by finite differences
if (cvm::step_relative() == 0)
x_old = x;
else {
v_fdiff = fdiff_velocity (x_old, x);
v_reported = v_fdiff;
}
}
if (tasks[task_extended_lagrangian]) {
// initialize the restraint center in the first step to the value
// just calculated from the cvcs
// TODO: put it in the restart information
if (cvm::step_relative() == 0) {
xr = x;
vr = 0.0; // (already 0; added for clarity)
}
// report the restraint center as "value"
x_reported = xr;
v_reported = vr;
// the "system force" with the extended Lagrangian is just the
// harmonic term acting on the extended coordinate
// Note: this is the force for current timestep
ft_reported = (-0.5 * ext_force_k) * this->dist2_lgrad (xr, x);
} else {
x_reported = x;
ft_reported = ft;
}
if (cvm::debug())
cvm::log ("Done calculating colvar \""+this->name+"\".\n");
}
cvm::real colvar::update()
{
if (cvm::debug())
cvm::log ("Updating colvar \""+this->name+"\".\n");
// set to zero the applied force
f.reset();
// add the biases' force, which at this point should already have
// been summed over each bias using this colvar
f += fb;
if (tasks[task_lower_wall] || tasks[task_upper_wall]) {
// wall force
colvarvalue fw (this->type());
// if the two walls are applied concurrently, decide which is the
// closer one (on a periodic colvar, both walls may be applicable
// at the same time)
if ( (!tasks[task_upper_wall]) ||
(this->dist2 (x, lower_wall) < this->dist2 (x, upper_wall)) ) {
cvm::real const grad = this->dist2_lgrad (x, lower_wall);
if (grad < 0.0) {
fw = -0.5 * lower_wall_k * grad;
if (cvm::debug())
cvm::log ("Applying a lower wall force ("+
cvm::to_str (fw)+") to \""+this->name+"\".\n");
f += fw;
}
} else {
cvm::real const grad = this->dist2_lgrad (x, upper_wall);
if (grad > 0.0) {
fw = -0.5 * upper_wall_k * grad;
if (cvm::debug())
cvm::log ("Applying an upper wall force ("+
cvm::to_str (fw)+") to \""+this->name+"\".\n");
f += fw;
}
}
}
if (tasks[task_Jacobian_force]) {
cvm::increase_depth();
for (size_t i = 0; i < cvcs.size(); i++) {
(cvcs[i])->calc_Jacobian_derivative();
}
cvm::decrease_depth();
fj.reset();
for (size_t i = 0; i < cvcs.size(); i++) {
// linear combination is assumed
fj += 1.0 / ( cvm::real (cvcs.size()) * cvm::real ((cvcs[i])->sup_coeff) ) *
(cvcs[i])->Jacobian_derivative();
}
fj *= cvm::boltzmann() * cvm::temperature();
// the instantaneous Jacobian force was not included in the reported system force;
// instead, it is subtracted from the applied force (silent Jacobian correction)
if (! tasks[task_report_Jacobian_force])
f -= fj;
}
if (tasks[task_extended_lagrangian]) {
cvm::real dt = cvm::dt();
// the total force is applied to the fictitious mass, while the
// atoms only feel the harmonic force
// fr: extended coordinate force; f: colvar force applied to atomic coordinates
fr = f;
fr += (-0.5 * ext_force_k) * this->dist2_lgrad (xr, x);
f = (-0.5 * ext_force_k) * this->dist2_rgrad (xr, x);
// leap frog: starting from x_i, f_i, v_(i-1/2)
vr += (0.5 * dt) * fr / ext_mass;
// Because of leapfrog, kinetic energy at time i is approximate
kinetic_energy = 0.5 * ext_mass * vr * vr;
potential_energy = 0.5 * ext_force_k * this->dist2(xr, x);
// leap to v_(i+1/2)
if (tasks[task_langevin]) {
vr -= dt * ext_gamma * vr.real_value;
vr += dt * ext_sigma * cvm::rand_gaussian() / ext_mass;
}
vr += (0.5 * dt) * fr / ext_mass;
xr += dt * vr;
xr.apply_constraints();
if (this->b_periodic) this->wrap (xr);
}
if (tasks[task_fdiff_velocity]) {
// set it for the next step
x_old = x;
}
if (cvm::debug())
cvm::log ("Done updating colvar \""+this->name+"\".\n");
return (potential_energy + kinetic_energy);
}
void colvar::communicate_forces()
{
if (cvm::debug())
cvm::log ("Communicating forces from colvar \""+this->name+"\".\n");
if (x.type() == colvarvalue::type_scalar) {
for (size_t i = 0; i < cvcs.size(); i++) {
cvm::increase_depth();
(cvcs[i])->apply_force (f * (cvcs[i])->sup_coeff *
cvm::real ((cvcs[i])->sup_np) *
(std::pow ((cvcs[i])->value().real_value,
(cvcs[i])->sup_np-1)) );
cvm::decrease_depth();
}
} else {
for (size_t i = 0; i < cvcs.size(); i++) {
cvm::increase_depth();
(cvcs[i])->apply_force (f * (cvcs[i])->sup_coeff);
cvm::decrease_depth();
}
}
if (cvm::debug())
cvm::log ("Done communicating forces from colvar \""+this->name+"\".\n");
}
// ******************** METRIC FUNCTIONS ********************
// Use the metrics defined by \link cvc \endlink objects
bool colvar::periodic_boundaries (colvarvalue const &lb, colvarvalue const &ub) const
{
if ( (!tasks[task_lower_boundary]) || (!tasks[task_upper_boundary]) ) {
cvm::fatal_error ("Error: requesting to histogram the "
"collective variable \""+this->name+"\", but a "
"pair of lower and upper boundaries must be "
"defined.\n");
}
if (period > 0.0) {
if ( ((std::sqrt (this->dist2 (lb, ub))) / this->width)
< 1.0E-10 ) {
return true;
}
}
return false;
}
bool colvar::periodic_boundaries() const
{
if ( (!tasks[task_lower_boundary]) || (!tasks[task_upper_boundary]) ) {
cvm::fatal_error ("Error: requesting to histogram the "
"collective variable \""+this->name+"\", but a "
"pair of lower and upper boundaries must be "
"defined.\n");
}
return periodic_boundaries (lower_boundary, upper_boundary);
}
cvm::real colvar::dist2 (colvarvalue const &x1,
colvarvalue const &x2) const
{
return (cvcs[0])->dist2 (x1, x2);
}
colvarvalue colvar::dist2_lgrad (colvarvalue const &x1,
colvarvalue const &x2) const
{
return (cvcs[0])->dist2_lgrad (x1, x2);
}
colvarvalue colvar::dist2_rgrad (colvarvalue const &x1,
colvarvalue const &x2) const
{
return (cvcs[0])->dist2_rgrad (x1, x2);
}
cvm::real colvar::compare (colvarvalue const &x1,
colvarvalue const &x2) const
{
return (cvcs[0])->compare (x1, x2);
}
void colvar::wrap (colvarvalue &x) const
{
(cvcs[0])->wrap (x);
return;
}
// ******************** INPUT FUNCTIONS ********************
std::istream & colvar::read_restart (std::istream &is)
{
size_t const start_pos = is.tellg();
std::string conf;
if ( !(is >> colvarparse::read_block ("colvar", conf)) ) {
// this is not a colvar block
is.clear();
is.seekg (start_pos, std::ios::beg);
is.setstate (std::ios::failbit);
return is;
}
{
std::string check_name = "";
if ( (get_keyval (conf, "name", check_name,
std::string (""), colvarparse::parse_silent)) &&
(check_name != name) ) {
cvm::fatal_error ("Error: the state file does not match the "
"configuration file, at colvar \""+name+"\".\n");
}
if (check_name.size() == 0) {
cvm::fatal_error ("Error: Collective variable in the "
"restart file without any identifier.\n");
}
}
if ( !(get_keyval (conf, "x", x,
colvarvalue (x.type()), colvarparse::parse_silent)) ) {
cvm::log ("Error: restart file does not contain "
"the value of the colvar \""+
name+"\" .\n");
} else {
cvm::log ("Restarting collective variable \""+name+"\" from value: "+
cvm::to_str (x)+"\n");
}
if (tasks[colvar::task_extended_lagrangian]) {
if ( !(get_keyval (conf, "extended_x", xr,
colvarvalue (x.type()), colvarparse::parse_silent)) &&
!(get_keyval (conf, "extended_v", vr,
colvarvalue (x.type()), colvarparse::parse_silent)) ) {
cvm::log ("Error: restart file does not contain "
"\"extended_x\" or \"extended_v\" for the colvar \""+
name+"\", but you requested \"extendedLagrangian\".\n");
}
}
if (tasks[task_extended_lagrangian]) {
x_reported = xr;
} else {
x_reported = x;
}
if (tasks[task_output_velocity]) {
if ( !(get_keyval (conf, "v", v_fdiff,
colvarvalue (x.type()), colvarparse::parse_silent)) ) {
cvm::log ("Error: restart file does not contain "
"the velocity for the colvar \""+
name+"\", but you requested \"outputVelocity\".\n");
}
if (tasks[task_extended_lagrangian]) {
v_reported = vr;
} else {
v_reported = v_fdiff;
}
}
return is;
}
std::istream & colvar::read_traj (std::istream &is)
{
size_t const start_pos = is.tellg();
if (tasks[task_output_value]) {
if (!(is >> x)) {
cvm::log ("Error: in reading the value of colvar \""+
this->name+"\" from trajectory.\n");
is.clear();
is.seekg (start_pos, std::ios::beg);
is.setstate (std::ios::failbit);
return is;
}
if (tasks[task_extended_lagrangian]) {
is >> xr;
x_reported = xr;
} else {
x_reported = x;
}
}
if (tasks[task_output_velocity]) {
is >> v_fdiff;
if (tasks[task_extended_lagrangian]) {
is >> vr;
v_reported = vr;
} else {
v_reported = v_fdiff;
}
}
if (tasks[task_output_system_force]) {
is >> ft;
if (tasks[task_extended_lagrangian]) {
is >> fr;
ft_reported = fr;
} else {
ft_reported = ft;
}
}
if (tasks[task_output_applied_force]) {
is >> f;
}
return is;
}
// ******************** OUTPUT FUNCTIONS ********************
std::ostream & colvar::write_restart (std::ostream &os) {
os << "colvar {\n"
<< " name " << name << "\n"
<< " x "
<< std::setprecision (cvm::cv_prec)
<< std::setw (cvm::cv_width)
<< x << "\n";
if (tasks[task_output_velocity]) {
os << " v "
<< std::setprecision (cvm::cv_prec)
<< std::setw (cvm::cv_width)
<< v_reported << "\n";
}
if (tasks[task_extended_lagrangian]) {
os << " extended_x "
<< std::setprecision (cvm::cv_prec)
<< std::setw (cvm::cv_width)
<< xr << "\n"
<< " extended_v "
<< std::setprecision (cvm::cv_prec)
<< std::setw (cvm::cv_width)
<< vr << "\n";
}
os << "}\n\n";
return os;
}
std::ostream & colvar::write_traj_label (std::ostream & os)
{
size_t const this_cv_width = x.output_width (cvm::cv_width);
os << " ";
if (tasks[task_output_value]) {
os << " "
<< cvm::wrap_string (this->name, this_cv_width);
if (tasks[task_extended_lagrangian]) {
// restraint center
os << " r_"
<< cvm::wrap_string (this->name, this_cv_width-2);
}
}
if (tasks[task_output_velocity]) {
os << " v_"
<< cvm::wrap_string (this->name, this_cv_width-2);
if (tasks[task_extended_lagrangian]) {
// restraint center
os << " vr_"
<< cvm::wrap_string (this->name, this_cv_width-3);
}
}
if (tasks[task_output_energy]) {
os << " Ep_"
<< cvm::wrap_string (this->name, this_cv_width-3)
<< " Ek_"
<< cvm::wrap_string (this->name, this_cv_width-3);
}
if (tasks[task_output_system_force]) {
os << " fs_"
<< cvm::wrap_string (this->name, this_cv_width-2);
if (tasks[task_extended_lagrangian]) {
// restraint center
os << " fr_"
<< cvm::wrap_string (this->name, this_cv_width-3);
}
}
if (tasks[task_output_applied_force]) {
os << " fa_"
<< cvm::wrap_string (this->name, this_cv_width-2);
}
return os;
}
std::ostream & colvar::write_traj (std::ostream &os)
{
os << " ";
if (tasks[task_output_value]) {
if (tasks[task_extended_lagrangian]) {
os << " "
<< std::setprecision (cvm::cv_prec) << std::setw (cvm::cv_width)
<< x;
}
os << " "
<< std::setprecision (cvm::cv_prec) << std::setw (cvm::cv_width)
<< x_reported;
}
if (tasks[task_output_velocity]) {
if (tasks[task_extended_lagrangian]) {
os << " "
<< std::setprecision (cvm::cv_prec) << std::setw (cvm::cv_width)
<< v_fdiff;
}
os << " "
<< std::setprecision (cvm::cv_prec) << std::setw (cvm::cv_width)
<< v_reported;
}
if (tasks[task_output_energy]) {
os << " "
<< std::setprecision (cvm::cv_prec) << std::setw (cvm::cv_width)
<< potential_energy
<< " "
<< kinetic_energy;
}
if (tasks[task_output_system_force]) {
if (tasks[task_extended_lagrangian]) {
os << " "
<< std::setprecision (cvm::cv_prec) << std::setw (cvm::cv_width)
<< ft;
}
os << " "
<< std::setprecision (cvm::cv_prec) << std::setw (cvm::cv_width)
<< ft_reported;
}
if (tasks[task_output_applied_force]) {
os << " "
<< std::setprecision (cvm::cv_prec) << std::setw (cvm::cv_width)
<< f;
}
return os;
}
void colvar::write_output_files()
{
if (cvm::b_analysis) {
if (acf.size()) {
cvm::log ("Writing acf to file \""+acf_outfile+"\".\n");
std::ofstream acf_os (acf_outfile.c_str());
if (! acf_os.good())
cvm::fatal_error ("Cannot open file \""+acf_outfile+"\".\n");
write_acf (acf_os);
acf_os.close();
}
if (runave_os.good()) {
runave_os.close();
}
}
}
// ******************** ANALYSIS FUNCTIONS ********************
void colvar::analyse()
{
if (tasks[task_runave]) {
calc_runave();
}
if (tasks[task_corrfunc]) {
calc_acf();
}
}
inline void history_add_value (size_t const &history_length,
std::list<colvarvalue> &history,
colvarvalue const &new_value)
{
history.push_front (new_value);
if (history.size() > history_length)
history.pop_back();
}
inline void history_incr (std::list< std::list<colvarvalue> > &history,
std::list< std::list<colvarvalue> >::iterator &history_p)
{
if ((++history_p) == history.end())
history_p = history.begin();
}
void colvar::calc_acf()
{
// using here an acf_stride-long list of vectors for either
// coordinates (acf_x_history) or velocities (acf_v_history); each vector can
// contain up to acf_length values, which are contiguous in memory
// representation but separated by acf_stride in the time series;
// the pointer to each vector is changed at every step
if (! (acf_x_history.size() || acf_v_history.size()) ) {
// first-step operations
colvar *cfcv = (acf_colvar_name.size() ?
cvm::colvar_p (acf_colvar_name) :
this);
if (cfcv->type() != this->type())
cvm::fatal_error ("Error: correlation function between \""+cfcv->name+
"\" and \""+this->name+"\" cannot be calculated, "
"because their value types are different.\n");
acf_nframes = 0;
cvm::log ("Colvar \""+this->name+"\": initializing ACF calculation.\n");
if (acf.size() < acf_length+1)
acf.resize (acf_length+1, 0.0);
switch (acf_type) {
case acf_vel:
// allocate space for the velocities history
for (size_t i = 0; i < acf_stride; i++) {
acf_v_history.push_back (std::list<colvarvalue>());
}
acf_v_history_p = acf_v_history.begin();
break;
case acf_coor:
case acf_p2coor:
// allocate space for the coordinates history
for (size_t i = 0; i < acf_stride; i++) {
acf_x_history.push_back (std::list<colvarvalue>());
}
acf_x_history_p = acf_x_history.begin();
break;
default:
break;
}
} else {
colvar *cfcv = (acf_colvar_name.size() ?
cvm::colvar_p (acf_colvar_name) :
this);
switch (acf_type) {
case acf_vel:
if (tasks[task_fdiff_velocity]) {
// calc() should do this already, but this only happens in a
// simulation; better do it again in case a trajectory is
// being read
v_reported = v_fdiff = fdiff_velocity (x_old, cfcv->value());
}
calc_vel_acf ((*acf_v_history_p), cfcv->velocity());
// store this value in the history
history_add_value (acf_length+acf_offset, *acf_v_history_p, cfcv->velocity());
// if stride is larger than one, cycle among different histories
history_incr (acf_v_history, acf_v_history_p);
break;
case acf_coor:
calc_coor_acf ((*acf_x_history_p), cfcv->value());
history_add_value (acf_length+acf_offset, *acf_x_history_p, cfcv->value());
history_incr (acf_x_history, acf_x_history_p);
break;
case acf_p2coor:
calc_p2coor_acf ((*acf_x_history_p), cfcv->value());
history_add_value (acf_length+acf_offset, *acf_x_history_p, cfcv->value());
history_incr (acf_x_history, acf_x_history_p);
break;
default:
break;
}
}
if (tasks[task_fdiff_velocity]) {
// set it for the next step
x_old = x;
}
}
void colvar::calc_vel_acf (std::list<colvarvalue> &v_list,
colvarvalue const &v)
{
// loop over stored velocities and add to the ACF, but only the
// length is sufficient to hold an entire row of ACF values
if (v_list.size() >= acf_length+acf_offset) {
std::list<colvarvalue>::iterator vs_i = v_list.begin();
std::vector<cvm::real>::iterator acf_i = acf.begin();
for (size_t i = 0; i < acf_offset; i++)
vs_i++;
// current vel with itself
*(acf_i++) += v.norm2();
// inner products of previous velocities with current (acf_i and
// vs_i are updated)
colvarvalue::inner_opt (v, vs_i, v_list.end(), acf_i);
acf_nframes++;
}
}
void colvar::calc_coor_acf (std::list<colvarvalue> &x_list,
colvarvalue const &x)
{
// same as above but for coordinates
if (x_list.size() >= acf_length+acf_offset) {
std::list<colvarvalue>::iterator xs_i = x_list.begin();
std::vector<cvm::real>::iterator acf_i = acf.begin();
for (size_t i = 0; i < acf_offset; i++)
xs_i++;
*(acf_i++) += x.norm2();
colvarvalue::inner_opt (x, xs_i, x_list.end(), acf_i);
acf_nframes++;
}
}
void colvar::calc_p2coor_acf (std::list<colvarvalue> &x_list,
colvarvalue const &x)
{
// same as above but with second order Legendre polynomial instead
// of just the scalar product
if (x_list.size() >= acf_length+acf_offset) {
std::list<colvarvalue>::iterator xs_i = x_list.begin();
std::vector<cvm::real>::iterator acf_i = acf.begin();
for (size_t i = 0; i < acf_offset; i++)
xs_i++;
// value of P2(0) = 1
*(acf_i++) += 1.0;
colvarvalue::p2leg_opt (x, xs_i, x_list.end(), acf_i);
acf_nframes++;
}
}
void colvar::write_acf (std::ostream &os)
{
if (!acf_nframes)
cvm::log ("Warning: ACF was not calculated (insufficient frames).\n");
os.setf (std::ios::scientific, std::ios::floatfield);
os << "# Autocorrelation function for collective variable \""
<< this->name << "\"\n";
// one frame is used for normalization, the statistical sample is
// hence decreased
os << "# nframes = " << (acf_normalize ?
acf_nframes - 1 :
acf_nframes) << "\n";
cvm::real const acf_norm = acf.front() / cvm::real (acf_nframes);
std::vector<cvm::real>::iterator acf_i;
size_t it = acf_offset;
for (acf_i = acf.begin(); acf_i != acf.end(); acf_i++) {
os << std::setw (cvm::it_width) << acf_stride * (it++) << " "
<< std::setprecision (cvm::cv_prec)
<< std::setw (cvm::cv_width)
<< ( acf_normalize ?
(*acf_i)/(acf_norm * cvm::real (acf_nframes)) :
(*acf_i)/(cvm::real (acf_nframes)) ) << "\n";
}
}
void colvar::calc_runave()
{
if (!x_history.size()) {
runave.type (x.type());
runave.reset();
// first-step operations
if (cvm::debug())
cvm::log ("Colvar \""+this->name+
"\": initializing running average calculation.\n");
acf_nframes = 0;
x_history.push_back (std::list<colvarvalue>());
x_history_p = x_history.begin();
} else {
if ( (cvm::step_relative() % runave_stride) == 0) {
if ((*x_history_p).size() >= runave_length-1) {
runave = x;
for (std::list<colvarvalue>::iterator xs_i = (*x_history_p).begin();
xs_i != (*x_history_p).end(); xs_i++) {
runave += (*xs_i);
}
runave *= 1.0 / cvm::real (runave_length);
runave.apply_constraints();
runave_variance = 0.0;
runave_variance += this->dist2 (x, runave);
for (std::list<colvarvalue>::iterator xs_i = (*x_history_p).begin();
xs_i != (*x_history_p).end(); xs_i++) {
runave_variance += this->dist2 (x, (*xs_i));
}
runave_variance *= 1.0 / cvm::real (runave_length-1);
runave_os << std::setw (cvm::it_width) << cvm::step_relative()
<< " "
<< std::setprecision (cvm::cv_prec) << std::setw (cvm::cv_width)
<< runave << " "
<< std::setprecision (cvm::cv_prec) << std::setw (cvm::cv_width)
<< std::sqrt (runave_variance) << "\n";
}
history_add_value (runave_length, *x_history_p, x);
}
}
}