Files
lammps/lib/colvars/colvarbias_meta.cpp
Giacomo Fiorin f3cf407a21 Collected fixes and updates to Colvars library
This commit includes several fixes to moving restraints; also added is support
for runtime integration of 2D and 3D PMFs from ABF.

Mostly changes to existing member functions, with few additions in classes not
directly accessible by LAMMPS.  Also removed are calls to std::pow(), replaced
by a copy of MathSpecial::powint().

Relevant commits in Colvars repository:

7307b5c 2017-12-14 Doc improvements [Giacomo Fiorin]
7f86f37 2017-12-14 Allow K-changing restraints computing accumulated work; fix staged-k TI estimator [Giacomo Fiorin]
7c1c175 2017-12-14 Fix 1D ABF trying to do pABF [Jérôme Hénin]
b94aa7e 2017-11-16 Unify PMF output for 1D, 2D and 3D in ABF [Jérôme Hénin]
771a88f 2017-11-15 Poisson integration for all BC in 2d and 3d [Jérôme Hénin]
6af4d60 2017-12-01 Print message when issuing cv delete in VMD [Giacomo Fiorin]
4413972 2017-11-30 Check for homogeneous colvar to set it periodic [Jérôme Hénin]
95fe4b2 2017-11-06 Allow abf_integrate to start in bin with 1 sample [Jérôme Hénin]
06eea27 2017-10-23 Shorten a few constructs by using the power function [Giacomo Fiorin]
3165dfb 2017-10-20 Move includes of colvarproxy.h from headers to files [Giacomo Fiorin]
32a867b 2017-10-20 Add optimized powint function from LAMMPS headers [Giacomo Fiorin]
3ad070a 2017-10-20 Remove some unused includes, isolate calls to std::pow() [Giacomo Fiorin]
0aaf540 2017-10-20 Replace all calls to std::pow() where the exponent is not an integer [Giacomo Fiorin]
2018-02-23 08:34:53 -05:00

1886 lines
62 KiB
C++

// -*- c++ -*-
// This file is part of the Collective Variables module (Colvars).
// The original version of Colvars and its updates are located at:
// https://github.com/colvars/colvars
// Please update all Colvars source files before making any changes.
// If you wish to distribute your changes, please submit them to the
// Colvars repository at GitHub.
#include <iostream>
#include <sstream>
#include <fstream>
#include <iomanip>
#include <cmath>
#include <algorithm>
// used to set the absolute path of a replica file
#if defined(WIN32) && !defined(__CYGWIN__)
#include <direct.h>
#define CHDIR ::_chdir
#define GETCWD ::_getcwd
#define PATHSEP "\\"
#else
#include <unistd.h>
#define CHDIR ::chdir
#define GETCWD ::getcwd
#define PATHSEP "/"
#endif
#include "colvarmodule.h"
#include "colvarproxy.h"
#include "colvar.h"
#include "colvarbias_meta.h"
colvarbias_meta::colvarbias_meta(char const *key)
: colvarbias(key), colvarbias_ti(key)
{
new_hills_begin = hills.end();
hills_traj_os = NULL;
replica_hills_os = NULL;
}
int colvarbias_meta::init(std::string const &conf)
{
colvarbias::init(conf);
colvarbias_ti::init(conf);
enable(f_cvb_calc_pmf);
get_keyval(conf, "hillWeight", hill_weight, 0.0);
if (hill_weight > 0.0) {
enable(f_cvb_apply_force);
} else {
cvm::error("Error: hillWeight must be provided, and a positive number.\n", INPUT_ERROR);
}
get_keyval(conf, "newHillFrequency", new_hill_freq, 1000);
if (new_hill_freq > 0) {
enable(f_cvb_history_dependent);
}
get_keyval(conf, "hillWidth", hill_width, std::sqrt(2.0 * PI) / 2.0);
cvm::log("Half-widths of the Gaussian hills (sigma's):\n");
for (size_t i = 0; i < num_variables(); i++) {
cvm::log(variables(i)->name+std::string(": ")+
cvm::to_str(0.5 * variables(i)->width * hill_width));
}
{
bool b_replicas = false;
get_keyval(conf, "multipleReplicas", b_replicas, false);
if (b_replicas)
comm = multiple_replicas;
else
comm = single_replica;
}
// in all cases, the first replica is this bias itself
if (replicas.size() == 0) {
replicas.push_back(this);
}
get_keyval(conf, "useGrids", use_grids, true);
if (use_grids) {
get_keyval(conf, "gridsUpdateFrequency", grids_freq, new_hill_freq);
get_keyval(conf, "rebinGrids", rebin_grids, false);
expand_grids = false;
size_t i;
for (i = 0; i < num_variables(); i++) {
variables(i)->enable(f_cv_grid);
if (variables(i)->expand_boundaries) {
expand_grids = true;
cvm::log("Metadynamics bias \""+this->name+"\""+
((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+
": Will expand grids when the colvar \""+
variables(i)->name+"\" approaches its boundaries.\n");
}
}
get_keyval(conf, "keepHills", keep_hills, false);
if (! get_keyval(conf, "writeFreeEnergyFile", dump_fes, true))
get_keyval(conf, "dumpFreeEnergyFile", dump_fes, true, colvarparse::parse_silent);
if (get_keyval(conf, "saveFreeEnergyFile", dump_fes_save, false, colvarparse::parse_silent)) {
cvm::log("Option \"saveFreeEnergyFile\" is deprecated, "
"please use \"keepFreeEnergyFiles\" instead.");
}
get_keyval(conf, "keepFreeEnergyFiles", dump_fes_save, dump_fes_save);
hills_energy = new colvar_grid_scalar(colvars);
hills_energy_gradients = new colvar_grid_gradient(colvars);
} else {
rebin_grids = false;
keep_hills = false;
dump_fes = false;
dump_fes_save = false;
dump_replica_fes = false;
hills_energy = NULL;
hills_energy_gradients = NULL;
}
if (comm != single_replica) {
if (expand_grids)
cvm::fatal_error("Error: expandBoundaries is not supported when "
"using more than one replicas; please allocate "
"wide enough boundaries for each colvar"
"ahead of time.\n");
if (get_keyval(conf, "dumpPartialFreeEnergyFile", dump_replica_fes, false)) {
if (dump_replica_fes && (! dump_fes)) {
cvm::log("Enabling \"dumpFreeEnergyFile\".\n");
}
}
get_keyval(conf, "replicaID", replica_id, std::string(""));
if (!replica_id.size())
cvm::error("Error: replicaID must be defined "
"when using more than one replica.\n", INPUT_ERROR);
get_keyval(conf, "replicasRegistry",
replicas_registry_file,
(this->name+".replicas.registry.txt"));
get_keyval(conf, "replicaUpdateFrequency",
replica_update_freq, new_hill_freq);
if (keep_hills)
cvm::log("Warning: in metadynamics bias \""+this->name+"\""+
((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+
": keepHills with more than one replica can lead to a very "
"large amount of input/output and slow down your calculations. "
"Please consider disabling it.\n");
}
get_keyval(conf, "writeHillsTrajectory", b_hills_traj, false);
init_well_tempered_params(conf);
init_ebmeta_params(conf);
if (cvm::debug())
cvm::log("Done initializing the metadynamics bias \""+this->name+"\""+
((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+".\n");
return COLVARS_OK;
}
int colvarbias_meta::init_well_tempered_params(std::string const &conf)
{
// for well-tempered metadynamics
get_keyval(conf, "wellTempered", well_tempered, false);
get_keyval(conf, "biasTemperature", bias_temperature, -1.0);
if ((bias_temperature == -1.0) && well_tempered) {
cvm::fatal_error("Error: biasTemperature is not set.\n");
}
if (well_tempered) {
cvm::log("Well-tempered metadynamics is used.\n");
cvm::log("The bias temperature is "+cvm::to_str(bias_temperature)+".\n");
}
return COLVARS_OK;
}
int colvarbias_meta::init_ebmeta_params(std::string const &conf)
{
// for ebmeta
target_dist = NULL;
get_keyval(conf, "ebMeta", ebmeta, false);
if(ebmeta){
if (use_grids && expand_grids) {
cvm::fatal_error("Error: expandBoundaries is not supported with "
"ebMeta please allocate wide enough boundaries for "
"each colvar ahead of time and set targetdistfile "
"accordingly. \n");
}
target_dist = new colvar_grid_scalar();
target_dist->init_from_colvars(colvars);
get_keyval(conf, "targetdistfile", target_dist_file);
std::ifstream targetdiststream(target_dist_file.c_str());
target_dist->read_multicol(targetdiststream);
cvm::real min_val = target_dist->minimum_value();
if(min_val<0){
cvm::error("Error: Target distribution of ebMeta "
"has negative values!.\n", INPUT_ERROR);
}
cvm::real min_pos_val = target_dist->minimum_pos_value();
if(min_pos_val<=0){
cvm::error("Error: Target distribution of ebMeta has negative "
"or zero minimum positive value!.\n", INPUT_ERROR);
}
if(min_val==0){
cvm::log("WARNING: Target distribution has zero values.\n");
cvm::log("Zeros will be converted to the minimum positive value.\n");
target_dist->remove_zeros(min_pos_val);
}
// normalize target distribution and multiply by effective volume = exp(differential entropy)
target_dist->multiply_constant(1.0/target_dist->integral());
cvm::real volume = std::exp(target_dist->entropy());
target_dist->multiply_constant(volume);
get_keyval(conf, "ebMetaEquilSteps", ebmeta_equil_steps, 0);
}
return COLVARS_OK;
}
colvarbias_meta::~colvarbias_meta()
{
colvarbias_meta::clear_state_data();
if (replica_hills_os) {
cvm::proxy->close_output_stream(replica_hills_file);
replica_hills_os = NULL;
}
if (hills_traj_os) {
cvm::proxy->close_output_stream(hills_traj_file_name());
hills_traj_os = NULL;
}
if (target_dist) {
delete target_dist;
target_dist = NULL;
}
}
int colvarbias_meta::clear_state_data()
{
if (hills_energy) {
delete hills_energy;
hills_energy = NULL;
}
if (hills_energy_gradients) {
delete hills_energy_gradients;
hills_energy_gradients = NULL;
}
hills.clear();
hills_off_grid.clear();
return COLVARS_OK;
}
// **********************************************************************
// Hill management member functions
// **********************************************************************
std::list<colvarbias_meta::hill>::const_iterator
colvarbias_meta::create_hill(colvarbias_meta::hill const &h)
{
hill_iter const hills_end = hills.end();
hills.push_back(h);
if (new_hills_begin == hills_end) {
// if new_hills_begin is unset, set it for the first time
new_hills_begin = hills.end();
new_hills_begin--;
}
if (use_grids) {
// also add it to the list of hills that are off-grid, which may
// need to be computed analytically when the colvar returns
// off-grid
cvm::real const min_dist = hills_energy->bin_distance_from_boundaries(h.centers, true);
if (min_dist < (3.0 * std::floor(hill_width)) + 1.0) {
hills_off_grid.push_back(h);
}
}
// output to trajectory (if specified)
if (hills_traj_os) {
*hills_traj_os << (hills.back()).output_traj();
cvm::proxy->flush_output_stream(hills_traj_os);
}
has_data = true;
return hills.end();
}
std::list<colvarbias_meta::hill>::const_iterator
colvarbias_meta::delete_hill(hill_iter &h)
{
if (cvm::debug()) {
cvm::log("Deleting hill from the metadynamics bias \""+this->name+"\""+
((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+
", with step number "+
cvm::to_str(h->it)+(h->replica.size() ?
", replica id \""+h->replica :
"")+".\n");
}
if (use_grids && !hills_off_grid.empty()) {
for (hill_iter hoff = hills_off_grid.begin();
hoff != hills_off_grid.end(); hoff++) {
if (*h == *hoff) {
hills_off_grid.erase(hoff);
break;
}
}
}
if (hills_traj_os) {
// output to the trajectory
*hills_traj_os << "# DELETED this hill: "
<< (hills.back()).output_traj()
<< "\n";
cvm::proxy->flush_output_stream(hills_traj_os);
}
return hills.erase(h);
}
int colvarbias_meta::update()
{
int error_code = COLVARS_OK;
// update base class
error_code |= colvarbias::update();
// update the TI estimator (if defined)
error_code |= colvarbias_ti::update();
// update grid definition, if needed
error_code |= update_grid_params();
// add new biasing energy/forces
error_code |= update_bias();
// update grid content to reflect new bias
error_code |= update_grid_data();
if (comm != single_replica &&
(cvm::step_absolute() % replica_update_freq) == 0) {
// sync with the other replicas (if needed)
error_code |= replica_share();
}
error_code |= calc_energy();
error_code |= calc_forces();
return error_code;
}
int colvarbias_meta::update_grid_params()
{
if (use_grids) {
std::vector<int> curr_bin = hills_energy->get_colvars_index();
if (cvm::debug()) {
cvm::log("Metadynamics bias \""+this->name+"\""+
((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+
": current coordinates on the grid: "+
cvm::to_str(curr_bin)+".\n");
}
if (expand_grids) {
// first of all, expand the grids, if specified
bool changed_grids = false;
int const min_buffer =
(3 * (size_t) std::floor(hill_width)) + 1;
std::vector<int> new_sizes(hills_energy->sizes());
std::vector<colvarvalue> new_lower_boundaries(hills_energy->lower_boundaries);
std::vector<colvarvalue> new_upper_boundaries(hills_energy->upper_boundaries);
for (size_t i = 0; i < num_variables(); i++) {
if (! variables(i)->expand_boundaries)
continue;
cvm::real &new_lb = new_lower_boundaries[i].real_value;
cvm::real &new_ub = new_upper_boundaries[i].real_value;
int &new_size = new_sizes[i];
bool changed_lb = false, changed_ub = false;
if (!variables(i)->hard_lower_boundary)
if (curr_bin[i] < min_buffer) {
int const extra_points = (min_buffer - curr_bin[i]);
new_lb -= extra_points * variables(i)->width;
new_size += extra_points;
// changed offset in this direction => the pointer needs to
// be changed, too
curr_bin[i] += extra_points;
changed_lb = true;
cvm::log("Metadynamics bias \""+this->name+"\""+
((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+
": new lower boundary for colvar \""+
variables(i)->name+"\", at "+
cvm::to_str(new_lower_boundaries[i])+".\n");
}
if (!variables(i)->hard_upper_boundary)
if (curr_bin[i] > new_size - min_buffer - 1) {
int const extra_points = (curr_bin[i] - (new_size - 1) + min_buffer);
new_ub += extra_points * variables(i)->width;
new_size += extra_points;
changed_ub = true;
cvm::log("Metadynamics bias \""+this->name+"\""+
((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+
": new upper boundary for colvar \""+
variables(i)->name+"\", at "+
cvm::to_str(new_upper_boundaries[i])+".\n");
}
if (changed_lb || changed_ub)
changed_grids = true;
}
if (changed_grids) {
// map everything into new grids
colvar_grid_scalar *new_hills_energy =
new colvar_grid_scalar(*hills_energy);
colvar_grid_gradient *new_hills_energy_gradients =
new colvar_grid_gradient(*hills_energy_gradients);
// supply new boundaries to the new grids
new_hills_energy->lower_boundaries = new_lower_boundaries;
new_hills_energy->upper_boundaries = new_upper_boundaries;
new_hills_energy->setup(new_sizes, 0.0, 1);
new_hills_energy_gradients->lower_boundaries = new_lower_boundaries;
new_hills_energy_gradients->upper_boundaries = new_upper_boundaries;
new_hills_energy_gradients->setup(new_sizes, 0.0, num_variables());
new_hills_energy->map_grid(*hills_energy);
new_hills_energy_gradients->map_grid(*hills_energy_gradients);
delete hills_energy;
delete hills_energy_gradients;
hills_energy = new_hills_energy;
hills_energy_gradients = new_hills_energy_gradients;
curr_bin = hills_energy->get_colvars_index();
if (cvm::debug())
cvm::log("Coordinates on the new grid: "+
cvm::to_str(curr_bin)+".\n");
}
}
}
return COLVARS_OK;
}
int colvarbias_meta::update_bias()
{
// add a new hill if the required time interval has passed
if ((cvm::step_absolute() % new_hill_freq) == 0 &&
is_enabled(f_cvb_history_dependent)) {
if (cvm::debug()) {
cvm::log("Metadynamics bias \""+this->name+"\""+
((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+
": adding a new hill at step "+cvm::to_str(cvm::step_absolute())+".\n");
}
cvm::real hills_scale=1.0;
if (ebmeta) {
hills_scale *= 1.0/target_dist->value(target_dist->get_colvars_index());
if(cvm::step_absolute() <= long(ebmeta_equil_steps)) {
cvm::real const hills_lambda =
(cvm::real(long(ebmeta_equil_steps) - cvm::step_absolute())) /
(cvm::real(ebmeta_equil_steps));
hills_scale = hills_lambda + (1-hills_lambda)*hills_scale;
}
}
if (well_tempered) {
cvm::real hills_energy_sum_here = 0.0;
if (use_grids) {
std::vector<int> curr_bin = hills_energy->get_colvars_index();
hills_energy_sum_here = hills_energy->value(curr_bin);
} else {
calc_hills(new_hills_begin, hills.end(), hills_energy_sum_here);
}
hills_scale *= std::exp(-1.0*hills_energy_sum_here/(bias_temperature*cvm::boltzmann()));
}
switch (comm) {
case single_replica:
create_hill(hill(hill_weight*hills_scale, colvars, hill_width));
break;
case multiple_replicas:
create_hill(hill(hill_weight*hills_scale, colvars, hill_width, replica_id));
if (replica_hills_os) {
*replica_hills_os << hills.back();
} else {
return cvm::error("Error: in metadynamics bias \""+this->name+"\""+
((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+
" while writing hills for the other replicas.\n", FILE_ERROR);
}
break;
}
}
return COLVARS_OK;
}
int colvarbias_meta::update_grid_data()
{
if ((cvm::step_absolute() % grids_freq) == 0) {
// map the most recent gaussians to the grids
project_hills(new_hills_begin, hills.end(),
hills_energy, hills_energy_gradients);
new_hills_begin = hills.end();
// TODO: we may want to condense all into one replicas array,
// including "this" as the first element
if (comm == multiple_replicas) {
for (size_t ir = 0; ir < replicas.size(); ir++) {
replicas[ir]->project_hills(replicas[ir]->new_hills_begin,
replicas[ir]->hills.end(),
replicas[ir]->hills_energy,
replicas[ir]->hills_energy_gradients);
replicas[ir]->new_hills_begin = replicas[ir]->hills.end();
}
}
}
return COLVARS_OK;
}
int colvarbias_meta::calc_energy(std::vector<colvarvalue> const &values)
{
size_t ir = 0;
for (ir = 0; ir < replicas.size(); ir++) {
replicas[ir]->bias_energy = 0.0;
}
std::vector<int> const curr_bin = values.size() ?
hills_energy->get_colvars_index(values) :
hills_energy->get_colvars_index();
if (hills_energy->index_ok(curr_bin)) {
// index is within the grid: get the energy from there
for (ir = 0; ir < replicas.size(); ir++) {
bias_energy += replicas[ir]->hills_energy->value(curr_bin);
if (cvm::debug()) {
cvm::log("Metadynamics bias \""+this->name+"\""+
((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+
": current coordinates on the grid: "+
cvm::to_str(curr_bin)+".\n");
cvm::log("Grid energy = "+cvm::to_str(bias_energy)+".\n");
}
}
} else {
// off the grid: compute analytically only the hills at the grid's edges
for (ir = 0; ir < replicas.size(); ir++) {
calc_hills(replicas[ir]->hills_off_grid.begin(),
replicas[ir]->hills_off_grid.end(),
bias_energy,
values);
}
}
// now include the hills that have not been binned yet (starting
// from new_hills_begin)
for (ir = 0; ir < replicas.size(); ir++) {
calc_hills(replicas[ir]->new_hills_begin,
replicas[ir]->hills.end(),
bias_energy);
if (cvm::debug()) {
cvm::log("Hills energy = "+cvm::to_str(bias_energy)+".\n");
}
}
return COLVARS_OK;
}
int colvarbias_meta::calc_forces(std::vector<colvarvalue> const &values)
{
size_t ir = 0, ic = 0;
for (ir = 0; ir < replicas.size(); ir++) {
for (ic = 0; ic < num_variables(); ic++) {
replicas[ir]->colvar_forces[ic].reset();
}
}
std::vector<int> const curr_bin = values.size() ?
hills_energy->get_colvars_index(values) :
hills_energy->get_colvars_index();
if (hills_energy->index_ok(curr_bin)) {
for (ir = 0; ir < replicas.size(); ir++) {
cvm::real const *f = &(replicas[ir]->hills_energy_gradients->value(curr_bin));
for (ic = 0; ic < num_variables(); ic++) {
// the gradients are stored, not the forces
colvar_forces[ic].real_value += -1.0 * f[ic];
}
}
} else {
// off the grid: compute analytically only the hills at the grid's edges
for (ir = 0; ir < replicas.size(); ir++) {
for (ic = 0; ic < num_variables(); ic++) {
calc_hills_force(ic,
replicas[ir]->hills_off_grid.begin(),
replicas[ir]->hills_off_grid.end(),
colvar_forces,
values);
}
}
}
// now include the hills that have not been binned yet (starting
// from new_hills_begin)
if (cvm::debug()) {
cvm::log("Metadynamics bias \""+this->name+"\""+
((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+
": adding the forces from the other replicas.\n");
}
for (ir = 0; ir < replicas.size(); ir++) {
for (ic = 0; ic < num_variables(); ic++) {
calc_hills_force(ic,
replicas[ir]->new_hills_begin,
replicas[ir]->hills.end(),
colvar_forces,
values);
if (cvm::debug()) {
cvm::log("Hills forces = "+cvm::to_str(colvar_forces)+".\n");
}
}
}
return COLVARS_OK;
}
void colvarbias_meta::calc_hills(colvarbias_meta::hill_iter h_first,
colvarbias_meta::hill_iter h_last,
cvm::real &energy,
std::vector<colvarvalue> const &colvar_values)
{
size_t i = 0;
std::vector<colvarvalue> curr_values(num_variables());
for (i = 0; i < num_variables(); i++) {
curr_values[i].type(variables(i)->value());
}
if (colvar_values.size()) {
for (i = 0; i < num_variables(); i++) {
curr_values[i] = colvar_values[i];
}
} else {
for (i = 0; i < num_variables(); i++) {
curr_values[i] = variables(i)->value();
}
}
for (hill_iter h = h_first; h != h_last; h++) {
// compute the gaussian exponent
cvm::real cv_sqdev = 0.0;
for (i = 0; i < num_variables(); i++) {
colvarvalue const &x = curr_values[i];
colvarvalue const &center = h->centers[i];
cvm::real const half_width = 0.5 * h->widths[i];
cv_sqdev += (variables(i)->dist2(x, center)) / (half_width*half_width);
}
// compute the gaussian
if (cv_sqdev > 23.0) {
// set it to zero if the exponent is more negative than log(1.0E-05)
h->value(0.0);
} else {
h->value(std::exp(-0.5*cv_sqdev));
}
energy += h->energy();
}
}
void colvarbias_meta::calc_hills_force(size_t const &i,
colvarbias_meta::hill_iter h_first,
colvarbias_meta::hill_iter h_last,
std::vector<colvarvalue> &forces,
std::vector<colvarvalue> const &values)
{
// Retrieve the value of the colvar
colvarvalue const x(values.size() ? values[i] : variables(i)->value());
// do the type check only once (all colvarvalues in the hills series
// were already saved with their types matching those in the
// colvars)
hill_iter h;
switch (variables(i)->value().type()) {
case colvarvalue::type_scalar:
for (h = h_first; h != h_last; h++) {
if (h->value() == 0.0) continue;
colvarvalue const &center = h->centers[i];
cvm::real const half_width = 0.5 * h->widths[i];
forces[i].real_value +=
( h->weight() * h->value() * (0.5 / (half_width*half_width)) *
(variables(i)->dist2_lgrad(x, center)).real_value );
}
break;
case colvarvalue::type_3vector:
case colvarvalue::type_unit3vector:
case colvarvalue::type_unit3vectorderiv:
for (h = h_first; h != h_last; h++) {
if (h->value() == 0.0) continue;
colvarvalue const &center = h->centers[i];
cvm::real const half_width = 0.5 * h->widths[i];
forces[i].rvector_value +=
( h->weight() * h->value() * (0.5 / (half_width*half_width)) *
(variables(i)->dist2_lgrad(x, center)).rvector_value );
}
break;
case colvarvalue::type_quaternion:
case colvarvalue::type_quaternionderiv:
for (h = h_first; h != h_last; h++) {
if (h->value() == 0.0) continue;
colvarvalue const &center = h->centers[i];
cvm::real const half_width = 0.5 * h->widths[i];
forces[i].quaternion_value +=
( h->weight() * h->value() * (0.5 / (half_width*half_width)) *
(variables(i)->dist2_lgrad(x, center)).quaternion_value );
}
break;
case colvarvalue::type_vector:
for (h = h_first; h != h_last; h++) {
if (h->value() == 0.0) continue;
colvarvalue const &center = h->centers[i];
cvm::real const half_width = 0.5 * h->widths[i];
forces[i].vector1d_value +=
( h->weight() * h->value() * (0.5 / (half_width*half_width)) *
(variables(i)->dist2_lgrad(x, center)).vector1d_value );
}
break;
case colvarvalue::type_notset:
case colvarvalue::type_all:
default:
break;
}
}
// **********************************************************************
// grid management functions
// **********************************************************************
void colvarbias_meta::project_hills(colvarbias_meta::hill_iter h_first,
colvarbias_meta::hill_iter h_last,
colvar_grid_scalar *he,
colvar_grid_gradient *hg,
bool print_progress)
{
if (cvm::debug())
cvm::log("Metadynamics bias \""+this->name+"\""+
((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+
": projecting hills.\n");
// TODO: improve it by looping over a small subgrid instead of the whole grid
std::vector<colvarvalue> colvar_values(num_variables());
std::vector<cvm::real> colvar_forces_scalar(num_variables());
std::vector<int> he_ix = he->new_index();
std::vector<int> hg_ix = (hg != NULL) ? hg->new_index() : std::vector<int> (0);
cvm::real hills_energy_here = 0.0;
std::vector<colvarvalue> hills_forces_here(num_variables(), 0.0);
size_t count = 0;
size_t const print_frequency = ((hills.size() >= 1000000) ? 1 : (1000000/(hills.size()+1)));
if (hg != NULL) {
// loop over the points of the grid
for ( ;
(he->index_ok(he_ix)) && (hg->index_ok(hg_ix));
count++) {
size_t i;
for (i = 0; i < num_variables(); i++) {
colvar_values[i] = hills_energy->bin_to_value_scalar(he_ix[i], i);
}
// loop over the hills and increment the energy grid locally
hills_energy_here = 0.0;
calc_hills(h_first, h_last, hills_energy_here, colvar_values);
he->acc_value(he_ix, hills_energy_here);
for (i = 0; i < num_variables(); i++) {
hills_forces_here[i].reset();
calc_hills_force(i, h_first, h_last, hills_forces_here, colvar_values);
colvar_forces_scalar[i] = hills_forces_here[i].real_value;
}
hg->acc_force(hg_ix, &(colvar_forces_scalar.front()));
he->incr(he_ix);
hg->incr(hg_ix);
if ((count % print_frequency) == 0) {
if (print_progress) {
cvm::real const progress = cvm::real(count) / cvm::real(hg->number_of_points());
std::ostringstream os;
os.setf(std::ios::fixed, std::ios::floatfield);
os << std::setw(6) << std::setprecision(2)
<< 100.0 * progress
<< "% done.";
cvm::log(os.str());
}
}
}
} else {
// simpler version, with just the energy
for ( ; (he->index_ok(he_ix)); ) {
for (size_t i = 0; i < num_variables(); i++) {
colvar_values[i] = hills_energy->bin_to_value_scalar(he_ix[i], i);
}
hills_energy_here = 0.0;
calc_hills(h_first, h_last, hills_energy_here, colvar_values);
he->acc_value(he_ix, hills_energy_here);
he->incr(he_ix);
count++;
if ((count % print_frequency) == 0) {
if (print_progress) {
cvm::real const progress = cvm::real(count) / cvm::real(he->number_of_points());
std::ostringstream os;
os.setf(std::ios::fixed, std::ios::floatfield);
os << std::setw(6) << std::setprecision(2)
<< 100.0 * progress
<< "% done.";
cvm::log(os.str());
}
}
}
}
if (print_progress) {
cvm::log("100.00% done.");
}
if (! keep_hills) {
hills.erase(hills.begin(), hills.end());
}
}
void colvarbias_meta::recount_hills_off_grid(colvarbias_meta::hill_iter h_first,
colvarbias_meta::hill_iter h_last,
colvar_grid_scalar *he)
{
hills_off_grid.clear();
for (hill_iter h = h_first; h != h_last; h++) {
cvm::real const min_dist = hills_energy->bin_distance_from_boundaries(h->centers, true);
if (min_dist < (3.0 * std::floor(hill_width)) + 1.0) {
hills_off_grid.push_back(*h);
}
}
}
// **********************************************************************
// multiple replicas functions
// **********************************************************************
int colvarbias_meta::replica_share()
{
// sync with the other replicas (if needed)
if (comm == multiple_replicas) {
// reread the replicas registry
update_replicas_registry();
// empty the output buffer
if (replica_hills_os) {
cvm::proxy->flush_output_stream(replica_hills_os);
}
read_replica_files();
}
return COLVARS_OK;
}
void colvarbias_meta::update_replicas_registry()
{
if (cvm::debug())
cvm::log("Metadynamics bias \""+this->name+"\""+
": updating the list of replicas, currently containing "+
cvm::to_str(replicas.size())+" elements.\n");
{
// copy the whole file into a string for convenience
std::string line("");
std::ifstream reg_file(replicas_registry_file.c_str());
if (reg_file.is_open()) {
replicas_registry.clear();
while (colvarparse::getline_nocomments(reg_file, line))
replicas_registry.append(line+"\n");
} else {
cvm::error("Error: failed to open file \""+replicas_registry_file+
"\" for reading.\n", FILE_ERROR);
}
}
// now parse it
std::istringstream reg_is(replicas_registry);
if (reg_is.good()) {
std::string new_replica("");
std::string new_replica_file("");
while ((reg_is >> new_replica) && new_replica.size() &&
(reg_is >> new_replica_file) && new_replica_file.size()) {
if (new_replica == this->replica_id) {
// this is the record for this same replica, skip it
if (cvm::debug())
cvm::log("Metadynamics bias \""+this->name+"\""+
((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+
": skipping this replica's own record: \""+
new_replica+"\", \""+new_replica_file+"\"\n");
new_replica_file.clear();
new_replica.clear();
continue;
}
bool already_loaded = false;
for (size_t ir = 0; ir < replicas.size(); ir++) {
if (new_replica == (replicas[ir])->replica_id) {
// this replica was already added
if (cvm::debug())
cvm::log("Metadynamics bias \""+this->name+"\""+
((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+
": skipping a replica already loaded, \""+
(replicas[ir])->replica_id+"\".\n");
already_loaded = true;
break;
}
}
if (!already_loaded) {
// add this replica to the registry
cvm::log("Metadynamics bias \""+this->name+"\""+
": accessing replica \""+new_replica+"\".\n");
replicas.push_back(new colvarbias_meta("metadynamics"));
(replicas.back())->replica_id = new_replica;
(replicas.back())->replica_list_file = new_replica_file;
(replicas.back())->replica_state_file = "";
(replicas.back())->replica_state_file_in_sync = false;
// Note: the following could become a copy constructor?
(replicas.back())->name = this->name;
(replicas.back())->colvars = colvars;
(replicas.back())->use_grids = use_grids;
(replicas.back())->dump_fes = false;
(replicas.back())->expand_grids = false;
(replicas.back())->rebin_grids = false;
(replicas.back())->keep_hills = false;
(replicas.back())->colvar_forces = colvar_forces;
(replicas.back())->comm = multiple_replicas;
if (use_grids) {
(replicas.back())->hills_energy = new colvar_grid_scalar(colvars);
(replicas.back())->hills_energy_gradients = new colvar_grid_gradient(colvars);
}
if (is_enabled(f_cvb_calc_ti_samples)) {
(replicas.back())->enable(f_cvb_calc_ti_samples);
(replicas.back())->colvarbias_ti::init_grids();
}
}
}
} else {
cvm::fatal_error("Error: cannot read the replicas registry file \""+
replicas_registry+"\".\n");
}
// now (re)read the list file of each replica
for (size_t ir = 0; ir < replicas.size(); ir++) {
if (cvm::debug())
cvm::log("Metadynamics bias \""+this->name+"\""+
": reading the list file for replica \""+
(replicas[ir])->replica_id+"\".\n");
std::ifstream list_is((replicas[ir])->replica_list_file.c_str());
std::string key;
std::string new_state_file, new_hills_file;
if (!(list_is >> key) ||
!(key == std::string("stateFile")) ||
!(list_is >> new_state_file) ||
!(list_is >> key) ||
!(key == std::string("hillsFile")) ||
!(list_is >> new_hills_file)) {
cvm::log("Metadynamics bias \""+this->name+"\""+
": failed to read the file \""+
(replicas[ir])->replica_list_file+"\": will try again after "+
cvm::to_str(replica_update_freq)+" steps.\n");
(replicas[ir])->update_status++;
} else {
(replicas[ir])->update_status = 0;
if (new_state_file != (replicas[ir])->replica_state_file) {
cvm::log("Metadynamics bias \""+this->name+"\""+
": replica \""+(replicas[ir])->replica_id+
"\" has supplied a new state file, \""+new_state_file+
"\".\n");
(replicas[ir])->replica_state_file_in_sync = false;
(replicas[ir])->replica_state_file = new_state_file;
(replicas[ir])->replica_hills_file = new_hills_file;
}
}
}
if (cvm::debug())
cvm::log("Metadynamics bias \""+this->name+"\": the list of replicas contains "+
cvm::to_str(replicas.size())+" elements.\n");
}
void colvarbias_meta::read_replica_files()
{
// Note: we start from the 2nd replica.
for (size_t ir = 1; ir < replicas.size(); ir++) {
if (! (replicas[ir])->replica_state_file_in_sync) {
// if a new state file is being read, the hills file is also new
(replicas[ir])->replica_hills_file_pos = 0;
}
// (re)read the state file if necessary
if ( (! (replicas[ir])->has_data) ||
(! (replicas[ir])->replica_state_file_in_sync) ) {
cvm::log("Metadynamics bias \""+this->name+"\""+
": reading the state of replica \""+
(replicas[ir])->replica_id+"\" from file \""+
(replicas[ir])->replica_state_file+"\".\n");
std::ifstream is((replicas[ir])->replica_state_file.c_str());
if (! (replicas[ir])->read_state(is)) {
cvm::log("Reading from file \""+(replicas[ir])->replica_state_file+
"\" failed or incomplete: will try again in "+
cvm::to_str(replica_update_freq)+" steps.\n");
} else {
// state file has been read successfully
(replicas[ir])->replica_state_file_in_sync = true;
(replicas[ir])->update_status = 0;
}
is.close();
}
// now read the hills added after writing the state file
if ((replicas[ir])->replica_hills_file.size()) {
if (cvm::debug())
cvm::log("Metadynamics bias \""+this->name+"\""+
": checking for new hills from replica \""+
(replicas[ir])->replica_id+"\" in the file \""+
(replicas[ir])->replica_hills_file+"\".\n");
// read hills from the other replicas' files; for each file, resume
// the position recorded previously
std::ifstream is((replicas[ir])->replica_hills_file.c_str());
if (is.is_open()) {
// try to resume the previous position
is.seekg((replicas[ir])->replica_hills_file_pos, std::ios::beg);
if (!is.is_open()){
// if fail (the file may have been overwritten), reset this
// position
is.clear();
is.seekg(0, std::ios::beg);
// reset the counter
(replicas[ir])->replica_hills_file_pos = 0;
// schedule to reread the state file
(replicas[ir])->replica_state_file_in_sync = false;
// and record the failure
(replicas[ir])->update_status++;
cvm::log("Failed to read the file \""+(replicas[ir])->replica_hills_file+
"\" at the previous position: will try again in "+
cvm::to_str(replica_update_freq)+" steps.\n");
} else {
while ((replicas[ir])->read_hill(is)) {
// if (cvm::debug())
cvm::log("Metadynamics bias \""+this->name+"\""+
": received a hill from replica \""+
(replicas[ir])->replica_id+
"\" at step "+
cvm::to_str(((replicas[ir])->hills.back()).it)+".\n");
}
is.clear();
// store the position for the next read
(replicas[ir])->replica_hills_file_pos = is.tellg();
if (cvm::debug())
cvm::log("Metadynamics bias \""+this->name+"\""+
": stopped reading file \""+(replicas[ir])->replica_hills_file+
"\" at position "+
cvm::to_str((replicas[ir])->replica_hills_file_pos)+".\n");
// test whether this is the end of the file
is.seekg(0, std::ios::end);
if (is.tellg() > (replicas[ir])->replica_hills_file_pos+1) {
(replicas[ir])->update_status++;
} else {
(replicas[ir])->update_status = 0;
}
}
} else {
cvm::log("Failed to read the file \""+(replicas[ir])->replica_hills_file+
"\": will try again in "+
cvm::to_str(replica_update_freq)+" steps.\n");
(replicas[ir])->update_status++;
// cvm::fatal_error ("Error: cannot read from file \""+
// (replicas[ir])->replica_hills_file+"\".\n");
}
is.close();
}
size_t const n_flush = (replica_update_freq/new_hill_freq + 1);
if ((replicas[ir])->update_status > 3*n_flush) {
// TODO: suspend the calculation?
cvm::log("WARNING: in metadynamics bias \""+this->name+"\""+
" failed to read completely the output of replica \""+
(replicas[ir])->replica_id+
"\" after more than "+
cvm::to_str((replicas[ir])->update_status * replica_update_freq)+
" steps. Ensure that it is still running.\n");
}
}
}
int colvarbias_meta::set_state_params(std::string const &state_conf)
{
std::string new_replica = "";
if (colvarparse::get_keyval(state_conf, "replicaID", new_replica,
std::string(""), colvarparse::parse_silent) &&
(new_replica != this->replica_id)) {
cvm::error("Error: in the state file, the "
"\"metadynamics\" block has a different replicaID: different system?\n",
INPUT_ERROR);
return INPUT_ERROR;
}
return COLVARS_OK;
}
std::istream & colvarbias_meta::read_state_data(std::istream& is)
{
bool grids_from_restart_file = use_grids;
if (use_grids) {
if (expand_grids) {
// the boundaries of the colvars may have been changed; TODO:
// this reallocation is only for backward-compatibility, and may
// be deleted when grid_parameters (i.e. colvargrid's own
// internal reallocation) has kicked in
delete hills_energy;
delete hills_energy_gradients;
hills_energy = new colvar_grid_scalar(colvars);
hills_energy_gradients = new colvar_grid_gradient(colvars);
}
colvar_grid_scalar *hills_energy_backup = NULL;
colvar_grid_gradient *hills_energy_gradients_backup = NULL;
if (has_data) {
if (cvm::debug())
cvm::log("Backupping grids for metadynamics bias \""+
this->name+"\""+
((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+".\n");
hills_energy_backup = hills_energy;
hills_energy_gradients_backup = hills_energy_gradients;
hills_energy = new colvar_grid_scalar(colvars);
hills_energy_gradients = new colvar_grid_gradient(colvars);
}
size_t const hills_energy_pos = is.tellg();
std::string key;
if (!(is >> key)) {
if (hills_energy_backup != NULL) {
delete hills_energy;
delete hills_energy_gradients;
hills_energy = hills_energy_backup;
hills_energy_gradients = hills_energy_gradients_backup;
}
is.clear();
is.seekg(hills_energy_pos, std::ios::beg);
is.setstate(std::ios::failbit);
return is;
} else if (!(key == std::string("hills_energy")) ||
!(hills_energy->read_restart(is))) {
is.clear();
is.seekg(hills_energy_pos, std::ios::beg);
grids_from_restart_file = false;
if (!rebin_grids) {
if (hills_energy_backup == NULL)
cvm::fatal_error("Error: couldn't read the free energy grid for metadynamics bias \""+
this->name+"\""+
((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+
"; if useGrids was off when the state file was written, "
"enable rebinGrids now to regenerate the grids.\n");
else {
if (comm == single_replica)
cvm::log("Error: couldn't read the free energy grid for metadynamics bias \""+
this->name+"\".\n");
delete hills_energy;
delete hills_energy_gradients;
hills_energy = hills_energy_backup;
hills_energy_gradients = hills_energy_gradients_backup;
is.setstate(std::ios::failbit);
return is;
}
}
}
size_t const hills_energy_gradients_pos = is.tellg();
if (!(is >> key)) {
if (hills_energy_backup != NULL) {
delete hills_energy;
delete hills_energy_gradients;
hills_energy = hills_energy_backup;
hills_energy_gradients = hills_energy_gradients_backup;
}
is.clear();
is.seekg(hills_energy_gradients_pos, std::ios::beg);
is.setstate(std::ios::failbit);
return is;
} else if (!(key == std::string("hills_energy_gradients")) ||
!(hills_energy_gradients->read_restart(is))) {
is.clear();
is.seekg(hills_energy_gradients_pos, std::ios::beg);
grids_from_restart_file = false;
if (!rebin_grids) {
if (hills_energy_backup == NULL)
cvm::fatal_error("Error: couldn't read the free energy gradients grid for metadynamics bias \""+
this->name+"\""+
((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+
"; if useGrids was off when the state file was written, "
"enable rebinGrids now to regenerate the grids.\n");
else {
if (comm == single_replica)
cvm::log("Error: couldn't read the free energy gradients grid for metadynamics bias \""+
this->name+"\".\n");
delete hills_energy;
delete hills_energy_gradients;
hills_energy = hills_energy_backup;
hills_energy_gradients = hills_energy_gradients_backup;
is.setstate(std::ios::failbit);
return is;
}
}
}
if (cvm::debug())
cvm::log("Successfully read new grids for bias \""+
this->name+"\""+
((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+"\n");
if (hills_energy_backup != NULL) {
// now that we have successfully updated the grids, delete the
// backup copies
if (cvm::debug())
cvm::log("Deallocating the older grids.\n");
delete hills_energy_backup;
delete hills_energy_gradients_backup;
}
}
bool const existing_hills = !hills.empty();
size_t const old_hills_size = hills.size();
hill_iter old_hills_end = hills.end();
hill_iter old_hills_off_grid_end = hills_off_grid.end();
// read the hills explicitly written (if there are any)
while (read_hill(is)) {
if (cvm::debug())
cvm::log("Read a previously saved hill under the "
"metadynamics bias \""+
this->name+"\", created at step "+
cvm::to_str((hills.back()).it)+".\n");
}
is.clear();
new_hills_begin = hills.end();
if (grids_from_restart_file) {
if (hills.size() > old_hills_size)
cvm::log("Read "+cvm::to_str(hills.size())+
" hills in addition to the grids.\n");
} else {
if (!hills.empty())
cvm::log("Read "+cvm::to_str(hills.size())+" hills.\n");
}
if (rebin_grids) {
// allocate new grids (based on the new boundaries and widths just
// read from the configuration file), and project onto them the
// grids just read from the restart file
colvar_grid_scalar *new_hills_energy =
new colvar_grid_scalar(colvars);
colvar_grid_gradient *new_hills_energy_gradients =
new colvar_grid_gradient(colvars);
if (!grids_from_restart_file || (keep_hills && !hills.empty())) {
// if there are hills, recompute the new grids from them
cvm::log("Rebinning the energy and forces grids from "+
cvm::to_str(hills.size())+" hills (this may take a while)...\n");
project_hills(hills.begin(), hills.end(),
new_hills_energy, new_hills_energy_gradients, true);
cvm::log("rebinning done.\n");
} else {
// otherwise, use the grids in the restart file
cvm::log("Rebinning the energy and forces grids "
"from the grids in the restart file.\n");
new_hills_energy->map_grid(*hills_energy);
new_hills_energy_gradients->map_grid(*hills_energy_gradients);
}
delete hills_energy;
delete hills_energy_gradients;
hills_energy = new_hills_energy;
hills_energy_gradients = new_hills_energy_gradients;
// assuming that some boundaries have expanded, eliminate those
// off-grid hills that aren't necessary any more
if (!hills.empty())
recount_hills_off_grid(hills.begin(), hills.end(), hills_energy);
}
if (use_grids) {
if (!hills_off_grid.empty()) {
cvm::log(cvm::to_str(hills_off_grid.size())+" hills are near the "
"grid boundaries: they will be computed analytically "
"and saved to the state files.\n");
}
}
colvarbias_ti::read_state_data(is);
if (cvm::debug())
cvm::log("colvarbias_meta::read_restart() done\n");
if (existing_hills) {
hills.erase(hills.begin(), old_hills_end);
hills_off_grid.erase(hills_off_grid.begin(), old_hills_off_grid_end);
}
has_data = true;
if (comm != single_replica) {
read_replica_files();
}
return is;
}
std::istream & colvarbias_meta::read_hill(std::istream &is)
{
if (!is) return is; // do nothing if failbit is set
size_t const start_pos = is.tellg();
std::string data;
if ( !(is >> read_block("hill", data)) ) {
is.clear();
is.seekg(start_pos, std::ios::beg);
is.setstate(std::ios::failbit);
return is;
}
size_t h_it;
get_keyval(data, "step", h_it, 0, parse_silent);
if (h_it <= state_file_step) {
if (cvm::debug())
cvm::log("Skipping a hill older than the state file for metadynamics bias \""+
this->name+"\""+
((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+"\n");
return is;
}
cvm::real h_weight;
get_keyval(data, "weight", h_weight, hill_weight, parse_silent);
std::vector<colvarvalue> h_centers(num_variables());
for (size_t i = 0; i < num_variables(); i++) {
h_centers[i].type(variables(i)->value());
}
{
// it is safer to read colvarvalue objects one at a time;
// TODO: change this it later
std::string centers_input;
key_lookup(data, "centers", &centers_input);
std::istringstream centers_is(centers_input);
for (size_t i = 0; i < num_variables(); i++) {
centers_is >> h_centers[i];
}
}
std::vector<cvm::real> h_widths(num_variables());
get_keyval(data, "widths", h_widths,
std::vector<cvm::real>(num_variables(), (std::sqrt(2.0 * PI) / 2.0)),
parse_silent);
std::string h_replica = "";
if (comm != single_replica) {
get_keyval(data, "replicaID", h_replica, replica_id, parse_silent);
if (h_replica != replica_id)
cvm::fatal_error("Error: trying to read a hill created by replica \""+h_replica+
"\" for replica \""+replica_id+
"\"; did you swap output files?\n");
}
hill_iter const hills_end = hills.end();
hills.push_back(hill(h_it, h_weight, h_centers, h_widths, h_replica));
if (new_hills_begin == hills_end) {
// if new_hills_begin is unset, set it for the first time
new_hills_begin = hills.end();
new_hills_begin--;
}
if (use_grids) {
// add this also to the list of hills that are off-grid, which will
// be computed analytically
cvm::real const min_dist =
hills_energy->bin_distance_from_boundaries((hills.back()).centers, true);
if (min_dist < (3.0 * std::floor(hill_width)) + 1.0) {
hills_off_grid.push_back(hills.back());
}
}
has_data = true;
return is;
}
int colvarbias_meta::setup_output()
{
output_prefix = cvm::output_prefix();
if (cvm::main()->num_biases_feature(colvardeps::f_cvb_calc_pmf) > 1) {
// if this is not the only free energy integrator, append
// this bias's name, to distinguish it from the output of the other
// biases producing a .pmf file
output_prefix += ("."+this->name);
}
if (comm == multiple_replicas) {
// TODO: one may want to specify the path manually for intricated filesystems?
char *pwd = new char[3001];
if (GETCWD(pwd, 3000) == NULL)
cvm::fatal_error("Error: cannot get the path of the current working directory.\n");
replica_list_file =
(std::string(pwd)+std::string(PATHSEP)+
this->name+"."+replica_id+".files.txt");
// replica_hills_file and replica_state_file are those written
// by the current replica; within the mirror biases, they are
// those by another replica
replica_hills_file =
(std::string(pwd)+std::string(PATHSEP)+
cvm::output_prefix()+".colvars."+this->name+"."+replica_id+".hills");
replica_state_file =
(std::string(pwd)+std::string(PATHSEP)+
cvm::output_prefix()+".colvars."+this->name+"."+replica_id+".state");
delete[] pwd;
// now register this replica
// first check that it isn't already there
bool registered_replica = false;
std::ifstream reg_is(replicas_registry_file.c_str());
if (reg_is.is_open()) { // the file may not be there yet
std::string existing_replica("");
std::string existing_replica_file("");
while ((reg_is >> existing_replica) && existing_replica.size() &&
(reg_is >> existing_replica_file) && existing_replica_file.size()) {
if (existing_replica == replica_id) {
// this replica was already registered
replica_list_file = existing_replica_file;
reg_is.close();
registered_replica = true;
break;
}
}
reg_is.close();
}
// if this replica was not included yet, we should generate a
// new record for it: but first, we write this replica's files,
// for the others to read
// open the "hills" buffer file
if (!replica_hills_os) {
cvm::proxy->backup_file(replica_hills_file);
replica_hills_os = cvm::proxy->output_stream(replica_hills_file);
if (!replica_hills_os) return cvm::get_error();
replica_hills_os->setf(std::ios::scientific, std::ios::floatfield);
}
// write the state file (so that there is always one available)
write_replica_state_file();
// schedule to read the state files of the other replicas
for (size_t ir = 0; ir < replicas.size(); ir++) {
(replicas[ir])->replica_state_file_in_sync = false;
}
// if we're running without grids, use a growing list of "hills" files
// otherwise, just one state file and one "hills" file as buffer
std::ostream *list_os =
cvm::proxy->output_stream(replica_list_file,
(use_grids ? std::ios_base::trunc :
std::ios_base::app));
if (!list_os) {
return cvm::get_error();
}
*list_os << "stateFile " << replica_state_file << "\n";
*list_os << "hillsFile " << replica_hills_file << "\n";
cvm::proxy->close_output_stream(replica_list_file);
// finally, add a new record for this replica to the registry
if (! registered_replica) {
std::ostream *reg_os =
cvm::proxy->output_stream(replicas_registry_file,
std::ios::app);
if (!reg_os) {
return cvm::get_error();
}
*reg_os << replica_id << " " << replica_list_file << "\n";
cvm::proxy->close_output_stream(replicas_registry_file);
}
}
if (b_hills_traj) {
if (!hills_traj_os) {
hills_traj_os = cvm::proxy->output_stream(hills_traj_file_name());
if (!hills_traj_os) return cvm::get_error();
}
}
return (cvm::get_error() ? COLVARS_ERROR : COLVARS_OK);
}
std::string const colvarbias_meta::hills_traj_file_name() const
{
return std::string(cvm::output_prefix()+
".colvars."+this->name+
( (comm != single_replica) ?
("."+replica_id) :
("") )+
".hills.traj");
}
std::string const colvarbias_meta::get_state_params() const
{
std::ostringstream os;
if (this->comm != single_replica)
os << "replicaID " << this->replica_id << "\n";
return (colvarbias::get_state_params() + os.str());
}
std::ostream & colvarbias_meta::write_state_data(std::ostream& os)
{
if (use_grids) {
// this is a very good time to project hills, if you haven't done
// it already!
project_hills(new_hills_begin, hills.end(),
hills_energy, hills_energy_gradients);
new_hills_begin = hills.end();
// write down the grids to the restart file
os << " hills_energy\n";
hills_energy->write_restart(os);
os << " hills_energy_gradients\n";
hills_energy_gradients->write_restart(os);
}
if ( (!use_grids) || keep_hills ) {
// write all hills currently in memory
for (std::list<hill>::const_iterator h = this->hills.begin();
h != this->hills.end();
h++) {
os << *h;
}
} else {
// write just those that are near the grid boundaries
for (std::list<hill>::const_iterator h = this->hills_off_grid.begin();
h != this->hills_off_grid.end();
h++) {
os << *h;
}
}
colvarbias_ti::write_state_data(os);
return os;
}
int colvarbias_meta::write_state_to_replicas()
{
if (comm != single_replica) {
write_replica_state_file();
// schedule to reread the state files of the other replicas (they
// have also rewritten them)
for (size_t ir = 0; ir < replicas.size(); ir++) {
(replicas[ir])->replica_state_file_in_sync = false;
}
}
return COLVARS_OK;
}
int colvarbias_meta::write_output_files()
{
colvarbias_ti::write_output_files();
if (dump_fes) {
write_pmf();
}
return COLVARS_OK;
}
void colvarbias_meta::write_pmf()
{
// allocate a new grid to store the pmf
colvar_grid_scalar *pmf = new colvar_grid_scalar(*hills_energy);
pmf->setup();
if ((comm == single_replica) || (dump_replica_fes)) {
// output the PMF from this instance or replica
pmf->reset();
pmf->add_grid(*hills_energy);
cvm::real const max = pmf->maximum_value();
pmf->add_constant(-1.0 * max);
pmf->multiply_constant(-1.0);
if (well_tempered) {
cvm::real const well_temper_scale = (bias_temperature + cvm::temperature()) / bias_temperature;
pmf->multiply_constant(well_temper_scale);
}
{
std::string const fes_file_name(this->output_prefix +
((comm != single_replica) ? ".partial" : "") +
(dump_fes_save ?
"."+cvm::to_str(cvm::step_absolute()) : "") +
".pmf");
cvm::proxy->backup_file(fes_file_name);
std::ostream *fes_os = cvm::proxy->output_stream(fes_file_name);
pmf->write_multicol(*fes_os);
cvm::proxy->close_output_stream(fes_file_name);
}
}
if (comm != single_replica) {
// output the combined PMF from all replicas
pmf->reset();
pmf->add_grid(*hills_energy);
for (size_t ir = 0; ir < replicas.size(); ir++) {
pmf->add_grid(*(replicas[ir]->hills_energy));
}
cvm::real const max = pmf->maximum_value();
pmf->add_constant(-1.0 * max);
pmf->multiply_constant(-1.0);
if (well_tempered) {
cvm::real const well_temper_scale = (bias_temperature + cvm::temperature()) / bias_temperature;
pmf->multiply_constant(well_temper_scale);
}
std::string const fes_file_name(this->output_prefix +
(dump_fes_save ?
"."+cvm::to_str(cvm::step_absolute()) : "") +
".pmf");
cvm::proxy->backup_file(fes_file_name);
std::ostream *fes_os = cvm::proxy->output_stream(fes_file_name);
pmf->write_multicol(*fes_os);
cvm::proxy->close_output_stream(fes_file_name);
}
delete pmf;
}
int colvarbias_meta::write_replica_state_file()
{
if (cvm::debug()) {
cvm::log("Writing replica state file for bias \""+name+"\"\n");
}
// write down also the restart for the other replicas
cvm::backup_file(replica_state_file.c_str());
std::ostream *rep_state_os = cvm::proxy->output_stream(replica_state_file);
if (rep_state_os == NULL) {
cvm::error("Error: in opening file \""+
replica_state_file+"\" for writing.\n", FILE_ERROR);
return FILE_ERROR;
}
rep_state_os->setf(std::ios::scientific, std::ios::floatfield);
if (!write_state(*rep_state_os)) {
cvm::error("Error: in writing to file \""+
replica_state_file+"\".\n", FILE_ERROR);
cvm::proxy->close_output_stream(replica_state_file);
return FILE_ERROR;
}
cvm::proxy->close_output_stream(replica_state_file);
// rep_state_os.setf(std::ios::scientific, std::ios::floatfield);
// rep_state_os << "\n"
// << "metadynamics {\n"
// << " configuration {\n"
// << " name " << this->name << "\n"
// << " step " << cvm::step_absolute() << "\n";
// if (this->comm != single_replica) {
// rep_state_os << " replicaID " << this->replica_id << "\n";
// }
// rep_state_os << " }\n\n";
// rep_state_os << " hills_energy\n";
// rep_state_os << std::setprecision(cvm::cv_prec)
// << std::setw(cvm::cv_width);
// hills_energy->write_restart(rep_state_os);
// rep_state_os << " hills_energy_gradients\n";
// rep_state_os << std::setprecision(cvm::cv_prec)
// << std::setw(cvm::cv_width);
// hills_energy_gradients->write_restart(rep_state_os);
// if ( (!use_grids) || keep_hills ) {
// // write all hills currently in memory
// for (std::list<hill>::const_iterator h = this->hills.begin();
// h != this->hills.end();
// h++) {
// rep_state_os << *h;
// }
// } else {
// // write just those that are near the grid boundaries
// for (std::list<hill>::const_iterator h = this->hills_off_grid.begin();
// h != this->hills_off_grid.end();
// h++) {
// rep_state_os << *h;
// }
// }
// rep_state_os << "}\n\n";
// rep_state_os.close();
// reopen the hills file
cvm::proxy->close_output_stream(replica_hills_file);
cvm::proxy->backup_file(replica_hills_file);
replica_hills_os = cvm::proxy->output_stream(replica_hills_file);
if (!replica_hills_os) return cvm::get_error();
replica_hills_os->setf(std::ios::scientific, std::ios::floatfield);
return COLVARS_OK;
}
std::string colvarbias_meta::hill::output_traj()
{
std::ostringstream os;
os.setf(std::ios::fixed, std::ios::floatfield);
os << std::setw(cvm::it_width) << it << " ";
os.setf(std::ios::scientific, std::ios::floatfield);
size_t i;
os << " ";
for (i = 0; i < centers.size(); i++) {
os << " ";
os << std::setprecision(cvm::cv_prec)
<< std::setw(cvm::cv_width) << centers[i];
}
os << " ";
for (i = 0; i < widths.size(); i++) {
os << " ";
os << std::setprecision(cvm::cv_prec)
<< std::setw(cvm::cv_width) << widths[i];
}
os << " ";
os << std::setprecision(cvm::en_prec)
<< std::setw(cvm::en_width) << W << "\n";
return os.str();
}
std::ostream & operator << (std::ostream &os, colvarbias_meta::hill const &h)
{
os.setf(std::ios::scientific, std::ios::floatfield);
os << "hill {\n";
os << " step " << std::setw(cvm::it_width) << h.it << "\n";
os << " weight "
<< std::setprecision(cvm::en_prec)
<< std::setw(cvm::en_width)
<< h.W << "\n";
if (h.replica.size())
os << " replicaID " << h.replica << "\n";
size_t i;
os << " centers ";
for (i = 0; i < (h.centers).size(); i++) {
os << " "
<< std::setprecision(cvm::cv_prec)
<< std::setw(cvm::cv_width)
<< h.centers[i];
}
os << "\n";
os << " widths ";
for (i = 0; i < (h.widths).size(); i++) {
os << " "
<< std::setprecision(cvm::cv_prec)
<< std::setw(cvm::cv_width)
<< h.widths[i];
}
os << "\n";
os << "}\n";
return os;
}