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]
This commit is contained in:
Giacomo Fiorin
2018-02-23 08:34:53 -05:00
parent 77efd3dfb3
commit f3cf407a21
28 changed files with 1776 additions and 431 deletions

View File

@ -195,7 +195,7 @@ int colvar::init(std::string const &conf)
// - it is homogeneous
// - all cvcs are periodic
// - all cvcs have the same period
if (cvcs[0]->b_periodic) { // TODO make this a CVC feature
if (is_enabled(f_cv_homogeneous) && cvcs[0]->b_periodic) { // TODO make this a CVC feature
bool b_periodic = true;
period = cvcs[0]->period;
for (i = 1; i < cvcs.size(); i++) {

View File

@ -11,8 +11,6 @@
#define COLVAR_H
#include <iostream>
#include <iomanip>
#include <cmath>
#include "colvarmodule.h"
#include "colvarvalue.h"
@ -61,6 +59,9 @@ public:
/// \brief Current actual value (not extended DOF)
colvarvalue const & actual_value() const;
/// \brief Current running average (if calculated as set by analysis flag)
colvarvalue const & run_ave() const;
/// \brief Force constant of the spring
cvm::real const & force_constant() const;
@ -516,7 +517,7 @@ public:
// collective variable component base class
class cvc;
// currently available collective variable components
// list of available collective variable components
// scalar colvar components
class distance;
@ -611,12 +612,15 @@ inline colvarvalue const & colvar::value() const
return x_reported;
}
inline colvarvalue const & colvar::actual_value() const
{
return x;
}
inline colvarvalue const & colvar::run_ave() const
{
return runave;
}
inline colvarvalue const & colvar::velocity() const
{

View File

@ -45,7 +45,7 @@ namespace UIestimator {
this->width = width;
this->dimension = lowerboundary.size();
this->y_size = y_size; // keep in mind the internal (spare) matrix is stored in diagonal form
this->y_total_size = int(pow(double(y_size), dimension) + EPSILON);
this->y_total_size = int(std::pow(double(y_size), double(dimension)) + EPSILON);
// the range of the matrix is [lowerboundary, upperboundary]
x_total_size = 1;
@ -121,7 +121,7 @@ namespace UIestimator {
int index = 0;
for (i = 0; i < dimension; i++) {
if (i + 1 < dimension)
index += temp[i] * int(pow(double(y_size), dimension - i - 1) + EPSILON);
index += temp[i] * int(std::pow(double(y_size), double(dimension - i - 1)) + EPSILON);
else
index += temp[i];
}

View File

@ -8,9 +8,11 @@
// Colvars repository at GitHub.
#include "colvarmodule.h"
#include "colvarproxy.h"
#include "colvarparse.h"
#include "colvaratoms.h"
cvm::atom::atom()
{
index = -1;

View File

@ -11,6 +11,7 @@
#define COLVARATOMS_H
#include "colvarmodule.h"
#include "colvarproxy.h"
#include "colvarparse.h"
#include "colvardeps.h"

View File

@ -8,6 +8,7 @@
// Colvars repository at GitHub.
#include "colvarmodule.h"
#include "colvarproxy.h"
#include "colvarvalue.h"
#include "colvarbias.h"
#include "colvargrid.h"

View File

@ -8,6 +8,7 @@
// Colvars repository at GitHub.
#include "colvarmodule.h"
#include "colvarproxy.h"
#include "colvar.h"
#include "colvarbias_abf.h"
@ -18,16 +19,18 @@ colvarbias_abf::colvarbias_abf(char const *key)
b_CZAR_estimator(false),
system_force(NULL),
gradients(NULL),
pmf(NULL),
samples(NULL),
z_gradients(NULL),
z_samples(NULL),
czar_gradients(NULL),
czar_pmf(NULL),
last_gradients(NULL),
last_samples(NULL)
last_samples(NULL),
pabf_freq(0)
{
}
int colvarbias_abf::init(std::string const &conf)
{
colvarbias::init(conf);
@ -91,7 +94,7 @@ int colvarbias_abf::init(std::string const &conf)
// ************* checking the associated colvars *******************
if (colvars.size() == 0) {
if (num_variables() == 0) {
cvm::error("Error: no collective variables specified for the ABF bias.\n");
return COLVARS_ERROR;
}
@ -102,7 +105,8 @@ int colvarbias_abf::init(std::string const &conf)
}
bool b_extended = false;
for (size_t i = 0; i < colvars.size(); i++) {
size_t i;
for (i = 0; i < num_variables(); i++) {
if (colvars[i]->value().type() != colvarvalue::type_scalar) {
cvm::error("Error: ABF bias can only use scalar-type variables.\n");
@ -132,10 +136,10 @@ int colvarbias_abf::init(std::string const &conf)
}
if (get_keyval(conf, "maxForce", max_force)) {
if (max_force.size() != colvars.size()) {
if (max_force.size() != num_variables()) {
cvm::error("Error: Number of parameters to maxForce does not match number of colvars.");
}
for (size_t i = 0; i < colvars.size(); i++) {
for (i = 0; i < num_variables(); i++) {
if (max_force[i] < 0.0) {
cvm::error("Error: maxForce should be non-negative.");
}
@ -145,9 +149,9 @@ int colvarbias_abf::init(std::string const &conf)
cap_force = false;
}
bin.assign(colvars.size(), 0);
force_bin.assign(colvars.size(), 0);
system_force = new cvm::real [colvars.size()];
bin.assign(num_variables(), 0);
force_bin.assign(num_variables(), 0);
system_force = new cvm::real [num_variables()];
// Construct empty grids based on the colvars
if (cvm::debug()) {
@ -159,14 +163,14 @@ int colvarbias_abf::init(std::string const &conf)
gradients->samples = samples;
samples->has_parent_data = true;
// Data for eABF z-based estimator
if (b_extended) {
// Data for eAB F z-based estimator
if ( b_extended ) {
get_keyval(conf, "CZARestimator", b_CZAR_estimator, true);
// CZAR output files for stratified eABF
get_keyval(conf, "writeCZARwindowFile", b_czar_window_file, false,
colvarparse::parse_silent);
z_bin.assign(colvars.size(), 0);
z_bin.assign(num_variables(), 0);
z_samples = new colvar_grid_count(colvars);
z_samples->request_actual_value();
z_gradients = new colvar_grid_gradient(colvars);
@ -176,6 +180,27 @@ int colvarbias_abf::init(std::string const &conf)
czar_gradients = new colvar_grid_gradient(colvars);
}
// For now, we integrate on-the-fly iff the grid is < 3D
if ( num_variables() <= 3 ) {
pmf = new integrate_potential(colvars, gradients);
if ( b_CZAR_estimator ) {
czar_pmf = new integrate_potential(colvars, czar_gradients);
}
get_keyval(conf, "integrate", b_integrate, true); // Integrate for output
if ( num_variables() > 1 ) {
// Projected ABF
get_keyval(conf, "pABFintegrateFreq", pabf_freq, 0);
// Parameters for integrating initial (and final) gradient data
get_keyval(conf, "integrateInitSteps", integrate_initial_steps, 1e4);
get_keyval(conf, "integrateInitTol", integrate_initial_tol, 1e-6);
// for updating the integrated PMF on the fly
get_keyval(conf, "integrateSteps", integrate_steps, 100);
get_keyval(conf, "integrateTol", integrate_tol, 1e-4);
}
} else {
b_integrate = false;
}
// For shared ABF, we store a second set of grids.
// This used to be only if "shared" was defined,
// but now we allow calling share externally (e.g. from Tcl).
@ -188,6 +213,8 @@ int colvarbias_abf::init(std::string const &conf)
// If custom grids are provided, read them
if ( input_prefix.size() > 0 ) {
read_gradients_samples();
// Update divergence to account for input data
pmf->set_div();
}
// if extendedLangrangian is on, then call UI estimator
@ -202,7 +229,7 @@ int colvarbias_abf::init(std::string const &conf)
bool UI_restart = (input_prefix.size() > 0);
for (size_t i = 0; i < colvars.size(); i++)
for (i = 0; i < num_variables(); i++)
{
UI_lowerboundary.push_back(colvars[i]->lower_boundary);
UI_upperboundary.push_back(colvars[i]->upper_boundary);
@ -238,6 +265,11 @@ colvarbias_abf::~colvarbias_abf()
gradients = NULL;
}
if (pmf) {
delete pmf;
pmf = NULL;
}
if (z_samples) {
delete z_samples;
z_samples = NULL;
@ -253,6 +285,11 @@ colvarbias_abf::~colvarbias_abf()
czar_gradients = NULL;
}
if (czar_pmf) {
delete czar_pmf;
czar_pmf = NULL;
}
// shared ABF
// We used to only do this if "shared" was defined,
// but now we can call shared externally
@ -278,44 +315,48 @@ colvarbias_abf::~colvarbias_abf()
int colvarbias_abf::update()
{
int iter;
if (cvm::debug()) cvm::log("Updating ABF bias " + this->name);
if (cvm::step_relative() == 0) {
// At first timestep, do only:
// initialization stuff (file operations relying on n_abf_biases
// compute current value of colvars
for (size_t i = 0; i < colvars.size(); i++) {
size_t i;
for (i = 0; i < num_variables(); i++) {
bin[i] = samples->current_bin_scalar(i);
}
} else {
for (size_t i = 0; i < colvars.size(); i++) {
bin[i] = samples->current_bin_scalar(i);
}
if ( update_bias && samples->index_ok(force_bin) ) {
// Only if requested and within bounds of the grid...
for (size_t i = 0; i < colvars.size(); i++) {
// get total forces (lagging by 1 timestep) from colvars
// and subtract previous ABF force if necessary
update_system_force(i);
}
if (cvm::proxy->total_forces_same_step()) {
// e.g. in LAMMPS, total forces are current
force_bin = bin;
}
gradients->acc_force(force_bin, system_force);
if (cvm::step_relative() > 0 || cvm::proxy->total_forces_same_step()) {
if (update_bias) {
// if (b_adiabatic_reweighting) {
// // Update gradients non-locally based on conditional distribution of
// // fictitious variable TODO
//
// } else
if (samples->index_ok(force_bin)) {
// Only if requested and within bounds of the grid...
for (i = 0; i < num_variables(); i++) {
// get total forces (lagging by 1 timestep) from colvars
// and subtract previous ABF force if necessary
update_system_force(i);
}
gradients->acc_force(force_bin, system_force);
if ( b_integrate ) {
pmf->update_div_neighbors(force_bin);
}
}
}
if ( z_gradients && update_bias ) {
for (size_t i = 0; i < colvars.size(); i++) {
for (i = 0; i < num_variables(); i++) {
z_bin[i] = z_samples->current_bin_scalar(i);
}
if ( z_samples->index_ok(z_bin) ) {
for (size_t i = 0; i < colvars.size(); i++) {
for (i = 0; i < num_variables(); i++) {
// If we are outside the range of xi, the force has not been obtained above
// the function is just an accessor, so cheap to call again anyway
update_system_force(i);
@ -323,6 +364,14 @@ int colvarbias_abf::update()
z_gradients->acc_force(z_bin, system_force);
}
}
if ( b_integrate ) {
if ( pabf_freq && cvm::step_relative() % pabf_freq == 0 ) {
cvm::real err;
iter = pmf->integrate(integrate_steps, integrate_tol, err);
pmf->set_zero_minimum(); // TODO: do this only when necessary
}
}
}
if (!cvm::proxy->total_forces_same_step()) {
@ -332,14 +381,14 @@ int colvarbias_abf::update()
}
// Reset biasing forces from previous timestep
for (size_t i = 0; i < colvars.size(); i++) {
for (i = 0; i < num_variables(); i++) {
colvar_forces[i].reset();
}
// Compute and apply the new bias, if applicable
if (is_enabled(f_cvb_apply_force) && samples->index_ok(bin)) {
size_t count = samples->value(bin);
cvm::real count = samples->value(bin);
cvm::real fact = 1.0;
// Factor that ensures smooth introduction of the force
@ -348,21 +397,34 @@ int colvarbias_abf::update()
(cvm::real(count - min_samples)) / (cvm::real(full_samples - min_samples));
}
const cvm::real * grad = &(gradients->value(bin));
std::vector<cvm::real> grad(num_variables());
if ( pabf_freq ) {
// In projected ABF, the force is the PMF gradient estimate
pmf->vector_gradient_finite_diff(bin, grad);
} else {
// Normal ABF
gradients->vector_value(bin, grad);
}
// if ( b_adiabatic_reweighting) {
// // Average of force according to conditional distribution of fictitious variable
// // need freshly integrated PMF, gradient TODO
// } else
if ( fact != 0.0 ) {
if ( (colvars.size() == 1) && colvars[0]->periodic_boundaries() ) {
if ( (num_variables() == 1) && colvars[0]->periodic_boundaries() ) {
// Enforce a zero-mean bias on periodic, 1D coordinates
// in other words: boundary condition is that the biasing potential is periodic
colvar_forces[0].real_value = fact * (grad[0] / cvm::real(count) - gradients->average());
// This is enforced naturally if using integrated PMF
colvar_forces[0].real_value = fact * (grad[0] - gradients->average ());
} else {
for (size_t i = 0; i < colvars.size(); i++) {
for (size_t i = 0; i < num_variables(); i++) {
// subtracting the mean force (opposite of the FE gradient) means adding the gradient
colvar_forces[i].real_value = fact * grad[i] / cvm::real(count);
colvar_forces[i].real_value = fact * grad[i];
}
}
if (cap_force) {
for (size_t i = 0; i < colvars.size(); i++) {
for (size_t i = 0; i < num_variables(); i++) {
if ( colvar_forces[i].real_value * colvar_forces[i].real_value > max_force[i] * max_force[i] ) {
colvar_forces[i].real_value = (colvar_forces[i].real_value > 0 ? max_force[i] : -1.0 * max_force[i]);
}
@ -407,9 +469,9 @@ int colvarbias_abf::update()
// update UI estimator every step
if (b_UI_estimator)
{
std::vector<double> x(colvars.size(),0);
std::vector<double> y(colvars.size(),0);
for (size_t i = 0; i < colvars.size(); i++)
std::vector<double> x(num_variables(),0);
std::vector<double> y(num_variables(),0);
for (size_t i = 0; i < num_variables(); i++)
{
x[i] = colvars[i]->actual_value();
y[i] = colvars[i]->value();
@ -509,26 +571,60 @@ void colvarbias_abf::write_gradients_samples(const std::string &prefix, bool app
cvm::proxy->output_stream(samples_out_name, mode);
if (!samples_os) {
cvm::error("Error opening ABF samples file " + samples_out_name + " for writing");
return;
}
samples->write_multicol(*samples_os);
cvm::proxy->close_output_stream(samples_out_name);
// In dimension higher than 2, dx is easier to handle and visualize
if (num_variables() > 2) {
std::string samples_dx_out_name = prefix + ".count.dx";
std::ostream *samples_dx_os = cvm::proxy->output_stream(samples_dx_out_name, mode);
if (!samples_os) {
cvm::error("Error opening samples file " + samples_dx_out_name + " for writing");
return;
}
samples->write_opendx(*samples_dx_os);
*samples_dx_os << std::endl;
cvm::proxy->close_output_stream(samples_dx_out_name);
}
std::ostream *gradients_os =
cvm::proxy->output_stream(gradients_out_name, mode);
if (!gradients_os) {
cvm::error("Error opening ABF gradient file " + gradients_out_name + " for writing");
return;
}
gradients->write_multicol(*gradients_os);
cvm::proxy->close_output_stream(gradients_out_name);
if (colvars.size() == 1) {
// Do numerical integration and output a PMF
if (b_integrate) {
// Do numerical integration (to high precision) and output a PMF
cvm::real err;
pmf->integrate(integrate_initial_steps, integrate_initial_tol, err);
pmf->set_zero_minimum();
std::string pmf_out_name = prefix + ".pmf";
std::ostream *pmf_os = cvm::proxy->output_stream(pmf_out_name, mode);
if (!pmf_os) {
cvm::error("Error opening pmf file " + pmf_out_name + " for writing");
return;
}
gradients->write_1D_integral(*pmf_os);
pmf->write_multicol(*pmf_os);
// In dimension higher than 2, dx is easier to handle and visualize
if (num_variables() > 2) {
std::string pmf_dx_out_name = prefix + ".pmf.dx";
std::ostream *pmf_dx_os = cvm::proxy->output_stream(pmf_dx_out_name, mode);
if (!pmf_dx_os) {
cvm::error("Error opening pmf file " + pmf_dx_out_name + " for writing");
return;
}
pmf->write_opendx(*pmf_dx_os);
*pmf_dx_os << std::endl;
cvm::proxy->close_output_stream(pmf_dx_out_name);
}
*pmf_os << std::endl;
cvm::proxy->close_output_stream(pmf_out_name);
}
@ -542,6 +638,7 @@ void colvarbias_abf::write_gradients_samples(const std::string &prefix, bool app
cvm::proxy->output_stream(z_samples_out_name, mode);
if (!z_samples_os) {
cvm::error("Error opening eABF z-histogram file " + z_samples_out_name + " for writing");
return;
}
z_samples->write_multicol(*z_samples_os);
cvm::proxy->close_output_stream(z_samples_out_name);
@ -553,6 +650,7 @@ void colvarbias_abf::write_gradients_samples(const std::string &prefix, bool app
cvm::proxy->output_stream(z_gradients_out_name, mode);
if (!z_gradients_os) {
cvm::error("Error opening eABF z-gradient file " + z_gradients_out_name + " for writing");
return;
}
z_gradients->write_multicol(*z_gradients_os);
cvm::proxy->close_output_stream(z_gradients_out_name);
@ -563,8 +661,7 @@ void colvarbias_abf::write_gradients_samples(const std::string &prefix, bool app
czar_gradients->index_ok(ix); czar_gradients->incr(ix)) {
for (size_t n = 0; n < czar_gradients->multiplicity(); n++) {
czar_gradients->set_value(ix, z_gradients->value_output(ix, n)
- cvm::temperature() * cvm::boltzmann() * z_samples->log_gradient_finite_diff(ix, n),
n);
- cvm::temperature() * cvm::boltzmann() * z_samples->log_gradient_finite_diff(ix, n), n);
}
}
@ -574,17 +671,39 @@ void colvarbias_abf::write_gradients_samples(const std::string &prefix, bool app
cvm::proxy->output_stream(czar_gradients_out_name, mode);
if (!czar_gradients_os) {
cvm::error("Error opening CZAR gradient file " + czar_gradients_out_name + " for writing");
return;
}
czar_gradients->write_multicol(*czar_gradients_os);
cvm::proxy->close_output_stream(czar_gradients_out_name);
if (colvars.size() == 1) {
// Do numerical integration and output a PMF
if (b_integrate) {
// Do numerical integration (to high precision) and output a PMF
cvm::real err;
czar_pmf->set_div();
czar_pmf->integrate(integrate_initial_steps, integrate_initial_tol, err);
czar_pmf->set_zero_minimum();
std::string czar_pmf_out_name = prefix + ".czar.pmf";
std::ostream *czar_pmf_os =
cvm::proxy->output_stream(czar_pmf_out_name, mode);
if (!czar_pmf_os) cvm::error("Error opening CZAR pmf file " + czar_pmf_out_name + " for writing");
czar_gradients->write_1D_integral(*czar_pmf_os);
std::ostream *czar_pmf_os = cvm::proxy->output_stream(czar_pmf_out_name, mode);
if (!czar_pmf_os) {
cvm::error("Error opening CZAR pmf file " + czar_pmf_out_name + " for writing");
return;
}
czar_pmf->write_multicol(*czar_pmf_os);
// In dimension higher than 2, dx is easier to handle and visualize
if (num_variables() > 2) {
std::string czar_pmf_dx_out_name = prefix + ".czar.pmf.dx";
std::ostream *czar_pmf_dx_os = cvm::proxy->output_stream(czar_pmf_dx_out_name, mode);
if (!czar_pmf_dx_os) {
cvm::error("Error opening CZAR pmf file " + czar_pmf_dx_out_name + " for writing");
return;
}
czar_pmf->write_opendx(*czar_pmf_dx_os);
*czar_pmf_dx_os << std::endl;
cvm::proxy->close_output_stream(czar_pmf_dx_out_name);
}
*czar_pmf_os << std::endl;
cvm::proxy->close_output_stream(czar_pmf_out_name);
}
@ -708,6 +827,10 @@ std::istream & colvarbias_abf::read_state_data(std::istream& is)
if (! gradients->read_raw(is)) {
return is;
}
if (b_integrate) {
// Update divergence to account for restart data
pmf->set_div();
}
if (b_CZAR_estimator) {

View File

@ -42,6 +42,8 @@ private:
bool update_bias;
bool hide_Jacobian;
bool b_integrate;
size_t full_samples;
size_t min_samples;
/// frequency for updating output files
@ -53,15 +55,29 @@ private:
size_t history_freq;
/// Umbrella Integration estimator of free energy from eABF
UIestimator::UIestimator eabf_UI;
// Run UI estimator?
/// Run UI estimator?
bool b_UI_estimator;
// Run CZAR estimator?
/// Run CZAR estimator?
bool b_CZAR_estimator;
/// Cap applied biasing force?
/// Frequency for updating pABF PMF (if zero, pABF is not used)
int pabf_freq;
/// Max number of CG iterations for integrating PMF at startup and for file output
int integrate_initial_steps;
/// Tolerance for integrating PMF at startup and for file output
cvm::real integrate_initial_tol;
/// Max number of CG iterations for integrating PMF at on-the-fly pABF updates
int integrate_steps;
/// Tolerance for integrating PMF at on-the-fly pABF updates
cvm::real integrate_tol;
/// Cap the biasing force to be applied?
bool cap_force;
std::vector<cvm::real> max_force;
// Frequency for updating 2D gradients
int integrate_freq;
// Internal data and methods
std::vector<int> bin, force_bin, z_bin;
@ -71,12 +87,16 @@ private:
colvar_grid_gradient *gradients;
/// n-dim grid of number of samples
colvar_grid_count *samples;
/// n-dim grid of pmf (dimension 1 to 3)
integrate_potential *pmf;
/// n-dim grid: average force on "real" coordinate for eABF z-based estimator
colvar_grid_gradient *z_gradients;
/// n-dim grid of number of samples on "real" coordinate for eABF z-based estimator
colvar_grid_count *z_samples;
/// n-dim grid contining CZAR estimator of "real" free energy gradients
colvar_grid_gradient *czar_gradients;
/// n-dim grid of CZAR pmf (dimension 1 to 3)
integrate_potential *czar_pmf;
inline int update_system_force(size_t i)
{
@ -114,7 +134,7 @@ private:
//// Give the count at a given bin index.
virtual int bin_count(int bin_index);
/// Write human-readable FE gradients and sample count
/// Write human-readable FE gradients and sample count, and DX file in dim > 2
void write_gradients_samples(const std::string &prefix, bool append = false);
void write_last_gradients_samples(const std::string &prefix, bool append = false);

View File

@ -7,13 +7,11 @@
// If you wish to distribute your changes, please submit them to the
// Colvars repository at GitHub.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <cstdlib>
#include "colvarmodule.h"
#include "colvarbias_alb.h"
#include "colvarbias.h"
#include "colvarbias_alb.h"
#ifdef _MSC_VER
#if _MSC_VER <= 1700
@ -45,22 +43,22 @@ int colvarbias_alb::init(std::string const &conf)
size_t i;
// get the initial restraint centers
colvar_centers.resize(colvars.size());
colvar_centers.resize(num_variables());
means.resize(colvars.size());
ssd.resize(colvars.size()); //sum of squares of differences from mean
means.resize(num_variables());
ssd.resize(num_variables()); //sum of squares of differences from mean
//setup force vectors
max_coupling_range.resize(colvars.size());
max_coupling_rate.resize(colvars.size());
coupling_accum.resize(colvars.size());
set_coupling.resize(colvars.size());
current_coupling.resize(colvars.size());
coupling_rate.resize(colvars.size());
max_coupling_range.resize(num_variables());
max_coupling_rate.resize(num_variables());
coupling_accum.resize(num_variables());
set_coupling.resize(num_variables());
current_coupling.resize(num_variables());
coupling_rate.resize(num_variables());
enable(f_cvb_apply_force);
for (i = 0; i < colvars.size(); i++) {
for (i = 0; i < num_variables(); i++) {
colvar_centers[i].type(colvars[i]->value());
//zero moments
means[i] = ssd[i] = 0;
@ -70,7 +68,7 @@ int colvarbias_alb::init(std::string const &conf)
}
if (get_keyval(conf, "centers", colvar_centers, colvar_centers)) {
for (i = 0; i < colvars.size(); i++) {
for (i = 0; i < num_variables(); i++) {
colvar_centers[i].apply_constraints();
}
} else {
@ -78,7 +76,7 @@ int colvarbias_alb::init(std::string const &conf)
cvm::fatal_error("Error: must define the initial centers of adaptive linear bias .\n");
}
if (colvar_centers.size() != colvars.size())
if (colvar_centers.size() != num_variables())
cvm::fatal_error("Error: number of centers does not match "
"that of collective variables.\n");
@ -100,17 +98,17 @@ int colvarbias_alb::init(std::string const &conf)
//initial guess
if (!get_keyval(conf, "forceConstant", set_coupling, set_coupling))
for (i =0 ; i < colvars.size(); i++)
for (i =0 ; i < num_variables(); i++)
set_coupling[i] = 0.;
//how we're going to increase to that point
for (i = 0; i < colvars.size(); i++)
for (i = 0; i < num_variables(); i++)
coupling_rate[i] = (set_coupling[i] - current_coupling[i]) / update_freq;
if (!get_keyval(conf, "forceRange", max_coupling_range, max_coupling_range)) {
//set to default
for (i = 0; i < colvars.size(); i++) {
for (i = 0; i < num_variables(); i++) {
if (cvm::temperature() > 0)
max_coupling_range[i] = 3 * cvm::temperature() * cvm::boltzmann();
else
@ -120,7 +118,7 @@ int colvarbias_alb::init(std::string const &conf)
if (!get_keyval(conf, "rateMax", max_coupling_rate, max_coupling_rate)) {
//set to default
for (i = 0; i < colvars.size(); i++) {
for (i = 0; i < num_variables(); i++) {
max_coupling_rate[i] = max_coupling_range[i] / (10 * update_freq);
}
}
@ -151,7 +149,7 @@ int colvarbias_alb::update()
// Force and energy calculation
bool finished_equil_flag = 1;
cvm::real delta;
for (size_t i = 0; i < colvars.size(); i++) {
for (size_t i = 0; i < num_variables(); i++) {
colvar_forces[i] = -1.0 * restraint_force(restraint_convert_k(current_coupling[i], colvars[i]->width),
colvars[i],
colvar_centers[i]);
@ -168,7 +166,9 @@ int colvarbias_alb::update()
} else {
//check if we've reached the setpoint
if (coupling_rate[i] == 0 || pow(current_coupling[i] - set_coupling[i],2) < pow(coupling_rate[i],2)) {
cvm::real const coupling_diff = current_coupling[i] - set_coupling[i];
if ((coupling_rate[i] == 0) ||
((coupling_diff*coupling_diff) < (coupling_rate[i]*coupling_rate[i]))) {
finished_equil_flag &= 1; //we continue equilibrating as long as we haven't reached all the set points
}
else {
@ -209,7 +209,7 @@ int colvarbias_alb::update()
cvm::real temp;
//reset means and sum of squares of differences
for (size_t i = 0; i < colvars.size(); i++) {
for (size_t i = 0; i < num_variables(); i++) {
temp = 2. * (means[i] / (static_cast<cvm::real> (colvar_centers[i])) - 1) * ssd[i] / (update_calls - 1);
@ -222,7 +222,7 @@ int colvarbias_alb::update()
ssd[i] = 0;
//stochastic if we do that update or not
if (colvars.size() == 1 || rand() < RAND_MAX / ((int) colvars.size())) {
if (num_variables() == 1 || rand() < RAND_MAX / ((int) num_variables())) {
coupling_accum[i] += step_size * step_size;
current_coupling[i] = set_coupling[i];
set_coupling[i] += max_coupling_range[i] / sqrt(coupling_accum[i]) * step_size;
@ -284,37 +284,37 @@ std::string const colvarbias_alb::get_state_params() const
std::ostringstream os;
os << " setCoupling ";
size_t i;
for (i = 0; i < colvars.size(); i++) {
for (i = 0; i < num_variables(); i++) {
os << std::setprecision(cvm::en_prec)
<< std::setw(cvm::en_width) << set_coupling[i] << "\n";
}
os << " currentCoupling ";
for (i = 0; i < colvars.size(); i++) {
for (i = 0; i < num_variables(); i++) {
os << std::setprecision(cvm::en_prec)
<< std::setw(cvm::en_width) << current_coupling[i] << "\n";
}
os << " maxCouplingRange ";
for (i = 0; i < colvars.size(); i++) {
for (i = 0; i < num_variables(); i++) {
os << std::setprecision(cvm::en_prec)
<< std::setw(cvm::en_width) << max_coupling_range[i] << "\n";
}
os << " couplingRate ";
for (i = 0; i < colvars.size(); i++) {
for (i = 0; i < num_variables(); i++) {
os << std::setprecision(cvm::en_prec)
<< std::setw(cvm::en_width) << coupling_rate[i] << "\n";
}
os << " couplingAccum ";
for (i = 0; i < colvars.size(); i++) {
for (i = 0; i < num_variables(); i++) {
os << std::setprecision(cvm::en_prec)
<< std::setw(cvm::en_width) << coupling_accum[i] << "\n";
}
os << " mean ";
for (i = 0; i < colvars.size(); i++) {
for (i = 0; i < num_variables(); i++) {
os << std::setprecision(cvm::en_prec)
<< std::setw(cvm::en_width) << means[i] << "\n";
}
os << " ssd ";
for (i = 0; i < colvars.size(); i++) {
for (i = 0; i < num_variables(); i++) {
os << std::setprecision(cvm::en_prec)
<< std::setw(cvm::en_width) << ssd[i] << "\n";
}
@ -350,7 +350,7 @@ std::ostream & colvarbias_alb::write_traj_label(std::ostream &os)
}
if (b_output_centers)
for (size_t i = 0; i < colvars.size(); i++) {
for (size_t i = 0; i < num_variables(); i++) {
size_t const this_cv_width = (colvars[i]->value()).output_width(cvm::cv_width);
os << " x0_"
<< cvm::wrap_string(colvars[i]->name, this_cv_width-3);
@ -378,7 +378,7 @@ std::ostream & colvarbias_alb::write_traj(std::ostream &os)
if (b_output_centers)
for (size_t i = 0; i < colvars.size(); i++) {
for (size_t i = 0; i < num_variables(); i++) {
os << " "
<< std::setprecision(cvm::cv_prec) << std::setw(cvm::cv_width)
<< colvar_centers[i];

View File

@ -8,10 +8,10 @@
// Colvars repository at GitHub.
#include "colvarmodule.h"
#include "colvarproxy.h"
#include "colvar.h"
#include "colvarbias_histogram.h"
/// Histogram "bias" constructor
colvarbias_histogram::colvarbias_histogram(char const *key)
: colvarbias(key),
@ -44,7 +44,7 @@ int colvarbias_histogram::init(std::string const &conf)
get_keyval(conf, "gatherVectorColvars", colvar_array, colvar_array);
if (colvar_array) {
for (i = 0; i < colvars.size(); i++) { // should be all vector
for (i = 0; i < num_variables(); i++) { // should be all vector
if (colvars[i]->value().type() != colvarvalue::type_vector) {
cvm::error("Error: used gatherVectorColvars with non-vector colvar.\n", INPUT_ERROR);
return INPUT_ERROR;
@ -63,7 +63,7 @@ int colvarbias_histogram::init(std::string const &conf)
}
}
} else {
for (i = 0; i < colvars.size(); i++) { // should be all scalar
for (i = 0; i < num_variables(); i++) { // should be all scalar
if (colvars[i]->value().type() != colvarvalue::type_scalar) {
cvm::error("Error: only scalar colvars are supported when gatherVectorColvars is off.\n", INPUT_ERROR);
return INPUT_ERROR;
@ -77,7 +77,7 @@ int colvarbias_histogram::init(std::string const &conf)
get_keyval(conf, "weights", weights, weights);
}
for (i = 0; i < colvars.size(); i++) {
for (i = 0; i < num_variables(); i++) {
colvars[i]->enable(f_cv_grid);
}
@ -116,7 +116,7 @@ int colvarbias_histogram::update()
}
// assign a valid bin size
bin.assign(colvars.size(), 0);
bin.assign(num_variables(), 0);
if (out_name.size() == 0) {
// At the first timestep, we need to assign out_name since
@ -137,7 +137,7 @@ int colvarbias_histogram::update()
if (colvar_array_size == 0) {
// update indices for scalar values
size_t i;
for (i = 0; i < colvars.size(); i++) {
for (i = 0; i < num_variables(); i++) {
bin[i] = grid->current_bin_scalar(i);
}
@ -148,7 +148,7 @@ int colvarbias_histogram::update()
// update indices for vector/array values
size_t iv, i;
for (iv = 0; iv < colvar_array_size; iv++) {
for (i = 0; i < colvars.size(); i++) {
for (i = 0; i < num_variables(); i++) {
bin[i] = grid->current_bin_scalar(i, iv);
}

View File

@ -27,7 +27,8 @@
#define PATHSEP "/"
#endif
#include "colvarmodule.h"
#include "colvarproxy.h"
#include "colvar.h"
#include "colvarbias_meta.h"

View File

@ -7,7 +7,10 @@
// If you wish to distribute your changes, please submit them to the
// Colvars repository at GitHub.
#include <cmath>
#include "colvarmodule.h"
#include "colvarproxy.h"
#include "colvarvalue.h"
#include "colvarbias_restraint.h"
@ -150,13 +153,14 @@ colvarbias_restraint_k::colvarbias_restraint_k(char const *key)
: colvarbias(key), colvarbias_ti(key), colvarbias_restraint(key)
{
force_k = -1.0;
check_positive_k = true;
}
int colvarbias_restraint_k::init(std::string const &conf)
{
get_keyval(conf, "forceConstant", force_k, (force_k > 0.0 ? force_k : 1.0));
if (force_k < 0.0) {
if (check_positive_k && (force_k < 0.0)) {
cvm::error("Error: undefined or invalid force constant.\n", INPUT_ERROR);
return INPUT_ERROR;
}
@ -177,6 +181,7 @@ colvarbias_restraint_moving::colvarbias_restraint_moving(char const *key)
target_nstages = 0;
target_nsteps = 0;
stage = 0;
acc_work = 0.0;
b_chg_centers = false;
b_chg_force_k = false;
}
@ -203,6 +208,14 @@ int colvarbias_restraint_moving::init(std::string const &conf)
cvm::error("Error: targetNumStages and lambdaSchedule are incompatible.\n", INPUT_ERROR);
return cvm::get_error();
}
get_keyval_feature(this, conf, "outputAccumulatedWork",
f_cvb_output_acc_work,
is_enabled(f_cvb_output_acc_work));
if (is_enabled(f_cvb_output_acc_work) && (target_nstages > 0)) {
return cvm::error("Error: outputAccumulatedWork and targetNumStages "
"are incompatible.\n", INPUT_ERROR);
}
}
return COLVARS_OK;
@ -246,8 +259,6 @@ colvarbias_restraint_centers_moving::colvarbias_restraint_centers_moving(char co
{
b_chg_centers = false;
b_output_centers = false;
b_output_acc_work = false;
acc_work = 0.0;
}
@ -288,9 +299,6 @@ int colvarbias_restraint_centers_moving::init(std::string const &conf)
0.5);
}
get_keyval(conf, "outputAccumulatedWork", b_output_acc_work,
b_output_acc_work); // TODO this conflicts with stages
} else {
target_centers.clear();
}
@ -382,7 +390,8 @@ int colvarbias_restraint_centers_moving::update()
int colvarbias_restraint_centers_moving::update_acc_work()
{
if (b_output_acc_work) {
if (b_chg_centers) {
if (is_enabled(f_cvb_output_acc_work)) {
if ((cvm::step_relative() > 0) &&
(cvm::step_absolute() <= target_nsteps)) {
for (size_t i = 0; i < num_variables(); i++) {
@ -391,6 +400,7 @@ int colvarbias_restraint_centers_moving::update_acc_work()
}
}
}
}
return COLVARS_OK;
}
@ -410,7 +420,7 @@ std::string const colvarbias_restraint_centers_moving::get_state_params() const
}
os << "\n";
if (b_output_acc_work) {
if (is_enabled(f_cvb_output_acc_work)) {
os << "accumulatedWork "
<< std::setprecision(cvm::en_prec) << std::setw(cvm::en_width)
<< acc_work << "\n";
@ -429,7 +439,7 @@ int colvarbias_restraint_centers_moving::set_state_params(std::string const &con
// cvm::log ("Reading the updated restraint centers from the restart.\n");
if (!get_keyval(conf, "centers", colvar_centers))
cvm::error("Error: restraint centers are missing from the restart.\n");
if (b_output_acc_work) {
if (is_enabled(f_cvb_output_acc_work)) {
if (!get_keyval(conf, "accumulatedWork", acc_work))
cvm::error("Error: accumulatedWork is missing from the restart.\n");
}
@ -449,7 +459,7 @@ std::ostream & colvarbias_restraint_centers_moving::write_traj_label(std::ostrea
}
}
if (b_output_acc_work) {
if (b_chg_centers && is_enabled(f_cvb_output_acc_work)) {
os << " W_"
<< cvm::wrap_string(this->name, cvm::en_width-2);
}
@ -468,7 +478,7 @@ std::ostream & colvarbias_restraint_centers_moving::write_traj(std::ostream &os)
}
}
if (b_output_acc_work) {
if (b_chg_centers && is_enabled(f_cvb_output_acc_work)) {
os << " "
<< std::setprecision(cvm::en_prec) << std::setw(cvm::en_width)
<< acc_work;
@ -488,10 +498,11 @@ colvarbias_restraint_k_moving::colvarbias_restraint_k_moving(char const *key)
{
b_chg_force_k = false;
target_equil_steps = 0;
target_force_k = 0.0;
starting_force_k = 0.0;
target_force_k = -1.0;
starting_force_k = -1.0;
force_k_exp = 1.0;
restraint_FE = 0.0;
force_k_incr = 0.0;
}
@ -569,14 +580,13 @@ int colvarbias_restraint_k_moving::update()
if (target_equil_steps == 0 || cvm::step_absolute() % target_nsteps >= target_equil_steps) {
// Start averaging after equilibration period, if requested
// Square distance normalized by square colvar width
cvm::real dist_sq = 0.0;
// Derivative of energy with respect to force_k
cvm::real dU_dk = 0.0;
for (size_t i = 0; i < num_variables(); i++) {
dist_sq += d_restraint_potential_dk(i);
dU_dk += d_restraint_potential_dk(i);
}
restraint_FE += 0.5 * force_k_exp * std::pow(lambda, force_k_exp - 1.0)
* (target_force_k - starting_force_k) * dist_sq;
restraint_FE += force_k_exp * std::pow(lambda, force_k_exp - 1.0)
* (target_force_k - starting_force_k) * dU_dk;
}
// Finish current stage...
@ -607,10 +617,13 @@ int colvarbias_restraint_k_moving::update()
} else if (cvm::step_absolute() <= target_nsteps) {
// update force constant (slow growth)
lambda = cvm::real(cvm::step_absolute()) / cvm::real(target_nsteps);
cvm::real const force_k_old = force_k;
force_k = starting_force_k + (target_force_k - starting_force_k)
* std::pow(lambda, force_k_exp);
force_k_incr = force_k - force_k_old;
}
}
@ -618,6 +631,23 @@ int colvarbias_restraint_k_moving::update()
}
int colvarbias_restraint_k_moving::update_acc_work()
{
if (b_chg_force_k) {
if (is_enabled(f_cvb_output_acc_work)) {
if (cvm::step_relative() > 0) {
cvm::real dU_dk = 0.0;
for (size_t i = 0; i < num_variables(); i++) {
dU_dk += d_restraint_potential_dk(i);
}
acc_work += dU_dk * force_k_incr;
}
}
}
return COLVARS_OK;
}
std::string const colvarbias_restraint_k_moving::get_state_params() const
{
std::ostringstream os;
@ -626,6 +656,12 @@ std::string const colvarbias_restraint_k_moving::get_state_params() const
os << "forceConstant "
<< std::setprecision(cvm::en_prec)
<< std::setw(cvm::en_width) << force_k << "\n";
if (is_enabled(f_cvb_output_acc_work)) {
os << "accumulatedWork "
<< std::setprecision(cvm::en_prec) << std::setw(cvm::en_width)
<< acc_work << "\n";
}
}
return os.str();
}
@ -639,6 +675,10 @@ int colvarbias_restraint_k_moving::set_state_params(std::string const &conf)
// cvm::log ("Reading the updated force constant from the restart.\n");
if (!get_keyval(conf, "forceConstant", force_k, force_k))
cvm::error("Error: force constant is missing from the restart.\n");
if (is_enabled(f_cvb_output_acc_work)) {
if (!get_keyval(conf, "accumulatedWork", acc_work))
cvm::error("Error: accumulatedWork is missing from the restart.\n");
}
}
return COLVARS_OK;
@ -647,12 +687,21 @@ int colvarbias_restraint_k_moving::set_state_params(std::string const &conf)
std::ostream & colvarbias_restraint_k_moving::write_traj_label(std::ostream &os)
{
if (b_chg_force_k && is_enabled(f_cvb_output_acc_work)) {
os << " W_"
<< cvm::wrap_string(this->name, cvm::en_width-2);
}
return os;
}
std::ostream & colvarbias_restraint_k_moving::write_traj(std::ostream &os)
{
if (b_chg_force_k && is_enabled(f_cvb_output_acc_work)) {
os << " "
<< std::setprecision(cvm::en_prec) << std::setw(cvm::en_width)
<< acc_work;
}
return os;
}
@ -765,6 +814,7 @@ int colvarbias_restraint_harmonic::update()
// update accumulated work using the current forces
error_code |= colvarbias_restraint_centers_moving::update_acc_work();
error_code |= colvarbias_restraint_k_moving::update_acc_work();
return error_code;
}
@ -876,8 +926,8 @@ colvarbias_restraint_harmonic_walls::colvarbias_restraint_harmonic_walls(char co
colvarbias_restraint_moving(key),
colvarbias_restraint_k_moving(key)
{
lower_wall_k = 0.0;
upper_wall_k = 0.0;
lower_wall_k = -1.0;
upper_wall_k = -1.0;
}
@ -887,26 +937,6 @@ int colvarbias_restraint_harmonic_walls::init(std::string const &conf)
colvarbias_restraint_moving::init(conf);
colvarbias_restraint_k_moving::init(conf);
get_keyval(conf, "lowerWallConstant", lower_wall_k,
(lower_wall_k > 0.0) ? lower_wall_k : force_k);
get_keyval(conf, "upperWallConstant", upper_wall_k,
(upper_wall_k > 0.0) ? upper_wall_k : force_k);
if (lower_wall_k * upper_wall_k > 0.0) {
for (size_t i = 0; i < num_variables(); i++) {
if (variables(i)->width != 1.0)
cvm::log("The lower and upper wall force constants for colvar \""+
variables(i)->name+
"\" will be rescaled to "+
cvm::to_str(lower_wall_k /
(variables(i)->width * variables(i)->width))+
" and "+
cvm::to_str(upper_wall_k /
(variables(i)->width * variables(i)->width))+
" according to the specified width.\n");
}
}
enable(f_cvb_scalar_variables);
size_t i;
@ -942,16 +972,23 @@ int colvarbias_restraint_harmonic_walls::init(std::string const &conf)
}
if ((lower_walls.size() == 0) && (upper_walls.size() == 0)) {
cvm::error("Error: no walls provided.\n", INPUT_ERROR);
return INPUT_ERROR;
return cvm::error("Error: no walls provided.\n", INPUT_ERROR);
}
if (lower_walls.size() > 0) {
get_keyval(conf, "lowerWallConstant", lower_wall_k,
(lower_wall_k > 0.0) ? lower_wall_k : force_k);
}
if (upper_walls.size() > 0) {
get_keyval(conf, "upperWallConstant", upper_wall_k,
(upper_wall_k > 0.0) ? upper_wall_k : force_k);
}
if ((lower_walls.size() == 0) || (upper_walls.size() == 0)) {
for (i = 0; i < num_variables(); i++) {
if (variables(i)->is_enabled(f_cv_periodic)) {
cvm::error("Error: at least one variable is periodic, "
return cvm::error("Error: at least one variable is periodic, "
"both walls must be provided.\n", INPUT_ERROR);
return INPUT_ERROR;
}
}
}
@ -972,20 +1009,50 @@ int colvarbias_restraint_harmonic_walls::init(std::string const &conf)
INPUT_ERROR);
return INPUT_ERROR;
}
force_k = lower_wall_k * upper_wall_k;
// transform the two constants to relative values
force_k = std::sqrt(lower_wall_k * upper_wall_k);
// transform the two constants to relative values using gemetric mean as ref
// to preserve force_k if provided as single parameter
// (allow changing both via force_k)
lower_wall_k /= force_k;
upper_wall_k /= force_k;
} else {
// If only one wall is defined, need to rescale as well
if (lower_walls.size() > 0) {
force_k = lower_wall_k;
lower_wall_k = 1.0;
}
if (upper_walls.size() > 0) {
force_k = upper_wall_k;
upper_wall_k = 1.0;
}
}
// Initialize starting value of the force constant (in case it's changing)
starting_force_k = force_k;
if (lower_walls.size() > 0) {
for (i = 0; i < num_variables(); i++) {
if (variables(i)->width != 1.0)
cvm::log("The force constant for colvar \""+variables(i)->name+
cvm::log("The lower wall force constant for colvar \""+
variables(i)->name+
"\" will be rescaled to "+
cvm::to_str(force_k / (variables(i)->width * variables(i)->width))+
cvm::to_str(lower_wall_k * force_k /
(variables(i)->width * variables(i)->width))+
" according to the specified width.\n");
}
}
if (upper_walls.size() > 0) {
for (i = 0; i < num_variables(); i++) {
if (variables(i)->width != 1.0)
cvm::log("The upper wall force constant for colvar \""+
variables(i)->name+
"\" will be rescaled to "+
cvm::to_str(upper_wall_k * force_k /
(variables(i)->width * variables(i)->width))+
" according to the specified width.\n");
}
}
return COLVARS_OK;
}
@ -1001,6 +1068,8 @@ int colvarbias_restraint_harmonic_walls::update()
error_code |= colvarbias_restraint::update();
error_code |= colvarbias_restraint_k_moving::update_acc_work();
return error_code;
}
@ -1134,6 +1203,7 @@ colvarbias_restraint_linear::colvarbias_restraint_linear(char const *key)
colvarbias_restraint_centers_moving(key),
colvarbias_restraint_k_moving(key)
{
check_positive_k = false;
}
@ -1177,6 +1247,7 @@ int colvarbias_restraint_linear::update()
// update accumulated work using the current forces
error_code |= colvarbias_restraint_centers_moving::update_acc_work();
error_code |= colvarbias_restraint_k_moving::update_acc_work();
return error_code;
}

View File

@ -89,8 +89,12 @@ public:
virtual int change_configuration(std::string const &conf);
protected:
/// \brief Restraint force constant
cvm::real force_k;
/// \brief Whether the force constant should be positive
bool check_positive_k;
};
@ -129,6 +133,9 @@ protected:
/// \brief Number of steps required to reach the target force constant
/// or restraint centers
long target_nsteps;
/// \brief Accumulated work (computed when outputAccumulatedWork == true)
cvm::real acc_work;
};
@ -157,8 +164,7 @@ protected:
/// \brief Initial value of the restraint centers
std::vector<colvarvalue> initial_centers;
/// \brief Amplitude of the restraint centers' increment at each step
/// towards the new values (calculated from target_nsteps)
/// \brief Increment of the restraint centers at each step
std::vector<colvarvalue> centers_incr;
/// \brief Update the centers by interpolating between initial and target
@ -167,12 +173,6 @@ protected:
/// Whether to write the current restraint centers to the trajectory file
bool b_output_centers;
/// Whether to write the current accumulated work to the trajectory file
bool b_output_acc_work;
/// \brief Accumulated work
cvm::real acc_work;
/// Update the accumulated work
int update_acc_work();
};
@ -212,6 +212,12 @@ protected:
/// \brief Equilibration steps for restraint FE calculation through TI
cvm::real target_equil_steps;
/// \brief Increment of the force constant at each step
cvm::real force_k_incr;
/// Update the accumulated work
int update_acc_work();
};

View File

@ -20,52 +20,48 @@
// simple_scalar_dist_functions (derived_class)
#include <fstream>
#include <cmath>
#include "colvarmodule.h"
#include "colvar.h"
#include "colvaratoms.h"
/// \brief Colvar component (base class); most implementations of
/// \link cvc \endlink utilize one or more \link
/// colvarmodule::atom \endlink or \link colvarmodule::atom_group
/// \endlink objects to access atoms.
/// \brief Colvar component (base class for collective variables)
///
/// A \link cvc \endlink object (or an object of a
/// cvc-derived class) specifies how to calculate a collective
/// variable, its gradients and other related physical quantities
/// which do not depend only on the numeric value (the \link colvar
/// \endlink class already serves this purpose).
/// cvc-derived class) implements the calculation of a collective
/// variable, its gradients and any other related physical quantities
/// that depend on microscopic degrees of freedom.
///
/// No restriction is set to what kind of calculation a \link
/// cvc \endlink object performs (usually calculate an
/// analytical function of atomic coordinates). The only constraint
/// is that the value calculated is implemented as a \link colvarvalue
/// \endlink object. This serves to provide a unique way to calculate
/// scalar and non-scalar collective variables, and specify if and how
/// they can be combined together by the parent \link colvar \endlink
/// object.
/// No restriction is set to what kind of calculation a \link cvc \endlink
/// object performs (usually an analytical function of atomic coordinates).
/// The only constraints are that: \par
///
/// - The value is calculated by the \link calc_value() \endlink
/// method, and is an object of \link colvarvalue \endlink class. This
/// provides a transparent way to treat scalar and non-scalar variables
/// alike, and allows an automatic selection of the applicable algorithms.
///
/// - The object provides an implementation \link apply_force() \endlink to
/// apply forces to atoms. Typically, one or more \link cvm::atom_group
/// \endlink objects are used, but this is not a requirement for as long as
/// the \link cvc \endlink object communicates with the simulation program.
///
/// <b> If you wish to implement a new collective variable component, you
/// should write your own class by inheriting directly from \link
/// cvc \endlink, or one of its derived classes (for instance,
/// \link distance \endlink is frequently used, because it provides
/// colvar::cvc \endlink, or one of its derived classes (for instance,
/// \link colvar::distance \endlink is frequently used, because it provides
/// useful data and function members for any colvar based on two
/// atom groups). The steps are: \par
/// 1. add the name of this class under colvar.h \par
/// 2. add a call to the parser in colvar.C, within the function colvar::colvar() \par
/// 3. declare the class in colvarcomp.h \par
/// 4. implement the class in one of the files colvarcomp_*.C
/// atom groups).</b>
///
/// The steps are: \par
/// 1. Declare the new class as a derivative of \link colvar::cvc \endlink
/// in the file \link colvarcomp.h \endlink
/// 2. Implement the new class in a file named colvarcomp_<something>.cpp
/// 3. Declare the name of the new class inside the \link colvar \endlink class
/// in \link colvar.h \endlink (see "list of available components")
/// 4. Add a call for the new class in colvar::init_components()
//// (file: colvar.cpp)
///
/// </b>
/// The cvm::atom and cvm::atom_group classes are available to
/// transparently communicate with the simulation program. However,
/// they are not strictly needed, as long as all the degrees of
/// freedom associated to the cvc are properly evolved by a simple
/// call to e.g. apply_force().
class colvar::cvc
: public colvarparse, public colvardeps
@ -155,7 +151,7 @@ public:
/// \brief Calculate the atomic gradients, to be reused later in
/// order to apply forces
virtual void calc_gradients() = 0;
virtual void calc_gradients() {}
/// \brief Calculate the atomic fit gradients
void calc_fit_gradients();

View File

@ -581,6 +581,12 @@ colvar::distance_inv::distance_inv(std::string const &conf)
}
}
if (is_enabled(f_cvc_debug_gradient)) {
cvm::log("Warning: debugGradients will not give correct results "
"for distanceInv, because its value and gradients are computed "
"simultaneously.\n");
}
x.type(colvarvalue::type_scalar);
}
@ -601,11 +607,9 @@ void colvar::distance_inv::calc_value()
for (cvm::atom_iter ai2 = group2->begin(); ai2 != group2->end(); ai2++) {
cvm::rvector const dv = ai2->pos - ai1->pos;
cvm::real const d2 = dv.norm2();
cvm::real dinv = 1.0;
for (int ne = 0; ne < exponent/2; ne++)
dinv *= 1.0/d2;
cvm::real const dinv = cvm::integer_power(d2, -1*(exponent/2));
x.real_value += dinv;
cvm::rvector const dsumddv = -(cvm::real(exponent)) * dinv/d2 * dv;
cvm::rvector const dsumddv = -1.0*(exponent/2) * dinv/d2 * 2.0 * dv;
ai1->grad += -1.0 * dsumddv;
ai2->grad += dsumddv;
}
@ -615,11 +619,9 @@ void colvar::distance_inv::calc_value()
for (cvm::atom_iter ai2 = group2->begin(); ai2 != group2->end(); ai2++) {
cvm::rvector const dv = cvm::position_distance(ai1->pos, ai2->pos);
cvm::real const d2 = dv.norm2();
cvm::real dinv = 1.0;
for (int ne = 0; ne < exponent/2; ne++)
dinv *= 1.0/d2;
cvm::real const dinv = cvm::integer_power(d2, -1*(exponent/2));
x.real_value += dinv;
cvm::rvector const dsumddv = -(cvm::real(exponent)) * dinv/d2 * dv;
cvm::rvector const dsumddv = -1.0*(exponent/2) * dinv/d2 * 2.0 * dv;
ai1->grad += -1.0 * dsumddv;
ai2->grad += dsumddv;
}
@ -627,13 +629,11 @@ void colvar::distance_inv::calc_value()
}
x.real_value *= 1.0 / cvm::real(group1->size() * group2->size());
x.real_value = std::pow(x.real_value, -1.0/(cvm::real(exponent)));
}
x.real_value = std::pow(x.real_value, -1.0/cvm::real(exponent));
void colvar::distance_inv::calc_gradients()
{
cvm::real const dxdsum = (-1.0/(cvm::real(exponent))) * std::pow(x.real_value, exponent+1) / cvm::real(group1->size() * group2->size());
cvm::real const dxdsum = (-1.0/(cvm::real(exponent))) *
cvm::integer_power(x.real_value, exponent+1) /
cvm::real(group1->size() * group2->size());
for (cvm::atom_iter ai1 = group1->begin(); ai1 != group1->end(); ai1++) {
ai1->grad *= dxdsum;
}
@ -643,6 +643,11 @@ void colvar::distance_inv::calc_gradients()
}
void colvar::distance_inv::calc_gradients()
{
}
void colvar::distance_inv::apply_force(colvarvalue const &force)
{
if (!group1->noforce)

View File

@ -8,10 +8,16 @@
// Colvars repository at GitHub.
#include "colvarmodule.h"
#include "colvarproxy.h"
#include "colvardeps.h"
colvardeps::colvardeps()
: time_step_factor (1) {}
{
time_step_factor = 1;
}
colvardeps::~colvardeps() {
size_t i;
@ -416,6 +422,9 @@ void colvardeps::init_cvb_requires() {
init_feature(f_cvb_get_total_force, "obtain total force", f_type_dynamic);
f_req_children(f_cvb_get_total_force, f_cv_total_force);
init_feature(f_cvb_output_acc_work, "output accumulated work", f_type_user);
f_req_self(f_cvb_output_acc_work, f_cvb_apply_force);
init_feature(f_cvb_history_dependent, "history-dependent", f_type_static);
init_feature(f_cvb_time_dependent, "time-dependent", f_type_static);

View File

@ -225,6 +225,8 @@ public:
f_cvb_apply_force,
/// \brief requires total forces
f_cvb_get_total_force,
/// \brief whether this bias should record the accumulated work
f_cvb_output_acc_work,
/// \brief depends on simulation history
f_cvb_history_dependent,
/// \brief depends on time

View File

@ -14,6 +14,7 @@
#include "colvarcomp.h"
#include "colvargrid.h"
#include <ctime>
colvar_grid_count::colvar_grid_count()
: colvar_grid<size_t>()
@ -27,38 +28,32 @@ colvar_grid_count::colvar_grid_count(std::vector<int> const &nx_i,
{}
colvar_grid_count::colvar_grid_count(std::vector<colvar *> &colvars,
size_t const &def_count)
: colvar_grid<size_t>(colvars, def_count, 1)
size_t const &def_count,
bool margin)
: colvar_grid<size_t>(colvars, def_count, 1, margin)
{}
colvar_grid_scalar::colvar_grid_scalar()
: colvar_grid<cvm::real>(), samples(NULL), grad(NULL)
: colvar_grid<cvm::real>(), samples(NULL)
{}
colvar_grid_scalar::colvar_grid_scalar(colvar_grid_scalar const &g)
: colvar_grid<cvm::real>(g), samples(NULL), grad(NULL)
: colvar_grid<cvm::real>(g), samples(NULL)
{
grad = new cvm::real[nd];
}
colvar_grid_scalar::colvar_grid_scalar(std::vector<int> const &nx_i)
: colvar_grid<cvm::real>(nx_i, 0.0, 1), samples(NULL), grad(NULL)
: colvar_grid<cvm::real>(nx_i, 0.0, 1), samples(NULL)
{
grad = new cvm::real[nd];
}
colvar_grid_scalar::colvar_grid_scalar(std::vector<colvar *> &colvars, bool margin)
: colvar_grid<cvm::real>(colvars, 0.0, 1, margin), samples(NULL), grad(NULL)
: colvar_grid<cvm::real>(colvars, 0.0, 1, margin), samples(NULL)
{
grad = new cvm::real[nd];
}
colvar_grid_scalar::~colvar_grid_scalar()
{
if (grad) {
delete [] grad;
grad = NULL;
}
}
cvm::real colvar_grid_scalar::maximum_value() const
@ -143,18 +138,18 @@ void colvar_grid_gradient::write_1D_integral(std::ostream &os)
os << "# xi A(xi)\n";
if ( cv.size() != 1 ) {
if (cv.size() != 1) {
cvm::error("Cannot write integral for multi-dimensional gradient grids.");
return;
}
integral = 0.0;
int_vals.push_back( 0.0 );
int_vals.push_back(0.0);
min = 0.0;
// correction for periodic colvars, so that the PMF is periodic
cvm::real corr;
if ( periodic[0] ) {
if (periodic[0]) {
corr = average();
} else {
corr = 0.0;
@ -171,7 +166,7 @@ void colvar_grid_gradient::write_1D_integral(std::ostream &os)
}
if ( integral < min ) min = integral;
int_vals.push_back( integral );
int_vals.push_back(integral);
}
bin = 0.0;
@ -192,3 +187,670 @@ void colvar_grid_gradient::write_1D_integral(std::ostream &os)
integrate_potential::integrate_potential(std::vector<colvar *> &colvars, colvar_grid_gradient * gradients)
: colvar_grid_scalar(colvars, true),
gradients(gradients)
{
// parent class colvar_grid_scalar is constructed with margin option set to true
// hence PMF grid is wider than gradient grid if non-PBC
if (nd > 1) {
divergence.resize(nt);
// Compute inverse of Laplacian diagonal for Jacobi preconditioning
// For now all code related to preconditioning is commented out
// until a method better than Jacobi is implemented
// cvm::log("Preparing inverse diagonal for preconditioning...");
// inv_lap_diag.resize(nt);
// std::vector<cvm::real> id(nt), lap_col(nt);
// for (int i = 0; i < nt; i++) {
// if (i % (nt / 100) == 0)
// cvm::log(cvm::to_str(i));
// id[i] = 1.;
// atimes(id, lap_col);
// id[i] = 0.;
// inv_lap_diag[i] = 1. / lap_col[i];
// }
// cvm::log("Done.");
}
}
int integrate_potential::integrate(const int itmax, const cvm::real &tol, cvm::real & err)
{
int iter = 0;
if (nd == 1) {
cvm::real sum = 0.0;
cvm::real corr;
if ( periodic[0] ) {
corr = gradients->average(); // Enforce PBC by subtracting average gradient
} else {
corr = 0.0;
}
std::vector<int> ix;
// Iterate over valid indices in gradient grid
for (ix = new_index(); gradients->index_ok(ix); incr(ix)) {
set_value(ix, sum);
sum += (gradients->value_output(ix) - corr) * widths[0];
}
if (index_ok(ix)) {
// This will happen if non-periodic: then PMF grid has one extra bin wrt gradient grid
set_value(ix, sum);
}
} else if (nd <= 3) {
nr_linbcg_sym(divergence, data, tol, itmax, iter, err);
cvm::log("Integrated in " + cvm::to_str(iter) + " steps, error: " + cvm::to_str(err));
} else {
cvm::error("Cannot integrate PMF in dimension > 3\n");
}
return iter;
}
void integrate_potential::set_div()
{
if (nd == 1) return;
for (std::vector<int> ix = new_index(); index_ok(ix); incr(ix)) {
update_div_local(ix);
}
}
void integrate_potential::update_div_neighbors(const std::vector<int> &ix0)
{
std::vector<int> ix(ix0);
int i, j, k;
// If not periodic, expanded grid ensures that neighbors of ix0 are valid grid points
if (nd == 1) {
return;
} else if (nd == 2) {
update_div_local(ix);
ix[0]++; wrap(ix);
update_div_local(ix);
ix[1]++; wrap(ix);
update_div_local(ix);
ix[0]--; wrap(ix);
update_div_local(ix);
} else if (nd == 3) {
for (i = 0; i<2; i++) {
ix[1] = ix0[1];
for (j = 0; j<2; j++) {
ix[2] = ix0[2];
for (k = 0; k<2; k++) {
wrap(ix);
update_div_local(ix);
ix[2]++;
}
ix[1]++;
}
ix[0]++;
}
}
}
void integrate_potential::get_grad(cvm::real * g, std::vector<int> &ix)
{
size_t count, i;
bool edge = gradients->wrap_edge(ix); // Detect edge if non-PBC
if (gradients->samples) {
count = gradients->samples->value(ix);
} else {
count = 1;
}
if (!edge && count) {
cvm::real const *grad = &(gradients->value(ix));
cvm::real const fact = 1.0 / count;
for ( i = 0; i<nd; i++ ) {
g[i] = fact * grad[i];
}
} else {
for ( i = 0; i<nd; i++ ) {
g[i] = 0.0;
}
}
}
void integrate_potential::update_div_local(const std::vector<int> &ix0)
{
const int linear_index = address(ix0);
int i, j, k;
std::vector<int> ix = ix0;
const cvm::real * g;
if (nd == 2) {
// gradients at grid points surrounding the current scalar grid point
cvm::real g00[2], g01[2], g10[2], g11[2];
get_grad(g11, ix);
ix[0] = ix0[0] - 1;
get_grad(g01, ix);
ix[1] = ix0[1] - 1;
get_grad(g00, ix);
ix[0] = ix0[0];
get_grad(g10, ix);
divergence[linear_index] = ((g10[0]-g00[0] + g11[0]-g01[0]) / widths[0]
+ (g01[1]-g00[1] + g11[1]-g10[1]) / widths[1]) * 0.5;
} else if (nd == 3) {
cvm::real gc[24]; // stores 3d gradients in 8 contiguous bins
int index = 0;
ix[0] = ix0[0] - 1;
for (i = 0; i<2; i++) {
ix[1] = ix0[1] - 1;
for (j = 0; j<2; j++) {
ix[2] = ix0[2] - 1;
for (k = 0; k<2; k++) {
get_grad(gc + index, ix);
index += 3;
ix[2]++;
}
ix[1]++;
}
ix[0]++;
}
divergence[linear_index] =
((gc[3*4]-gc[0] + gc[3*5]-gc[3*1] + gc[3*6]-gc[3*2] + gc[3*7]-gc[3*3])
/ widths[0]
+ (gc[3*2+1]-gc[0+1] + gc[3*3+1]-gc[3*1+1] + gc[3*6+1]-gc[3*4+1] + gc[3*7+1]-gc[3*5+1])
/ widths[1]
+ (gc[3*1+2]-gc[0+2] + gc[3*3+2]-gc[3*2+2] + gc[3*5+2]-gc[3*4+2] + gc[3*7+2]-gc[3*6+2])
/ widths[2]) * 0.25;
}
}
/// Multiplication by sparse matrix representing Laplacian
/// NOTE: Laplacian must be symmetric for solving with CG
void integrate_potential::atimes(const std::vector<cvm::real> &A, std::vector<cvm::real> &LA)
{
if (nd == 2) {
// DIMENSION 2
size_t index, index2;
int i, j;
cvm::real fact;
const cvm::real ffx = 1.0 / (widths[0] * widths[0]);
const cvm::real ffy = 1.0 / (widths[1] * widths[1]);
const int h = nx[1];
const int w = nx[0];
// offsets for 4 reference points of the Laplacian stencil
int xm = -h;
int xp = h;
int ym = -1;
int yp = 1;
// NOTE on performance: this version is slightly sub-optimal because
// it contains two double loops on the core of the array (for x and y terms)
// The slightly faster version is in commit 0254cb5a2958cb2e135f268371c4b45fad34866b
// yet it is much uglier, and probably horrible to extend to dimension 3
// All terms in the matrix are assigned (=) during the x loops, then updated (+=)
// with the y (and z) contributions
// All x components except on x edges
index = h; // Skip first column
// Halve the term on y edges (if any) to preserve symmetry of the Laplacian matrix
// (Long Chen, Finite Difference Methods, UCI, 2017)
fact = periodic[1] ? 1.0 : 0.5;
for (i=1; i<w-1; i++) {
// Full range of j, but factor may change on y edges (j == 0 and j == h-1)
LA[index] = fact * ffx * (A[index + xm] + A[index + xp] - 2.0 * A[index]);
index++;
for (j=1; j<h-1; j++) {
LA[index] = ffx * (A[index + xm] + A[index + xp] - 2.0 * A[index]);
index++;
}
LA[index] = fact * ffx * (A[index + xm] + A[index + xp] - 2.0 * A[index]);
index++;
}
// Edges along x (x components only)
index = 0; // Follows left edge
index2 = h * (w - 1); // Follows right edge
if (periodic[0]) {
xm = h * (w - 1);
xp = h;
fact = periodic[1] ? 1.0 : 0.5;
LA[index] = fact * ffx * (A[index + xm] + A[index + xp] - 2.0 * A[index]);
LA[index2] = fact * ffx * (A[index2 - xp] + A[index2 - xm] - 2.0 * A[index2]);
index++;
index2++;
for (j=1; j<h-1; j++) {
LA[index] = ffx * (A[index + xm] + A[index + xp] - 2.0 * A[index]);
LA[index2] = ffx * (A[index2 - xp] + A[index2 - xm] - 2.0 * A[index2]);
index++;
index2++;
}
LA[index] = fact * ffx * (A[index + xm] + A[index + xp] - 2.0 * A[index]);
LA[index2] = fact * ffx * (A[index2 - xp] + A[index2 - xm] - 2.0 * A[index2]);
} else {
xm = -h;
xp = h;
fact = periodic[1] ? 1.0 : 0.5; // Halve in corners in full PBC only
// lower corner, "j == 0"
LA[index] = fact * ffx * (A[index + xp] - A[index]);
LA[index2] = fact * ffx * (A[index2 + xm] - A[index2]);
index++;
index2++;
for (j=1; j<h-1; j++) {
// x gradient (+ y term of laplacian, calculated below)
LA[index] = ffx * (A[index + xp] - A[index]);
LA[index2] = ffx * (A[index2 + xm] - A[index2]);
index++;
index2++;
}
// upper corner, j == h-1
LA[index] = fact * ffx * (A[index + xp] - A[index]);
LA[index2] = fact * ffx * (A[index2 + xm] - A[index2]);
}
// Now adding all y components
// All y components except on y edges
index = 1; // Skip first element (in first row)
fact = periodic[0] ? 1.0 : 0.5; // for i == 0
for (i=0; i<w; i++) {
// Factor of 1/2 on x edges if non-periodic
if (i == 1) fact = 1.0;
if (i == w - 1) fact = periodic[0] ? 1.0 : 0.5;
for (j=1; j<h-1; j++) {
LA[index] += fact * ffy * (A[index + ym] + A[index + yp] - 2.0 * A[index]);
index++;
}
index += 2; // skip the edges and move to next column
}
// Edges along y (y components only)
index = 0; // Follows bottom edge
index2 = h - 1; // Follows top edge
if (periodic[1]) {
fact = periodic[0] ? 1.0 : 0.5;
ym = h - 1;
yp = 1;
LA[index] += fact * ffy * (A[index + ym] + A[index + yp] - 2.0 * A[index]);
LA[index2] += fact * ffy * (A[index2 - yp] + A[index2 - ym] - 2.0 * A[index2]);
index += h;
index2 += h;
for (i=1; i<w-1; i++) {
LA[index] += ffy * (A[index + ym] + A[index + yp] - 2.0 * A[index]);
LA[index2] += ffy * (A[index2 - yp] + A[index2 - ym] - 2.0 * A[index2]);
index += h;
index2 += h;
}
LA[index] += fact * ffy * (A[index + ym] + A[index + yp] - 2.0 * A[index]);
LA[index2] += fact * ffy * (A[index2 - yp] + A[index2 - ym] - 2.0 * A[index2]);
} else {
ym = -1;
yp = 1;
fact = periodic[0] ? 1.0 : 0.5; // Halve in corners in full PBC only
// Left corner
LA[index] += fact * ffy * (A[index + yp] - A[index]);
LA[index2] += fact * ffy * (A[index2 + ym] - A[index2]);
index += h;
index2 += h;
for (i=1; i<w-1; i++) {
// y gradient (+ x term of laplacian, calculated above)
LA[index] += ffy * (A[index + yp] - A[index]);
LA[index2] += ffy * (A[index2 + ym] - A[index2]);
index += h;
index2 += h;
}
// Right corner
LA[index] += fact * ffy * (A[index + yp] - A[index]);
LA[index2] += fact * ffy * (A[index2 + ym] - A[index2]);
}
} else if (nd == 3) {
// DIMENSION 3
int i, j, k;
size_t index, index2;
cvm::real fact = 1.0;
const cvm::real ffx = 1.0 / (widths[0] * widths[0]);
const cvm::real ffy = 1.0 / (widths[1] * widths[1]);
const cvm::real ffz = 1.0 / (widths[2] * widths[2]);
const int h = nx[2]; // height
const int d = nx[1]; // depth
const int w = nx[0]; // width
// offsets for 6 reference points of the Laplacian stencil
int xm = -d * h;
int xp = d * h;
int ym = -h;
int yp = h;
int zm = -1;
int zp = 1;
cvm::real factx = periodic[0] ? 1 : 0.5; // factor to be applied on x edges
cvm::real facty = periodic[1] ? 1 : 0.5; // same for y
cvm::real factz = periodic[2] ? 1 : 0.5; // same for z
cvm::real ifactx = 1 / factx;
cvm::real ifacty = 1 / facty;
cvm::real ifactz = 1 / factz;
// All x components except on x edges
index = d * h; // Skip left slab
fact = facty * factz;
for (i=1; i<w-1; i++) {
for (j=0; j<d; j++) { // full range of y
if (j == 1) fact *= ifacty;
if (j == d-1) fact *= facty;
LA[index] = fact * ffx * (A[index + xm] + A[index + xp] - 2.0 * A[index]);
index++;
fact *= ifactz;
for (k=1; k<h-1; k++) { // full range of z
LA[index] = fact * ffx * (A[index + xm] + A[index + xp] - 2.0 * A[index]);
index++;
}
fact *= factz;
LA[index] = fact * ffx * (A[index + xm] + A[index + xp] - 2.0 * A[index]);
index++;
}
}
// Edges along x (x components only)
index = 0; // Follows left slab
index2 = d * h * (w - 1); // Follows right slab
if (periodic[0]) {
xm = d * h * (w - 1);
xp = d * h;
fact = facty * factz;
for (j=0; j<d; j++) {
if (j == 1) fact *= ifacty;
if (j == d-1) fact *= facty;
LA[index] = fact * ffx * (A[index + xm] + A[index + xp] - 2.0 * A[index]);
LA[index2] = fact * ffx * (A[index2 - xp] + A[index2 - xm] - 2.0 * A[index2]);
index++;
index2++;
fact *= ifactz;
for (k=1; k<h-1; k++) {
LA[index] = fact * ffx * (A[index + xm] + A[index + xp] - 2.0 * A[index]);
LA[index2] = fact * ffx * (A[index2 - xp] + A[index2 - xm] - 2.0 * A[index2]);
index++;
index2++;
}
fact *= factz;
LA[index] = fact * ffx * (A[index + xm] + A[index + xp] - 2.0 * A[index]);
LA[index2] = fact * ffx * (A[index2 - xp] + A[index2 - xm] - 2.0 * A[index2]);
index++;
index2++;
}
} else {
xm = -d * h;
xp = d * h;
fact = facty * factz;
for (j=0; j<d; j++) {
if (j == 1) fact *= ifacty;
if (j == d-1) fact *= facty;
LA[index] = fact * ffx * (A[index + xp] - A[index]);
LA[index2] = fact * ffx * (A[index2 + xm] - A[index2]);
index++;
index2++;
fact *= ifactz;
for (k=1; k<h-1; k++) {
// x gradient (+ y, z terms of laplacian, calculated below)
LA[index] = fact * ffx * (A[index + xp] - A[index]);
LA[index2] = fact * ffx * (A[index2 + xm] - A[index2]);
index++;
index2++;
}
fact *= factz;
LA[index] = fact * ffx * (A[index + xp] - A[index]);
LA[index2] = fact * ffx * (A[index2 + xm] - A[index2]);
index++;
index2++;
}
}
// Now adding all y components
// All y components except on y edges
index = h; // Skip first column (in front slab)
fact = factx * factz;
for (i=0; i<w; i++) { // full range of x
if (i == 1) fact *= ifactx;
if (i == w-1) fact *= factx;
for (j=1; j<d-1; j++) {
LA[index] += fact * ffy * (A[index + ym] + A[index + yp] - 2.0 * A[index]);
index++;
fact *= ifactz;
for (k=1; k<h-1; k++) {
LA[index] += fact * ffy * (A[index + ym] + A[index + yp] - 2.0 * A[index]);
index++;
}
fact *= factz;
LA[index] += fact * ffy * (A[index + ym] + A[index + yp] - 2.0 * A[index]);
index++;
}
index += 2 * h; // skip columns in front and back slabs
}
// Edges along y (y components only)
index = 0; // Follows front slab
index2 = h * (d - 1); // Follows back slab
if (periodic[1]) {
ym = h * (d - 1);
yp = h;
fact = factx * factz;
for (i=0; i<w; i++) {
if (i == 1) fact *= ifactx;
if (i == w-1) fact *= factx;
LA[index] += fact * ffy * (A[index + ym] + A[index + yp] - 2.0 * A[index]);
LA[index2] += fact * ffy * (A[index2 - yp] + A[index2 - ym] - 2.0 * A[index2]);
index++;
index2++;
fact *= ifactz;
for (k=1; k<h-1; k++) {
LA[index] += fact * ffy * (A[index + ym] + A[index + yp] - 2.0 * A[index]);
LA[index2] += fact * ffy * (A[index2 - yp] + A[index2 - ym] - 2.0 * A[index2]);
index++;
index2++;
}
fact *= factz;
LA[index] += fact * ffy * (A[index + ym] + A[index + yp] - 2.0 * A[index]);
LA[index2] += fact * ffy * (A[index2 - yp] + A[index2 - ym] - 2.0 * A[index2]);
index++;
index2++;
index += h * (d - 1);
index2 += h * (d - 1);
}
} else {
ym = -h;
yp = h;
fact = factx * factz;
for (i=0; i<w; i++) {
if (i == 1) fact *= ifactx;
if (i == w-1) fact *= factx;
LA[index] += fact * ffy * (A[index + yp] - A[index]);
LA[index2] += fact * ffy * (A[index2 + ym] - A[index2]);
index++;
index2++;
fact *= ifactz;
for (k=1; k<h-1; k++) {
// y gradient (+ x, z terms of laplacian, calculated above and below)
LA[index] += fact * ffy * (A[index + yp] - A[index]);
LA[index2] += fact * ffy * (A[index2 + ym] - A[index2]);
index++;
index2++;
}
fact *= factz;
LA[index] += fact * ffy * (A[index + yp] - A[index]);
LA[index2] += fact * ffy * (A[index2 + ym] - A[index2]);
index++;
index2++;
index += h * (d - 1);
index2 += h * (d - 1);
}
}
// Now adding all z components
// All z components except on z edges
index = 1; // Skip first element (in bottom slab)
fact = factx * facty;
for (i=0; i<w; i++) { // full range of x
if (i == 1) fact *= ifactx;
if (i == w-1) fact *= factx;
for (k=1; k<h-1; k++) {
LA[index] += fact * ffz * (A[index + zm] + A[index + zp] - 2.0 * A[index]);
index++;
}
fact *= ifacty;
index += 2; // skip edge slabs
for (j=1; j<d-1; j++) { // full range of y
for (k=1; k<h-1; k++) {
LA[index] += fact * ffz * (A[index + zm] + A[index + zp] - 2.0 * A[index]);
index++;
}
index += 2; // skip edge slabs
}
fact *= facty;
for (k=1; k<h-1; k++) {
LA[index] += fact * ffz * (A[index + zm] + A[index + zp] - 2.0 * A[index]);
index++;
}
index += 2; // skip edge slabs
}
// Edges along z (z components onlz)
index = 0; // Follows bottom slab
index2 = h - 1; // Follows top slab
if (periodic[2]) {
zm = h - 1;
zp = 1;
fact = factx * facty;
for (i=0; i<w; i++) {
if (i == 1) fact *= ifactx;
if (i == w-1) fact *= factx;
LA[index] += fact * ffz * (A[index + zm] + A[index + zp] - 2.0 * A[index]);
LA[index2] += fact * ffz * (A[index2 - zp] + A[index2 - zm] - 2.0 * A[index2]);
index += h;
index2 += h;
fact *= ifacty;
for (j=1; j<d-1; j++) {
LA[index] += fact * ffz * (A[index + zm] + A[index + zp] - 2.0 * A[index]);
LA[index2] += fact * ffz * (A[index2 - zp] + A[index2 - zm] - 2.0 * A[index2]);
index += h;
index2 += h;
}
fact *= facty;
LA[index] += fact * ffz * (A[index + zm] + A[index + zp] - 2.0 * A[index]);
LA[index2] += fact * ffz * (A[index2 - zp] + A[index2 - zm] - 2.0 * A[index2]);
index += h;
index2 += h;
}
} else {
zm = -1;
zp = 1;
fact = factx * facty;
for (i=0; i<w; i++) {
if (i == 1) fact *= ifactx;
if (i == w-1) fact *= factx;
LA[index] += fact * ffz * (A[index + zp] - A[index]);
LA[index2] += fact * ffz * (A[index2 + zm] - A[index2]);
index += h;
index2 += h;
fact *= ifacty;
for (j=1; j<d-1; j++) {
// z gradient (+ x, y terms of laplacian, calculated above)
LA[index] += fact * ffz * (A[index + zp] - A[index]);
LA[index2] += fact * ffz * (A[index2 + zm] - A[index2]);
index += h;
index2 += h;
}
fact *= facty;
LA[index] += fact * ffz * (A[index + zp] - A[index]);
LA[index2] += fact * ffz * (A[index2 + zm] - A[index2]);
index += h;
index2 += h;
}
}
}
}
/*
/// Inversion of preconditioner matrix (e.g. diagonal of the Laplacian)
void integrate_potential::asolve(const std::vector<cvm::real> &b, std::vector<cvm::real> &x)
{
for (size_t i=0; i<nt; i++) {
x[i] = b[i] * inv_lap_diag[i]; // Jacobi preconditioner - little benefit in tests so far
}
return;
}*/
// b : RHS of equation
// x : initial guess for the solution; output is solution
// itol : convergence criterion
void integrate_potential::nr_linbcg_sym(const std::vector<cvm::real> &b, std::vector<cvm::real> &x, const cvm::real &tol,
const int itmax, int &iter, cvm::real &err)
{
cvm::real ak,akden,bk,bkden,bknum,bnrm;
const cvm::real EPS=1.0e-14;
int j;
std::vector<cvm::real> p(nt), r(nt), z(nt);
iter=0;
atimes(x,r);
for (j=0;j<nt;j++) {
r[j]=b[j]-r[j];
}
bnrm=l2norm(b);
if (bnrm < EPS) {
return; // Target is zero, will break relative error calc
}
// asolve(r,z); // precon
bkden = 1.0;
while (iter < itmax) {
++iter;
for (bknum=0.0,j=0;j<nt;j++) {
bknum += r[j]*r[j]; // precon: z[j]*r[j]
}
if (iter == 1) {
for (j=0;j<nt;j++) {
p[j] = r[j]; // precon: p[j] = z[j]
}
} else {
bk=bknum/bkden;
for (j=0;j<nt;j++) {
p[j] = bk*p[j] + r[j]; // precon: bk*p[j] + z[j]
}
}
bkden = bknum;
atimes(p,z);
for (akden=0.0,j=0;j<nt;j++) {
akden += z[j]*p[j];
}
ak = bknum/akden;
for (j=0;j<nt;j++) {
x[j] += ak*p[j];
r[j] -= ak*z[j];
}
// asolve(r,z); // precon
err = l2norm(r)/bnrm;
if (cvm::debug())
std::cout << "iter=" << std::setw(4) << iter+1 << std::setw(12) << err << std::endl;
if (err <= tol)
break;
}
}
cvm::real integrate_potential::l2norm(const std::vector<cvm::real> &x)
{
size_t i;
cvm::real sum = 0.0;
for (i=0;i<x.size();i++)
sum += x[i]*x[i];
return sqrt(sum);
}

View File

@ -97,8 +97,8 @@ public:
/// Whether this grid has been filled with data or is still empty
bool has_data;
/// Return the number of colvars
inline size_t number_of_colvars() const
/// Return the number of colvar objects
inline size_t num_variables() const
{
return nd;
}
@ -374,6 +374,20 @@ public:
}
}
/// Wrap an index vector around periodic boundary conditions
/// or detects edges if non-periodic
inline bool wrap_edge(std::vector<int> & ix) const
{
bool edge = false;
for (size_t i = 0; i < nd; i++) {
if (periodic[i]) {
ix[i] = (ix[i] + nx[i]) % nx[i]; //to ensure non-negative result
} else if (ix[i] == -1 || ix[i] == nx[i]) {
edge = true;
}
}
return edge;
}
/// \brief Report the bin corresponding to the current value of variable i
inline int current_bin_scalar(int const i) const
@ -445,6 +459,12 @@ public:
has_data = true;
}
/// Set the value at the point with linear address i (for speed)
inline void set_value(size_t i, T const &t)
{
data[i] = t;
}
/// \brief Get the change from this to other_grid
/// and store the result in this.
/// this_grid := other_grid - this_grid
@ -518,6 +538,11 @@ public:
return data[this->address(ix) + imult];
}
/// \brief Get the binned value indexed by linear address i
inline T const & value(size_t i) const
{
return data[i];
}
/// \brief Add a constant to all elements (fast loop)
inline void add_constant(T const &t)
@ -1110,20 +1135,20 @@ public:
// write the header
os << "object 1 class gridpositions counts";
size_t icv;
for (icv = 0; icv < number_of_colvars(); icv++) {
for (icv = 0; icv < num_variables(); icv++) {
os << " " << number_of_points(icv);
}
os << "\n";
os << "origin";
for (icv = 0; icv < number_of_colvars(); icv++) {
for (icv = 0; icv < num_variables(); icv++) {
os << " " << (lower_boundaries[icv].real_value + 0.5 * widths[icv]);
}
os << "\n";
for (icv = 0; icv < number_of_colvars(); icv++) {
for (icv = 0; icv < num_variables(); icv++) {
os << "delta";
for (size_t icv2 = 0; icv2 < number_of_colvars(); icv2++) {
for (size_t icv2 = 0; icv2 < num_variables(); icv2++) {
if (icv == icv2) os << " " << widths[icv];
else os << " " << 0.0;
}
@ -1131,7 +1156,7 @@ public:
}
os << "object 2 class gridconnections counts";
for (icv = 0; icv < number_of_colvars(); icv++) {
for (icv = 0; icv < num_variables(); icv++) {
os << " " << number_of_points(icv);
}
os << "\n";
@ -1167,7 +1192,8 @@ public:
/// Constructor from a vector of colvars
colvar_grid_count(std::vector<colvar *> &colvars,
size_t const &def_count = 0);
size_t const &def_count = 0,
bool margin = false);
/// Increment the counter at given position
inline void incr_count(std::vector<int> const &ix)
@ -1210,12 +1236,13 @@ public:
int A0, A1, A2;
std::vector<int> ix = ix0;
// TODO this can be rewritten more concisely with wrap_edge()
if (periodic[n]) {
ix[n]--; wrap(ix);
A0 = data[address(ix)];
A0 = value(ix);
ix = ix0;
ix[n]++; wrap(ix);
A1 = data[address(ix)];
A1 = value(ix);
if (A0 * A1 == 0) {
return 0.; // can't handle empty bins
} else {
@ -1224,10 +1251,10 @@ public:
}
} else if (ix[n] > 0 && ix[n] < nx[n]-1) { // not an edge
ix[n]--;
A0 = data[address(ix)];
A0 = value(ix);
ix = ix0;
ix[n]++;
A1 = data[address(ix)];
A1 = value(ix);
if (A0 * A1 == 0) {
return 0.; // can't handle empty bins
} else {
@ -1238,9 +1265,9 @@ public:
// edge: use 2nd order derivative
int increment = (ix[n] == 0 ? 1 : -1);
// move right from left edge, or the other way around
A0 = data[address(ix)];
ix[n] += increment; A1 = data[address(ix)];
ix[n] += increment; A2 = data[address(ix)];
A0 = value(ix);
ix[n] += increment; A1 = value(ix);
ix[n] += increment; A2 = value(ix);
if (A0 * A1 * A2 == 0) {
return 0.; // can't handle empty bins
} else {
@ -1249,6 +1276,49 @@ public:
}
}
}
/// \brief Return the gradient of discrete count from finite differences
/// on the *same* grid for dimension n
inline cvm::real gradient_finite_diff(const std::vector<int> &ix0,
int n = 0)
{
int A0, A1, A2;
std::vector<int> ix = ix0;
// FIXME this can be rewritten more concisely with wrap_edge()
if (periodic[n]) {
ix[n]--; wrap(ix);
A0 = value(ix);
ix = ix0;
ix[n]++; wrap(ix);
A1 = value(ix);
if (A0 * A1 == 0) {
return 0.; // can't handle empty bins
} else {
return cvm::real(A1 - A0) / (widths[n] * 2.);
}
} else if (ix[n] > 0 && ix[n] < nx[n]-1) { // not an edge
ix[n]--;
A0 = value(ix);
ix = ix0;
ix[n]++;
A1 = value(ix);
if (A0 * A1 == 0) {
return 0.; // can't handle empty bins
} else {
return cvm::real(A1 - A0) / (widths[n] * 2.);
}
} else {
// edge: use 2nd order derivative
int increment = (ix[n] == 0 ? 1 : -1);
// move right from left edge, or the other way around
A0 = value(ix);
ix[n] += increment; A1 = value(ix);
ix[n] += increment; A2 = value(ix);
return (-1.5 * cvm::real(A0) + 2. * cvm::real(A1)
- 0.5 * cvm::real(A2)) * increment / widths[n];
}
}
};
@ -1289,27 +1359,57 @@ public:
has_data = true;
}
/// Return the gradient of the scalar field from finite differences
inline const cvm::real * gradient_finite_diff( const std::vector<int> &ix0 )
/// \brief Return the gradient of the scalar field from finite differences
/// Input coordinates are those of gradient grid, shifted wrt scalar grid
/// Should not be called on edges of scalar grid, provided the latter has margins
/// wrt gradient grid
inline void vector_gradient_finite_diff( const std::vector<int> &ix0, std::vector<cvm::real> &grad)
{
cvm::real A0, A1;
std::vector<int> ix;
if (nd != 2) {
cvm::error("Finite differences available in dimension 2 only.");
return grad;
}
for (unsigned int n = 0; n < nd; n++) {
size_t i, j, k, n;
if (nd == 2) {
for (n = 0; n < 2; n++) {
ix = ix0;
A0 = data[address(ix)];
A0 = value(ix);
ix[n]++; wrap(ix);
A1 = data[address(ix)];
A1 = value(ix);
ix[1-n]++; wrap(ix);
A1 += data[address(ix)];
A1 += value(ix);
ix[n]--; wrap(ix);
A0 += data[address(ix)];
A0 += value(ix);
grad[n] = 0.5 * (A1 - A0) / widths[n];
}
return grad;
} else if (nd == 3) {
cvm::real p[8]; // potential values within cube, indexed in binary (4 i + 2 j + k)
ix = ix0;
int index = 0;
for (i = 0; i<2; i++) {
ix[1] = ix0[1];
for (j = 0; j<2; j++) {
ix[2] = ix0[2];
for (k = 0; k<2; k++) {
wrap(ix);
p[index++] = value(ix);
ix[2]++;
}
ix[1]++;
}
ix[0]++;
}
// The following would be easier to read using binary literals
// 100 101 110 111 000 001 010 011
grad[0] = 0.25 * ((p[4] + p[5] + p[6] + p[7]) - (p[0] + p[1] + p[2] + p[3])) / widths[0];
// 010 011 110 111 000 001 100 101
grad[1] = 0.25 * ((p[2] + p[3] + p[6] + p[7]) - (p[0] + p[1] + p[4] + p[5])) / widths[0];
// 001 011 101 111 000 010 100 110
grad[2] = 0.25 * ((p[1] + p[3] + p[5] + p[7]) - (p[0] + p[2] + p[4] + p[6])) / widths[0];
} else {
cvm::error("Finite differences available in dimension 2 and 3 only.");
}
}
/// \brief Return the value of the function at ix divided by its
@ -1373,10 +1473,6 @@ public:
/// \brief Assuming that the map is a normalized probability density,
/// calculates the entropy (uses widths if they are defined)
cvm::real entropy() const;
private:
// gradient
cvm::real * grad;
};
@ -1390,6 +1486,10 @@ public:
/// should be divided
colvar_grid_count *samples;
/// \brief Provide the floating point weights by which each binned value
/// should be divided (alternate to samples, only one should be non-null)
colvar_grid_scalar *weights;
/// Default constructor
colvar_grid_gradient();
@ -1403,6 +1503,29 @@ public:
/// Constructor from a vector of colvars
colvar_grid_gradient(std::vector<colvar *> &colvars);
/// \brief Get a vector with the binned value(s) indexed by ix, normalized if applicable
inline void vector_value(std::vector<int> const &ix, std::vector<cvm::real> &v) const
{
cvm::real const * p = &value(ix);
if (samples) {
int count = samples->value(ix);
if (count) {
cvm::real invcount = 1.0 / count;
for (size_t i = 0; i < mult; i++) {
v[i] = invcount * p[i];
}
} else {
for (size_t i = 0; i < mult; i++) {
v[i] = 0.0;
}
}
} else {
for (size_t i = 0; i < mult; i++) {
v[i] = p[i];
}
}
}
/// \brief Accumulate the value
inline void acc_value(std::vector<int> const &ix, std::vector<colvarvalue> const &values) {
for (size_t imult = 0; imult < mult; imult++) {
@ -1412,15 +1535,6 @@ public:
samples->incr_count(ix);
}
/// \brief Accumulate the gradient
inline void acc_grad(std::vector<int> const &ix, cvm::real const *grads) {
for (size_t imult = 0; imult < mult; imult++) {
data[address(ix) + imult] += grads[imult];
}
if (samples)
samples->incr_count(ix);
}
/// \brief Accumulate the gradient based on the force (i.e. sums the
/// opposite of the force)
inline void acc_force(std::vector<int> const &ix, cvm::real const *forces) {
@ -1431,6 +1545,17 @@ public:
samples->incr_count(ix);
}
/// \brief Accumulate the gradient based on the force (i.e. sums the
/// opposite of the force) with a non-integer weight
inline void acc_force_weighted(std::vector<int> const &ix,
cvm::real const *forces,
cvm::real weight) {
for (size_t imult = 0; imult < mult; imult++) {
data[address(ix) + imult] -= forces[imult] * weight;
}
weights->acc_value(ix, weight);
}
/// \brief Return the value of the function at ix divided by its
/// number of samples (if the count grid is defined)
virtual inline cvm::real value_output(std::vector<int> const &ix,
@ -1498,5 +1623,70 @@ public:
};
/// Integrate (1D, 2D or 3D) gradients
class integrate_potential : public colvar_grid_scalar
{
public:
integrate_potential();
virtual ~integrate_potential()
{}
/// Constructor from a vector of colvars + gradient grid
integrate_potential (std::vector<colvar *> &colvars, colvar_grid_gradient * gradients);
/// \brief Calculate potential from divergence (in 2D); return number of steps
int integrate (const int itmax, const cvm::real & tol, cvm::real & err);
/// \brief Update matrix containing divergence and boundary conditions
/// based on new gradient point value, in neighboring bins
void update_div_neighbors(const std::vector<int> &ix);
/// \brief Set matrix containing divergence and boundary conditions
/// based on complete gradient grid
void set_div();
/// \brief Add constant to potential so that its minimum value is zero
/// Useful e.g. for output
inline void set_zero_minimum() {
add_constant(-1.0 * minimum_value());
}
protected:
// Reference to gradient grid
colvar_grid_gradient *gradients;
/// Array holding divergence + boundary terms (modified Neumann) if not periodic
std::vector<cvm::real> divergence;
// std::vector<cvm::real> inv_lap_diag; // Inverse of the diagonal of the Laplacian; for conditioning
/// \brief Update matrix containing divergence and boundary conditions
/// called by update_div_neighbors
void update_div_local(const std::vector<int> &ix);
/// Obtain the gradient vector at given location ix, if available
/// or zero if it is on the edge of the gradient grid
/// ix gets wrapped in PBC
void get_grad(cvm::real * g, std::vector<int> &ix);
/// \brief Solve linear system based on CG, valid for symmetric matrices only
void nr_linbcg_sym(const std::vector<cvm::real> &b, std::vector<cvm::real> &x,
const cvm::real &tol, const int itmax, int &iter, cvm::real &err);
/// l2 norm of a vector
cvm::real l2norm(const std::vector<cvm::real> &x);
/// Multiplication by sparse matrix representing Lagrangian (or its transpose)
void atimes(const std::vector<cvm::real> &x, std::vector<cvm::real> &r);
// /// Inversion of preconditioner matrix
// void asolve(const std::vector<cvm::real> &b, std::vector<cvm::real> &x);
};
#endif

View File

@ -24,6 +24,7 @@
#include "colvaratoms.h"
#include "colvarcomp.h"
colvarmodule::colvarmodule(colvarproxy *proxy_in)
{
depth_s = 0;
@ -417,10 +418,10 @@ int colvarmodule::parse_biases(std::string const &conf)
"Please ensure that their forces do not counteract each other.\n");
}
if (biases.size() || use_scripted_forces) {
if (num_biases() || use_scripted_forces) {
cvm::log(cvm::line_marker);
cvm::log("Collective variables biases initialized, "+
cvm::to_str(biases.size())+" in total.\n");
cvm::to_str(num_biases())+" in total.\n");
} else {
if (!use_scripted_forces) {
cvm::log("No collective variables biases were defined.\n");
@ -431,12 +432,37 @@ int colvarmodule::parse_biases(std::string const &conf)
}
int colvarmodule::num_variables() const
{
return colvars.size();
}
int colvarmodule::num_variables_feature(int feature_id) const
{
size_t n = 0;
for (std::vector<colvar *>::const_iterator cvi = colvars.begin();
cvi != colvars.end();
cvi++) {
if ((*cvi)->is_enabled(feature_id)) {
n++;
}
}
return n;
}
int colvarmodule::num_biases() const
{
return biases.size();
}
int colvarmodule::num_biases_feature(int feature_id) const
{
colvarmodule *cv = cvm::main();
size_t n = 0;
for (std::vector<colvarbias *>::iterator bi = cv->biases.begin();
bi != cv->biases.end();
for (std::vector<colvarbias *>::const_iterator bi = biases.begin();
bi != biases.end();
bi++) {
if ((*bi)->is_enabled(feature_id)) {
n++;
@ -448,10 +474,9 @@ int colvarmodule::num_biases_feature(int feature_id) const
int colvarmodule::num_biases_type(std::string const &type) const
{
colvarmodule *cv = cvm::main();
size_t n = 0;
for (std::vector<colvarbias *>::iterator bi = cv->biases.begin();
bi != cv->biases.end();
for (std::vector<colvarbias *>::const_iterator bi = biases.begin();
bi != biases.end();
bi++) {
if ((*bi)->bias_type == type) {
n++;
@ -465,7 +490,7 @@ std::vector<std::string> const colvarmodule::time_dependent_biases() const
{
size_t i;
std::vector<std::string> biases_names;
for (i = 0; i < biases.size(); i++) {
for (i = 0; i < num_biases(); i++) {
if (biases[i]->is_enabled(colvardeps::f_cvb_apply_force) &&
biases[i]->is_enabled(colvardeps::f_cvb_active) &&
(biases[i]->is_enabled(colvardeps::f_cvb_history_dependent) ||
@ -790,7 +815,7 @@ int colvarmodule::calc_biases()
{
// update the biases and communicate their forces to the collective
// variables
if (cvm::debug() && biases.size())
if (cvm::debug() && num_biases())
cvm::log("Updating collective variable biases.\n");
std::vector<colvarbias *>::iterator bi;
@ -852,7 +877,7 @@ int colvarmodule::update_colvar_forces()
std::vector<colvarbias *>::iterator bi;
// sum the forces from all biases for each collective variable
if (cvm::debug() && biases.size())
if (cvm::debug() && num_biases())
cvm::log("Collecting forces from all biases.\n");
cvm::increase_depth();
for (bi = biases_active()->begin(); bi != biases_active()->end(); bi++) {
@ -1073,8 +1098,6 @@ int colvarmodule::reset()
int colvarmodule::setup_input()
{
if (this->size() == 0) return cvm::get_error();
std::string restart_in_name("");
// read the restart configuration, if available
@ -1107,14 +1130,12 @@ int colvarmodule::setup_input()
}
}
return (cvm::get_error() ? COLVARS_ERROR : COLVARS_OK);
return cvm::get_error();
}
int colvarmodule::setup_output()
{
if (this->size() == 0) return cvm::get_error();
int error_code = COLVARS_OK;
// output state file (restart)
@ -1123,7 +1144,8 @@ int colvarmodule::setup_output()
std::string("");
if (restart_out_name.size()) {
cvm::log("The restart output state file will be \""+restart_out_name+"\".\n");
cvm::log("The restart output state file will be \""+
restart_out_name+"\".\n");
}
output_prefix() = proxy->output_prefix();
@ -1154,7 +1176,7 @@ int colvarmodule::setup_output()
set_error_bits(FILE_ERROR);
}
return (cvm::get_error() ? COLVARS_ERROR : COLVARS_OK);
return cvm::get_error();
}
@ -1738,6 +1760,89 @@ int cvm::load_coords_xyz(char const *filename,
}
// Wrappers to proxy functions: these may go in the future
cvm::real cvm::unit_angstrom()
{
return proxy->unit_angstrom();
}
cvm::real cvm::boltzmann()
{
return proxy->boltzmann();
}
cvm::real cvm::temperature()
{
return proxy->temperature();
}
cvm::real cvm::dt()
{
return proxy->dt();
}
void cvm::request_total_force()
{
proxy->request_total_force(true);
}
cvm::rvector cvm::position_distance(atom_pos const &pos1,
atom_pos const &pos2)
{
return proxy->position_distance(pos1, pos2);
}
cvm::real cvm::position_dist2(cvm::atom_pos const &pos1,
cvm::atom_pos const &pos2)
{
return proxy->position_dist2(pos1, pos2);
}
cvm::real cvm::rand_gaussian(void)
{
return proxy->rand_gaussian();
}
bool cvm::replica_enabled()
{
return proxy->replica_enabled();
}
int cvm::replica_index()
{
return proxy->replica_index();
}
int cvm::replica_num()
{
return proxy->replica_num();
}
void cvm::replica_comm_barrier()
{
return proxy->replica_comm_barrier();
}
int cvm::replica_comm_recv(char* msg_data, int buf_len, int src_rep)
{
return proxy->replica_comm_recv(msg_data,buf_len,src_rep);
}
int cvm::replica_comm_send(char* msg_data, int msg_len, int dest_rep)
{
return proxy->replica_comm_send(msg_data,msg_len,dest_rep);
}
// shared pointer to the proxy object
colvarproxy *colvarmodule::proxy = NULL;

View File

@ -39,16 +39,14 @@ You can browse the class hierarchy or the list of source files.
#define FILE_ERROR (1<<4)
#define MEMORY_ERROR (1<<5)
#define FATAL_ERROR (1<<6) // Should be set, or not, together with other bits
#define DELETE_COLVARS (1<<7) // Instruct the caller to delete cvm
//#define DELETE_COLVARS (1<<7) // Instruct the caller to delete cvm
#define COLVARS_NO_SUCH_FRAME (1<<8) // Cannot load the requested frame
#include <iostream>
#include <iomanip>
#include <string>
#include <cstring>
#include <sstream>
#include <fstream>
#include <cmath>
#include <sstream>
#include <string>
#include <vector>
#include <list>
@ -84,12 +82,18 @@ public:
/// Defining an abstract real number allows to switch precision
typedef double real;
/// Override std::pow with a product for n positive integer
static inline real integer_power(real x, int n)
/// Override std::pow with a product for n integer
static inline real integer_power(real const &x, int const n)
{
real result = 1.0;
for (int i = 0; i < n; i++) result *= x;
return result;
// Original code: math_special.h in LAMMPS
double yy, ww;
if (x == 0.0) return 0.0;
int nn = (n > 0) ? n : -n;
ww = x;
for (yy = 1.0; nn != 0; nn >>= 1, ww *=ww) {
if (nn & 1) yy *= ww;
}
return (n > 0) ? yy : 1.0/yy;
}
/// Residue identifier
@ -301,13 +305,23 @@ private:
public:
/// Return how many variables are defined
int num_variables() const;
/// Return how many variables have this feature enabled
int num_variables_feature(int feature_id) const;
/// Return how many biases are defined
int num_biases() const;
/// Return how many biases have this feature enabled
int num_biases_feature(int feature_id) const;
/// Return how many biases are defined with this type
/// Return how many biases of this type are defined
int num_biases_type(std::string const &type) const;
/// Return the names of time-dependent biases with forces enabled
/// Return the names of time-dependent biases with forces enabled (ABF,
/// metadynamics, etc)
std::vector<std::string> const time_dependent_biases() const;
private:
@ -602,8 +616,6 @@ public:
typedef colvarmodule cvm;
#include "colvartypes.h"
std::ostream & operator << (std::ostream &os, cvm::rvector const &v);
std::istream & operator >> (std::istream &is, cvm::rvector &v);
@ -622,6 +634,7 @@ template<typename T> std::string cvm::to_str(T const &x,
return os.str();
}
template<typename T> std::string cvm::to_str(std::vector<T> const &x,
size_t const &width,
size_t const &prec) {
@ -645,70 +658,4 @@ template<typename T> std::string cvm::to_str(std::vector<T> const &x,
}
#include "colvarproxy.h"
inline cvm::real cvm::unit_angstrom()
{
return proxy->unit_angstrom();
}
inline cvm::real cvm::boltzmann()
{
return proxy->boltzmann();
}
inline cvm::real cvm::temperature()
{
return proxy->temperature();
}
inline cvm::real cvm::dt()
{
return proxy->dt();
}
// Replica exchange commands
inline bool cvm::replica_enabled() {
return proxy->replica_enabled();
}
inline int cvm::replica_index() {
return proxy->replica_index();
}
inline int cvm::replica_num() {
return proxy->replica_num();
}
inline void cvm::replica_comm_barrier() {
return proxy->replica_comm_barrier();
}
inline int cvm::replica_comm_recv(char* msg_data, int buf_len, int src_rep) {
return proxy->replica_comm_recv(msg_data,buf_len,src_rep);
}
inline int cvm::replica_comm_send(char* msg_data, int msg_len, int dest_rep) {
return proxy->replica_comm_send(msg_data,msg_len,dest_rep);
}
inline void cvm::request_total_force()
{
proxy->request_total_force(true);
}
inline cvm::rvector cvm::position_distance(atom_pos const &pos1,
atom_pos const &pos2)
{
return proxy->position_distance(pos1, pos2);
}
inline cvm::real cvm::position_dist2(cvm::atom_pos const &pos1,
cvm::atom_pos const &pos2)
{
return proxy->position_dist2(pos1, pos2);
}
inline cvm::real cvm::rand_gaussian(void)
{
return proxy->rand_gaussian();
}
#endif

View File

@ -553,7 +553,8 @@ bool colvarparse::key_lookup(std::string const &conf,
size_t *save_pos)
{
if (cvm::debug()) {
cvm::log("Looking for the keyword \""+std::string(key_in)+"\" and its value.\n");
cvm::log("Looking for the keyword \""+std::string(key_in)+
"\" and its value.\n");
}
// add this keyword to the register (in its camelCase version)

View File

@ -14,6 +14,11 @@
#include <omp.h>
#endif
#if defined(NAMD_TCL) || defined(VMDTCL)
#define COLVARS_TCL
#include <tcl.h>
#endif
#include "colvarmodule.h"
#include "colvarproxy.h"
#include "colvarscript.h"
@ -420,8 +425,10 @@ colvarproxy_script::colvarproxy_script()
colvarproxy_script::~colvarproxy_script() {}
char *colvarproxy_script::script_obj_to_str(unsigned char *obj)
char const *colvarproxy_script::script_obj_to_str(unsigned char *obj)
{
cvm::error("Error: trying to print a script object without a scripting "
"language interface.\n", BUG_ERROR);
return reinterpret_cast<char *>(obj);
}
@ -451,6 +458,140 @@ int colvarproxy_script::run_colvar_gradient_callback(
colvarproxy_tcl::colvarproxy_tcl()
{
_tcl_interp = NULL;
}
colvarproxy_tcl::~colvarproxy_tcl()
{
}
void colvarproxy_tcl::init_tcl_pointers()
{
cvm::error("Error: Tcl support is currently unavailable "
"outside NAMD or VMD.\n", COLVARS_NOT_IMPLEMENTED);
}
char const *colvarproxy_tcl::tcl_obj_to_str(unsigned char *obj)
{
#if defined(COLVARS_TCL)
return Tcl_GetString(reinterpret_cast<Tcl_Obj *>(obj));
#else
return NULL;
#endif
}
int colvarproxy_tcl::tcl_run_force_callback()
{
#if defined(COLVARS_TCL)
Tcl_Interp *const tcl_interp = reinterpret_cast<Tcl_Interp *>(_tcl_interp);
std::string cmd = std::string("calc_colvar_forces ")
+ cvm::to_str(cvm::step_absolute());
int err = Tcl_Eval(tcl_interp, cmd.c_str());
if (err != TCL_OK) {
cvm::log(std::string("Error while executing calc_colvar_forces:\n"));
cvm::error(Tcl_GetStringResult(tcl_interp));
return COLVARS_ERROR;
}
return cvm::get_error();
#else
return COLVARS_NOT_IMPLEMENTED;
#endif
}
int colvarproxy_tcl::tcl_run_colvar_callback(
std::string const &name,
std::vector<const colvarvalue *> const &cvc_values,
colvarvalue &value)
{
#if defined(COLVARS_TCL)
Tcl_Interp *const tcl_interp = reinterpret_cast<Tcl_Interp *>(_tcl_interp);
size_t i;
std::string cmd = std::string("calc_") + name;
for (i = 0; i < cvc_values.size(); i++) {
cmd += std::string(" {") + (*(cvc_values[i])).to_simple_string() +
std::string("}");
}
int err = Tcl_Eval(tcl_interp, cmd.c_str());
const char *result = Tcl_GetStringResult(tcl_interp);
if (err != TCL_OK) {
return cvm::error(std::string("Error while executing ")
+ cmd + std::string(":\n") +
std::string(Tcl_GetStringResult(tcl_interp)), COLVARS_ERROR);
}
std::istringstream is(result);
if (value.from_simple_string(is.str()) != COLVARS_OK) {
cvm::log("Error parsing colvar value from script:");
cvm::error(result);
return COLVARS_ERROR;
}
return cvm::get_error();
#else
return COLVARS_NOT_IMPLEMENTED;
#endif
}
int colvarproxy_tcl::tcl_run_colvar_gradient_callback(
std::string const &name,
std::vector<const colvarvalue *> const &cvc_values,
std::vector<cvm::matrix2d<cvm::real> > &gradient)
{
#if defined(COLVARS_TCL)
Tcl_Interp *const tcl_interp = reinterpret_cast<Tcl_Interp *>(_tcl_interp);
size_t i;
std::string cmd = std::string("calc_") + name + "_gradient";
for (i = 0; i < cvc_values.size(); i++) {
cmd += std::string(" {") + (*(cvc_values[i])).to_simple_string() +
std::string("}");
}
int err = Tcl_Eval(tcl_interp, cmd.c_str());
if (err != TCL_OK) {
return cvm::error(std::string("Error while executing ")
+ cmd + std::string(":\n") +
std::string(Tcl_GetStringResult(tcl_interp)), COLVARS_ERROR);
}
Tcl_Obj **list;
int n;
Tcl_ListObjGetElements(tcl_interp, Tcl_GetObjResult(tcl_interp),
&n, &list);
if (n != int(gradient.size())) {
cvm::error("Error parsing list of gradient values from script: found "
+ cvm::to_str(n) + " values instead of " +
cvm::to_str(gradient.size()));
return COLVARS_ERROR;
}
for (i = 0; i < gradient.size(); i++) {
std::istringstream is(Tcl_GetString(list[i]));
if (gradient[i].from_simple_string(is.str()) != COLVARS_OK) {
cvm::log("Gradient matrix size: " + cvm::to_str(gradient[i].size()));
cvm::log("Gradient string: " + cvm::to_str(Tcl_GetString(list[i])));
cvm::error("Error parsing gradient value from script", COLVARS_ERROR);
return COLVARS_ERROR;
}
}
return cvm::get_error();
#else
return COLVARS_NOT_IMPLEMENTED;
#endif
}
colvarproxy_io::colvarproxy_io() {}
@ -541,6 +682,7 @@ colvarproxy::colvarproxy()
{
colvars = NULL;
b_simulation_running = true;
b_delete_requested = false;
}
@ -556,6 +698,14 @@ int colvarproxy::reset()
}
int colvarproxy::request_deletion()
{
return cvm::error("Error: \"delete\" command is only available in VMD; "
"please use \"reset\" instead.\n",
COLVARS_NOT_IMPLEMENTED);
}
int colvarproxy::setup()
{
return COLVARS_OK;
@ -579,13 +729,3 @@ size_t colvarproxy::restart_frequency()
return 0;
}

View File

@ -415,7 +415,7 @@ public:
};
/// Method for scripting language interface (Tcl or Python)
/// Methods for scripting language interface (Tcl or Python)
class colvarproxy_script {
public:
@ -427,7 +427,7 @@ public:
virtual ~colvarproxy_script();
/// Convert a script object (Tcl or Python call argument) to a C string
virtual char *script_obj_to_str(unsigned char *obj);
virtual char const *script_obj_to_str(unsigned char *obj);
/// Pointer to the scripting interface object
/// (does not need to be allocated in a new interface)
@ -454,6 +454,46 @@ public:
};
/// Methods for using Tcl within Colvars
class colvarproxy_tcl {
public:
/// Constructor
colvarproxy_tcl();
/// Destructor
virtual ~colvarproxy_tcl();
/// Is Tcl available? (trigger initialization if needed)
int tcl_available();
/// Tcl implementation of script_obj_to_str()
char const *tcl_obj_to_str(unsigned char *obj);
/// Run a user-defined colvar forces script
int tcl_run_force_callback();
int tcl_run_colvar_callback(
std::string const &name,
std::vector<const colvarvalue *> const &cvcs,
colvarvalue &value);
int tcl_run_colvar_gradient_callback(
std::string const &name,
std::vector<const colvarvalue *> const &cvcs,
std::vector<cvm::matrix2d<cvm::real> > &gradient);
protected:
/// Pointer to Tcl interpreter object
void *_tcl_interp;
/// Set Tcl pointers
virtual void init_tcl_pointers();
};
/// Methods for data input/output
class colvarproxy_io {
@ -540,6 +580,7 @@ class colvarproxy
public colvarproxy_smp,
public colvarproxy_replicas,
public colvarproxy_script,
public colvarproxy_tcl,
public colvarproxy_io
{
@ -554,6 +595,15 @@ public:
/// Destructor
virtual ~colvarproxy();
/// Request deallocation of the module (currently only implemented by VMD)
virtual int request_deletion();
/// Whether deallocation was requested
inline bool delete_requested()
{
return b_delete_requested;
}
/// \brief Reset proxy state, e.g. requested atoms
virtual int reset();
@ -591,6 +641,9 @@ protected:
/// Whether a simulation is running (warn against irrecovarable errors)
bool b_simulation_running;
/// Whether the entire module should be deallocated by the host engine
bool b_delete_requested;
};

View File

@ -1,5 +1,5 @@
#ifndef COLVARS_VERSION
#define COLVARS_VERSION "2017-10-20"
#define COLVARS_VERSION "2018-01-17"
// 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

View File

@ -74,7 +74,9 @@ int colvarscript::run(int objc, unsigned char *const objv[])
}
if (objc < 2) {
return exec_command(cv_help, NULL, objc, objv);
set_str_result("No commands given: use \"cv help\" "
"for a list of commands.");
return COLVARSCRIPT_ERROR;
}
std::string const cmd(obj_to_str(objv[1]));
@ -123,8 +125,7 @@ int colvarscript::run(int objc, unsigned char *const objv[])
if (cmd == "delete") {
// Note: the delete bit may be ignored by some backends
// it is mostly useful in VMD
colvars->set_error_bits(DELETE_COLVARS);
return COLVARS_OK;
return proxy->request_deletion();
}
if (cmd == "update") {
@ -272,6 +273,11 @@ int colvarscript::proc_colvar(colvar *cv, int objc, unsigned char *const objv[])
return COLVARS_OK;
}
if (subcmd == "run_ave") {
result = (cv->run_ave()).to_simple_string();
return COLVARS_OK;
}
if (subcmd == "width") {
result = cvm::to_str(cv->width, 0, cvm::cv_prec);
return COLVARS_OK;

View File

@ -1,5 +1,5 @@
#ifndef COLVARPROXY_VERSION
#define COLVARPROXY_VERSION "2017-10-20"
#define COLVARPROXY_VERSION "2017-12-01"
// 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