diff --git a/doc/src/PDF/colvars-refman-lammps.pdf b/doc/src/PDF/colvars-refman-lammps.pdf index daa1393269b953d569aff9846a135480dcdd42c7..2d28758819a9a7a0dac700cd4a8239f51257b8e1 100644 Binary files a/doc/src/PDF/colvars-refman-lammps.pdf and b/doc/src/PDF/colvars-refman-lammps.pdf differ diff --git a/lib/colvars/colvar.cpp b/lib/colvars/colvar.cpp index ce76b3b9eb2c8bd5f37b85af332611fa7ca963b3..5a4e8b617a9007fff02e87233b8f8dfebb1e7a25 100644 --- a/lib/colvars/colvar.cpp +++ b/lib/colvars/colvar.cpp @@ -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++) { diff --git a/lib/colvars/colvar.h b/lib/colvars/colvar.h index 20dad2771b6c25afef9c4667ca23f24cfe61d46d..32c329460d06cfa5ceba43e245aa08a0d0810100 100644 --- a/lib/colvars/colvar.h +++ b/lib/colvars/colvar.h @@ -11,8 +11,6 @@ #define COLVAR_H #include <iostream> -#include <iomanip> -#include <cmath> #include "colvarmodule.h" #include "colvarvalue.h" @@ -60,10 +58,13 @@ 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; - + /// \brief Current velocity (previously set by calc() or by read_traj()) colvarvalue const & velocity() 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 { diff --git a/lib/colvars/colvar_UIestimator.h b/lib/colvars/colvar_UIestimator.h index 7fc7f870a10932d176525bb3c6c42d574a572fb1..36ed938119d41232dfdbdc42589cd35f8f67a735 100644 --- a/lib/colvars/colvar_UIestimator.h +++ b/lib/colvars/colvar_UIestimator.h @@ -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]; } diff --git a/lib/colvars/colvaratoms.cpp b/lib/colvars/colvaratoms.cpp index d2a0f0a807d2727500d96e6a19a56eaa0af23f39..1be6f42e594f86adf16a5b4702a631a6bef9c1e7 100644 --- a/lib/colvars/colvaratoms.cpp +++ b/lib/colvars/colvaratoms.cpp @@ -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; diff --git a/lib/colvars/colvaratoms.h b/lib/colvars/colvaratoms.h index 71c587e23084e516c6b66358e97327c8d404822f..0dda6ab792d8ae20afcf6a9aeaa615675f332d1b 100644 --- a/lib/colvars/colvaratoms.h +++ b/lib/colvars/colvaratoms.h @@ -11,6 +11,7 @@ #define COLVARATOMS_H #include "colvarmodule.h" +#include "colvarproxy.h" #include "colvarparse.h" #include "colvardeps.h" diff --git a/lib/colvars/colvarbias.cpp b/lib/colvars/colvarbias.cpp index 301e83e73015a91f14ff75a82c045954083d4074..29620fbee8f103a032b5698c52ca841b2f122cbc 100644 --- a/lib/colvars/colvarbias.cpp +++ b/lib/colvars/colvarbias.cpp @@ -8,6 +8,7 @@ // Colvars repository at GitHub. #include "colvarmodule.h" +#include "colvarproxy.h" #include "colvarvalue.h" #include "colvarbias.h" #include "colvargrid.h" diff --git a/lib/colvars/colvarbias_abf.cpp b/lib/colvars/colvarbias_abf.cpp index e4aea8eb86e7dcb6712fc6dc2c771adeeb6909f6..b3b5b3eb1654df12992b2fafb206d82851d3788b 100644 --- a/lib/colvars/colvarbias_abf.cpp +++ b/lib/colvars/colvarbias_abf.cpp @@ -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() { - if (cvm::debug()) cvm::log("Updating ABF bias " + this->name); + int iter; - 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++) { - bin[i] = samples->current_bin_scalar(i); - } + if (cvm::debug()) cvm::log("Updating ABF bias " + this->name); - } else { + size_t i; + for (i = 0; i < num_variables(); i++) { + bin[i] = samples->current_bin_scalar(i); + } + if (cvm::proxy->total_forces_same_step()) { + // e.g. in LAMMPS, total forces are current + force_bin = bin; + } - for (size_t i = 0; i < colvars.size(); i++) { - bin[i] = samples->current_bin_scalar(i); - } + if (cvm::step_relative() > 0 || cvm::proxy->total_forces_same_step()) { - if ( update_bias && samples->index_ok(force_bin) ) { - // Only if requested and within bounds of the grid... + 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 (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; + 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); + } } - gradients->acc_force(force_bin, system_force); } + 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; + } + 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); } - gradients->write_1D_integral(*pmf_os); + *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) { diff --git a/lib/colvars/colvarbias_abf.h b/lib/colvars/colvarbias_abf.h index 1defe72268c487e82a536d2f5e441e1067897272..0260401292ecc40ee38c5fce3c282f359603591f 100644 --- a/lib/colvars/colvarbias_abf.h +++ b/lib/colvars/colvarbias_abf.h @@ -40,28 +40,44 @@ private: /// Base filename(s) for reading previous gradient data (replaces data from restart file) std::vector<std::string> input_prefix; - bool update_bias; - bool hide_Jacobian; - size_t full_samples; - size_t min_samples; + bool update_bias; + bool hide_Jacobian; + bool b_integrate; + + size_t full_samples; + size_t min_samples; /// frequency for updating output files - int output_freq; + int output_freq; /// Write combined files with a history of all output data? - bool b_history_files; + bool b_history_files; /// Write CZAR output file for stratified eABF (.zgrad) - bool b_czar_window_file; - size_t history_freq; + bool b_czar_window_file; + size_t history_freq; /// Umbrella Integration estimator of free energy from eABF UIestimator::UIestimator eabf_UI; - // Run UI estimator? - bool b_UI_estimator; - // Run CZAR estimator? - bool b_CZAR_estimator; - - /// Cap applied biasing force? + /// Run UI estimator? + bool b_UI_estimator; + /// Run CZAR estimator? + bool b_CZAR_estimator; + + /// 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) { @@ -96,9 +116,9 @@ private: } // shared ABF - bool shared_on; - size_t shared_freq; - int shared_last_step; + bool shared_on; + size_t shared_freq; + int shared_last_step; // Share between replicas -- may be called independently of update virtual int replica_share(); @@ -114,12 +134,12 @@ private: //// Give the count at a given bin index. virtual int bin_count(int bin_index); - /// Write human-readable FE gradients and sample count - void write_gradients_samples(const std::string &prefix, bool append = false); - void write_last_gradients_samples(const std::string &prefix, bool append = false); + /// 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); /// Read human-readable FE gradients and sample count (if not using restart) - void read_gradients_samples(); + void read_gradients_samples(); std::istream& read_state_data(std::istream&); std::ostream& write_state_data(std::ostream&); diff --git a/lib/colvars/colvarbias_alb.cpp b/lib/colvars/colvarbias_alb.cpp index 124a15c5da3f7b59e1e1da4ef5d1d65dc0f60c67..187ecc363a9da559555b7eb296703167f1d26389 100644 --- a/lib/colvars/colvarbias_alb.cpp +++ b/lib/colvars/colvarbias_alb.cpp @@ -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]; diff --git a/lib/colvars/colvarbias_histogram.cpp b/lib/colvars/colvarbias_histogram.cpp index 0722e6384dc7047e5f6eb0bdf0c623537cfaeb53..329b1d9dc08d918295f5ec89a0519a4a292168a0 100644 --- a/lib/colvars/colvarbias_histogram.cpp +++ b/lib/colvars/colvarbias_histogram.cpp @@ -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); } diff --git a/lib/colvars/colvarbias_meta.cpp b/lib/colvars/colvarbias_meta.cpp index b0d154dfc9ebf591f49b15ba517f4bcf7ba10317..f3ae3631a02f2478cb62a7f9a84e7535926501c4 100644 --- a/lib/colvars/colvarbias_meta.cpp +++ b/lib/colvars/colvarbias_meta.cpp @@ -27,7 +27,8 @@ #define PATHSEP "/" #endif - +#include "colvarmodule.h" +#include "colvarproxy.h" #include "colvar.h" #include "colvarbias_meta.h" diff --git a/lib/colvars/colvarbias_restraint.cpp b/lib/colvars/colvarbias_restraint.cpp index 23534f56eb43c1cd0a2e257ed8581449026d0c8f..4ed1a95f942fe664d336b7dfb170f1ea1c3a62a8 100644 --- a/lib/colvars/colvarbias_restraint.cpp +++ b/lib/colvars/colvarbias_restraint.cpp @@ -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,12 +390,14 @@ int colvarbias_restraint_centers_moving::update() int colvarbias_restraint_centers_moving::update_acc_work() { - if (b_output_acc_work) { - if ((cvm::step_relative() > 0) && - (cvm::step_absolute() <= target_nsteps)) { - for (size_t i = 0; i < num_variables(); i++) { - // project forces on the calculated increments at this step - acc_work += colvar_forces[i] * centers_incr[i]; + 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++) { + // project forces on the calculated increments at this step + acc_work += colvar_forces[i] * centers_incr[i]; + } } } } @@ -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, " - "both walls must be provided.\n", INPUT_ERROR); - return INPUT_ERROR; + return cvm::error("Error: at least one variable is periodic, " + "both walls must be provided.\n", INPUT_ERROR); } } } @@ -972,19 +1009,49 @@ 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; + } } - for (i = 0; i < num_variables(); i++) { - if (variables(i)->width != 1.0) - cvm::log("The force constant for colvar \""+variables(i)->name+ - "\" will be rescaled to "+ - cvm::to_str(force_k / (variables(i)->width * variables(i)->width))+ - " according to the specified width.\n"); + // 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 lower wall force constant for colvar \""+ + variables(i)->name+ + "\" will be rescaled to "+ + 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; } diff --git a/lib/colvars/colvarbias_restraint.h b/lib/colvars/colvarbias_restraint.h index b10649cab112f8a10bfedaff0c10823cc7a43f04..3ee999c2624cfe0cfd9997a2ae096106631444ec 100644 --- a/lib/colvars/colvarbias_restraint.h +++ b/lib/colvars/colvarbias_restraint.h @@ -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(); }; diff --git a/lib/colvars/colvarcomp.h b/lib/colvars/colvarcomp.h index b94d798be9f0a3cd49abc27bb8cdaf41f88e8c87..52078a3a3059cb91315ac9f52e62b2f06e1e55f3 100644 --- a/lib/colvars/colvarcomp.h +++ b/lib/colvars/colvarcomp.h @@ -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 an analytical function of atomic coordinates). +/// The only constraints are that: \par /// -/// 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. +/// - 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(); diff --git a/lib/colvars/colvarcomp_distances.cpp b/lib/colvars/colvarcomp_distances.cpp index ce8055843f93d389117f38926e81d97c66bc7954..9911f4c87edef9c17605403f21a0ffc1a45826e9 100644 --- a/lib/colvars/colvarcomp_distances.cpp +++ b/lib/colvars/colvarcomp_distances.cpp @@ -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) diff --git a/lib/colvars/colvardeps.cpp b/lib/colvars/colvardeps.cpp index ac906e7be755fc382d1cb383730522da38860092..a058ad55c2a4911324c9cd07b4ea7bb1e1afb147 100644 --- a/lib/colvars/colvardeps.cpp +++ b/lib/colvars/colvardeps.cpp @@ -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); diff --git a/lib/colvars/colvardeps.h b/lib/colvars/colvardeps.h index bd892fbca8cef746bb4dfad818b1e4577507a735..940eefb01b1a83c5793f9f7f5de68229b7bfe0ad 100644 --- a/lib/colvars/colvardeps.h +++ b/lib/colvars/colvardeps.h @@ -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 diff --git a/lib/colvars/colvargrid.cpp b/lib/colvars/colvargrid.cpp index 9016e2c23a0ce573f8711075ee6ecd93b694aa5c..1ac4aae133c3a225aa8d4a8bb2dbdd1c3ac381cb 100644 --- a/lib/colvars/colvargrid.cpp +++ b/lib/colvars/colvargrid.cpp @@ -14,6 +14,7 @@ #include "colvarcomp.h" #include "colvargrid.h" +#include <ctime> colvar_grid_count::colvar_grid_count() : colvar_grid<size_t>() @@ -22,43 +23,37 @@ colvar_grid_count::colvar_grid_count() } colvar_grid_count::colvar_grid_count(std::vector<int> const &nx_i, - size_t const &def_count) + size_t const &def_count) : colvar_grid<size_t>(nx_i, def_count, 1) {} 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); +} diff --git a/lib/colvars/colvargrid.h b/lib/colvars/colvargrid.h index a01104dba819c2745a9d63d434b8e6832c9d85b7..9a0fe4c8ecb805045a6e19caa4f47aefc9c37434 100644 --- a/lib/colvars/colvargrid.h +++ b/lib/colvars/colvargrid.h @@ -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 = value(ix); + ix[n]++; wrap(ix); + A1 = value(ix); + ix[1-n]++; wrap(ix); + A1 += value(ix); + ix[n]--; wrap(ix); + A0 += value(ix); + grad[n] = 0.5 * (A1 - A0) / widths[n]; + } + } else if (nd == 3) { + + cvm::real p[8]; // potential values within cube, indexed in binary (4 i + 2 j + k) ix = ix0; - A0 = data[address(ix)]; - ix[n]++; wrap(ix); - A1 = data[address(ix)]; - ix[1-n]++; wrap(ix); - A1 += data[address(ix)]; - ix[n]--; wrap(ix); - A0 += data[address(ix)]; - grad[n] = 0.5 * (A1 - A0) / widths[n]; + 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."); } - return grad; } /// \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,23 +1535,25 @@ public: samples->incr_count(ix); } - /// \brief Accumulate the gradient - inline void acc_grad(std::vector<int> const &ix, cvm::real const *grads) { + /// \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) { for (size_t imult = 0; imult < mult; imult++) { - data[address(ix) + imult] += grads[imult]; + data[address(ix) + imult] -= forces[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) { + /// 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]; + data[address(ix) + imult] -= forces[imult] * weight; } - if (samples) - samples->incr_count(ix); + weights->acc_value(ix, weight); } /// \brief Return the value of the function at ix divided by its @@ -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 diff --git a/lib/colvars/colvarmodule.cpp b/lib/colvars/colvarmodule.cpp index 200c2d6848f10cf6a52ab70745c6049c9387dff7..9898e2d5e8264d96ba79d12aac5e70bf9dbfd67d 100644 --- a/lib/colvars/colvarmodule.cpp +++ b/lib/colvars/colvarmodule.cpp @@ -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; diff --git a/lib/colvars/colvarmodule.h b/lib/colvars/colvarmodule.h index 14e5d56701f75730c7bb1ce07764c89edd0b665b..64a98b77a02d59eae598a3f223214fc103018263 100644 --- a/lib/colvars/colvarmodule.h +++ b/lib/colvars/colvarmodule.h @@ -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,16 +616,14 @@ 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); template<typename T> std::string cvm::to_str(T const &x, - size_t const &width, - size_t const &prec) { + size_t const &width, + size_t const &prec) { std::ostringstream os; if (width) os.width(width); if (prec) { @@ -622,9 +634,10 @@ 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) { + size_t const &width, + size_t const &prec) { if (!x.size()) return std::string(""); std::ostringstream os; if (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 diff --git a/lib/colvars/colvarparse.cpp b/lib/colvars/colvarparse.cpp index 9f333b7b76bc029f142fa5443d16c3d73249b77a..43c8c69c040e6b6fdb14a31249f39a293b997d8a 100644 --- a/lib/colvars/colvarparse.cpp +++ b/lib/colvars/colvarparse.cpp @@ -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) diff --git a/lib/colvars/colvarproxy.cpp b/lib/colvars/colvarproxy.cpp index 8160144c6bab1e11d36f4d1b7712e75bce90e6c8..8767d5f459090854cf19ad011f579e752c6d2230 100644 --- a/lib/colvars/colvarproxy.cpp +++ b/lib/colvars/colvarproxy.cpp @@ -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; } - - - - - - - - - - diff --git a/lib/colvars/colvarproxy.h b/lib/colvars/colvarproxy.h index e51ddfbe3bfc16a9d9a77264b86912a2cd8aa34f..bf290482704af3204518c0ffae7daf5ad5d88c0b 100644 --- a/lib/colvars/colvarproxy.h +++ b/lib/colvars/colvarproxy.h @@ -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; + }; diff --git a/lib/colvars/colvars_version.h b/lib/colvars/colvars_version.h index a92a776f8a3abc88700794228bcf4a1e0ad8da51..dc4b8bd07e420d83289b99f458ed2bf935ab210e 100644 --- a/lib/colvars/colvars_version.h +++ b/lib/colvars/colvars_version.h @@ -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 diff --git a/lib/colvars/colvarscript.cpp b/lib/colvars/colvarscript.cpp index 9570acd8327252a99e6394f9bd29d6f6a0bd2f4e..0977496b9e1c252e52c5670f44321796cfafd889 100644 --- a/lib/colvars/colvarscript.cpp +++ b/lib/colvars/colvarscript.cpp @@ -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; diff --git a/src/USER-COLVARS/colvarproxy_lammps_version.h b/src/USER-COLVARS/colvarproxy_lammps_version.h index 45ecea867f17c7fdf2b5bbad780ac860287e8836..79f77dad4e8284d4a7c0c771f0e464626a8f828d 100644 --- a/src/USER-COLVARS/colvarproxy_lammps_version.h +++ b/src/USER-COLVARS/colvarproxy_lammps_version.h @@ -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