diff --git a/doc/src/PDF/colvars-refman-lammps.pdf b/doc/src/PDF/colvars-refman-lammps.pdf index ad15752107cc006471cc4b3e7a400aa1658f26e2..ef1ed3b54577643d29722a9a176cf5b683d75006 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/Makefile.common b/lib/colvars/Makefile.common index 5e2f0f268e93944e6b8ce349a21e7754f10c45b7..e3fa4662e638ef1668a21259f7fb5c021c599e9a 100644 --- a/lib/colvars/Makefile.common +++ b/lib/colvars/Makefile.common @@ -59,8 +59,7 @@ LEPTON_OBJS = \ COLVARS_OBJS = $(COLVARS_SRCS:.cpp=.o) $(LEPTON_OBJS) %.o: %.cpp - $(CXX) $(CXXFLAGS) $(COLVARS_INCFLAGS) \ - -Ilepton/include -DLEPTON -c -o $@ $< + $(CXX) $(CXXFLAGS) $(COLVARS_INCFLAGS) -Ilepton/include -DLEPTON -c -o $@ $< $(COLVARS_LIB): Makefile.deps $(COLVARS_OBJS) $(AR) $(ARFLAGS) $(COLVARS_LIB) $(COLVARS_OBJS) $(LEPTON_OBJS) diff --git a/lib/colvars/Makefile.deps b/lib/colvars/Makefile.deps index fec7b2be123606b013910c273cdeb9e8b08540e8..e0c768dbc9b042b3ae14731b6319929af9a20705 100644 --- a/lib/colvars/Makefile.deps +++ b/lib/colvars/Makefile.deps @@ -14,7 +14,7 @@ $(COLVARS_OBJ_DIR)colvarbias_abf.o: colvarbias_abf.cpp colvarmodule.h \ lepton/include/lepton/Operation.h lepton/include/lepton/CustomFunction.h \ lepton/include/lepton/Exception.h \ lepton/include/lepton/ParsedExpression.h lepton/include/lepton/Parser.h \ - colvarbias_abf.h colvarbias.h colvargrid.h + colvarbias_abf.h colvarbias.h colvargrid.h colvar_UIestimator.h $(COLVARS_OBJ_DIR)colvarbias_alb.o: colvarbias_alb.cpp colvarmodule.h \ colvars_version.h colvartypes.h colvarproxy.h colvarvalue.h \ colvarbias_alb.h colvar.h colvarparse.h colvardeps.h \ @@ -39,7 +39,8 @@ $(COLVARS_OBJ_DIR)colvarbias.o: colvarbias.cpp colvarmodule.h \ lepton/include/lepton/ExpressionTreeNode.h \ lepton/include/lepton/Operation.h lepton/include/lepton/CustomFunction.h \ lepton/include/lepton/Exception.h \ - lepton/include/lepton/ParsedExpression.h lepton/include/lepton/Parser.h + lepton/include/lepton/ParsedExpression.h lepton/include/lepton/Parser.h \ + colvargrid.h $(COLVARS_OBJ_DIR)colvarbias_histogram.o: colvarbias_histogram.cpp \ colvarmodule.h colvars_version.h colvartypes.h colvarproxy.h \ colvarvalue.h colvar.h colvarparse.h colvardeps.h \ @@ -198,9 +199,9 @@ $(COLVARS_OBJ_DIR)colvarmodule.o: colvarmodule.cpp colvarmodule.h \ lepton/include/lepton/Operation.h lepton/include/lepton/CustomFunction.h \ lepton/include/lepton/Exception.h \ lepton/include/lepton/ParsedExpression.h lepton/include/lepton/Parser.h \ - colvarbias.h colvarbias_abf.h colvargrid.h colvarbias_alb.h \ - colvarbias_histogram.h colvarbias_meta.h colvarbias_restraint.h \ - colvarscript.h colvaratoms.h + colvarbias.h colvarbias_abf.h colvargrid.h colvar_UIestimator.h \ + colvarbias_alb.h colvarbias_histogram.h colvarbias_meta.h \ + colvarbias_restraint.h colvarscript.h colvaratoms.h colvarcomp.h $(COLVARS_OBJ_DIR)colvarparse.o: colvarparse.cpp colvarmodule.h \ colvars_version.h colvartypes.h colvarproxy.h colvarvalue.h \ colvarparse.h diff --git a/lib/colvars/colvar.cpp b/lib/colvars/colvar.cpp index d23bd852aaf8be1fe81c41039c9d577af87c7384..d1a398ce4dad6392b4587bffb2508f9ae12b0d19 100644 --- a/lib/colvars/colvar.cpp +++ b/lib/colvars/colvar.cpp @@ -1008,6 +1008,8 @@ int colvar::calc() int colvar::calc_cvcs(int first_cvc, size_t num_cvcs) { + colvarproxy *proxy = cvm::main()->proxy; + int error_code = COLVARS_OK; if (cvm::debug()) cvm::log("Calculating colvar \""+this->name+"\", components "+ @@ -1018,14 +1020,18 @@ int colvar::calc_cvcs(int first_cvc, size_t num_cvcs) return error_code; } - if (cvm::step_relative() > 0) { - // Total force depends on Jacobian derivative from previous timestep + if ((cvm::step_relative() > 0) && (!proxy->total_forces_same_step())){ + // Use Jacobian derivative from previous timestep error_code |= calc_cvc_total_force(first_cvc, num_cvcs); } // atom coordinates are updated by the next line error_code |= calc_cvc_values(first_cvc, num_cvcs); error_code |= calc_cvc_gradients(first_cvc, num_cvcs); error_code |= calc_cvc_Jacobians(first_cvc, num_cvcs); + if (proxy->total_forces_same_step()){ + // Use Jacobian derivative from this timestep + error_code |= calc_cvc_total_force(first_cvc, num_cvcs); + } if (cvm::debug()) cvm::log("Done calculating colvar \""+this->name+"\".\n"); @@ -1043,6 +1049,7 @@ int colvar::collect_cvc_data() if (cvm::step_relative() > 0) { // Total force depends on Jacobian derivative from previous timestep + // collect_cvc_total_forces() uses the previous value of jd error_code |= collect_cvc_total_forces(); } error_code |= collect_cvc_values(); @@ -1471,9 +1478,15 @@ cvm::real colvar::update_forces_energy() // Coupling force is a slow force, to be applied to atomic coords impulse-style f *= cvm::real(time_step_factor); - // The total force acting on the extended variable is f_ext - // This will be used in the next timestep - ft_reported = f_ext; + if (is_enabled(f_cv_subtract_applied_force)) { + // Report a "system" force without the biases on this colvar + // that is, just the spring force + ft_reported = (-0.5 * ext_force_k) * this->dist2_lgrad(xr, x); + } else { + // The total force acting on the extended variable is f_ext + // This will be used in the next timestep + ft_reported = f_ext; + } // leapfrog: starting from x_i, f_i, v_(i-1/2) vr += (0.5 * dt) * f_ext / ext_mass; diff --git a/lib/colvars/colvar.h b/lib/colvars/colvar.h index dfa9e093a537b797284909e2d7a468571cb8cb98..20dad2771b6c25afef9c4667ca23f24cfe61d46d 100644 --- a/lib/colvars/colvar.h +++ b/lib/colvars/colvar.h @@ -60,7 +60,10 @@ public: /// \brief Current actual value (not extended DOF) colvarvalue const & actual_value() 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; @@ -96,6 +99,12 @@ public: { return cv_features; } + static void delete_features() { + for (size_t i=0; i < cv_features.size(); i++) { + delete cv_features[i]; + } + cv_features.clear(); + } /// Implements possible actions to be carried out /// when a given feature is enabled @@ -592,6 +601,10 @@ public: } }; +inline cvm::real const & colvar::force_constant() const +{ + return ext_force_k; +} inline colvarvalue const & colvar::value() const { diff --git a/lib/colvars/colvar_UIestimator.h b/lib/colvars/colvar_UIestimator.h new file mode 100644 index 0000000000000000000000000000000000000000..2fb250b71de01bbb9ffee981209dc14f98bf8b52 --- /dev/null +++ b/lib/colvars/colvar_UIestimator.h @@ -0,0 +1,736 @@ +// -*- c++ -*- + +// This file is part of the Collective Variables module (Colvars). +// The original version of Colvars and its updates are located at: +// https://github.com/colvars/colvars +// Please update all Colvars source files before making any changes. +// If you wish to distribute your changes, please submit them to the +// Colvars repository at GitHub. + +#ifndef COLVAR_UIESTIMATOR_H +#define COLVAR_UIESTIMATOR_H + +#include <cmath> +#include <vector> +#include <iostream> +#include <fstream> +#include <string> + +#include <typeinfo> + +// only for colvar module! +// when integrated into other code, just remove this line and "...cvm::backup_file(...)" +#include "colvarmodule.h" + +namespace UIestimator { + const int Y_SIZE = 21; // defines the range of extended CV with respect to a given CV + // For example, CV=10, width=1, Y_SIZE=21, then eCV=[0-20], having a size of 21 + const int HALF_Y_SIZE = 10; + const int EXTENDED_X_SIZE = HALF_Y_SIZE; + const double EPSILON = 0.000001; // for comparison of float numbers + + class n_matrix { // Stores the distribution matrix of n(x,y) + + public: + n_matrix() {} + n_matrix(const std::vector<double> & lowerboundary, // lowerboundary of x + const std::vector<double> & upperboundary, // upperboundary of + const std::vector<double> & width, // width of x + const int y_size) { // size of y, for example, ysize=7, then when x=1, the distribution of y in [-2,4] is considered + + int i; + + this->lowerboundary = lowerboundary; + this->upperboundary = upperboundary; + 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(y_size, dimension) + EPSILON); + + // the range of the matrix is [lowerboundary, upperboundary] + x_total_size = 1; + for (i = 0; i < dimension; i++) { + x_size.push_back(int((upperboundary[i] - lowerboundary[i]) / width[i] + EPSILON)); + x_total_size *= x_size[i]; + } + + // initialize the internal matrix + matrix.reserve(x_total_size); + for (i = 0; i < x_total_size; i++) { + matrix.push_back(std::vector<int>(y_total_size, 0)); + } + + temp.resize(dimension); + } + + int inline get_value(const std::vector<double> & x, const std::vector<double> & y) { + return matrix[convert_x(x)][convert_y(x, y)]; + } + + void inline set_value(const std::vector<double> & x, const std::vector<double> & y, const int value) { + matrix[convert_x(x)][convert_y(x,y)] = value; + } + + void inline increase_value(const std::vector<double> & x, const std::vector<double> & y, const int value) { + matrix[convert_x(x)][convert_y(x,y)] += value; + } + + private: + std::vector<double> lowerboundary; + std::vector<double> upperboundary; + std::vector<double> width; + int dimension; + std::vector<int> x_size; // the size of x in each dimension + int x_total_size; // the size of x of the internal matrix + int y_size; // the size of y in each dimension + int y_total_size; // the size of y of the internal matrix + + std::vector<std::vector<int> > matrix; // the internal matrix + + std::vector<int> temp; // this vector is used in convert_x and convert_y to save computational resource + + int i, j; + + int convert_x(const std::vector<double> & x) { // convert real x value to its interal index + for (i = 0; i < dimension; i++) { + temp[i] = int((x[i] - lowerboundary[i]) / width[i] + EPSILON); + } + + int index = 0; + for (i = 0; i < dimension; i++) { + if (i + 1 < dimension) { + int x_temp = 1; + for (j = i + 1; j < dimension; j++) + x_temp *= x_size[j]; + index += temp[i] * x_temp; + } + else + index += temp[i]; + } + return index; + } + + int convert_y(const std::vector<double> & x, const std::vector<double> & y) { // convert real y value to its interal index + + int i; + + for (i = 0; i < dimension; i++) { + temp[i] = round((round(y[i] / width[i] + EPSILON) - round(x[i] / width[i] + EPSILON)) + (y_size - 1) / 2 + EPSILON); + } + + int index = 0; + for (i = 0; i < dimension; i++) { + if (i + 1 < dimension) + index += temp[i] * int(pow(y_size, dimension - i - 1) + EPSILON); + else + index += temp[i]; + } + return index; + } + + double round(double r) { + return (r > 0.0) ? floor(r + 0.5) : ceil(r - 0.5); + } + }; + + // vector, store the sum_x, sum_x_square, count_y + template <typename T> + class n_vector { + + public: + n_vector() {} + n_vector(const std::vector<double> & lowerboundary, // lowerboundary of x + const std::vector<double> & upperboundary, // upperboundary of + const std::vector<double> & width, // width of x + const int y_size, // size of y, for example, ysize=7, then when x=1, the distribution of y in [-2,4] is considered + const T & default_value) { // the default value of T + + this->width = width; + this->dimension = lowerboundary.size(); + + x_total_size = 1; + for (int i = 0; i < dimension; i++) { + this->lowerboundary.push_back(lowerboundary[i] - (y_size - 1) / 2 * width[i] - EPSILON); + this->upperboundary.push_back(upperboundary[i] + (y_size - 1) / 2 * width[i] + EPSILON); + + x_size.push_back(int((this->upperboundary[i] - this->lowerboundary[i]) / this->width[i] + EPSILON)); + x_total_size *= x_size[i]; + } + + // initialize the internal vector + vector.resize(x_total_size, default_value); + + temp.resize(dimension); + } + + const T inline get_value(const std::vector<double> & x) { + return vector[convert_x(x)]; + } + + void inline set_value(const std::vector<double> & x, const T value) { + vector[convert_x(x)] = value; + } + + void inline increase_value(const std::vector<double> & x, const T value) { + vector[convert_x(x)] += value; + } + private: + std::vector<double> lowerboundary; + std::vector<double> upperboundary; + std::vector<double> width; + int dimension; + std::vector<int> x_size; // the size of x in each dimension + int x_total_size; // the size of x of the internal matrix + + std::vector<T> vector; // the internal vector + + std::vector<int> temp; // this vector is used in convert_x and convert_y to save computational resource + + int convert_x(const std::vector<double> & x) { // convert real x value to its interal index + + int i, j; + + for (i = 0; i < dimension; i++) { + temp[i] = int((x[i] - lowerboundary[i]) / width[i] + EPSILON); + } + + int index = 0; + for (i = 0; i < dimension; i++) { + if (i + 1 < dimension) { + int x_temp = 1; + for (j = i + 1; j < dimension; j++) + x_temp *= x_size[j]; + index += temp[i] * x_temp; + } + else + index += temp[i]; + } + return index; + } + }; + + class UIestimator { // the implemension of UI estimator + + public: + UIestimator() {} + + //called when (re)start an eabf simulation + UIestimator(const std::vector<double> & lowerboundary, + const std::vector<double> & upperboundary, + const std::vector<double> & width, + const std::vector<double> & krestr, // force constant in eABF + const std::string & output_filename, // the prefix of output files + const int output_freq, + const bool restart, // whether restart from a .count and a .grad file + const std::vector<std::string> & input_filename, // the prefixes of input files + const double temperature) { + + // initialize variables + this->lowerboundary = lowerboundary; + this->upperboundary = upperboundary; + this->width = width; + this->krestr = krestr; + this->output_filename = output_filename; + this->output_freq = output_freq; + this->restart = restart; + this->input_filename = input_filename; + this->temperature = temperature; + + int i, j; + + dimension = lowerboundary.size(); + + for (i = 0; i < dimension; i++) { + sum_x.push_back(n_vector<double>(lowerboundary, upperboundary, width, Y_SIZE, 0.0)); + sum_x_square.push_back(n_vector<double>(lowerboundary, upperboundary, width, Y_SIZE, 0.0)); + + x_av.push_back(n_vector<double>(lowerboundary, upperboundary, width, Y_SIZE, 0.0)); + sigma_square.push_back(n_vector<double>(lowerboundary, upperboundary, width, Y_SIZE, 0.0)); + } + + count_y = n_vector<int>(lowerboundary, upperboundary, width, Y_SIZE, 0); + distribution_x_y = n_matrix(lowerboundary, upperboundary, width, Y_SIZE); + + grad = n_vector<std::vector<double> >(lowerboundary, upperboundary, width, 1, std::vector<double>(dimension, 0.0)); + count = n_vector<int>(lowerboundary, upperboundary, width, 1, 0); + + written = false; + written_1D = false; + + if (dimension == 1) { + std::vector<double> upperboundary_temp = upperboundary; + upperboundary_temp[0] = upperboundary[0] + width[0]; + oneD_pmf = n_vector<double>(lowerboundary, upperboundary_temp, width, 1, 0.0); + } + + if (restart == true) { + input_grad = n_vector<std::vector<double> >(lowerboundary, upperboundary, width, 1, std::vector<double>(dimension, 0.0)); + input_count = n_vector<int>(lowerboundary, upperboundary, width, 1, 0); + + // initialize input_Grad and input_count + // the loop_flag is a n-dimensional vector, increae from lowerboundary to upperboundary when looping + std::vector<double> loop_flag(dimension, 0); + for (i = 0; i < dimension; i++) { + loop_flag[i] = lowerboundary[i]; + } + + i = 0; + while (i >= 0) { + for (j = 0; j < dimension; j++) { + input_grad.set_value(loop_flag, std::vector<double>(dimension,0)); + } + input_count.set_value(loop_flag, 0); + + // iterate over any dimensions + i = dimension - 1; + while (i >= 0) { + loop_flag[i] += width[i]; + if (loop_flag[i] > upperboundary[i] - width[i] + EPSILON) { + loop_flag[i] = lowerboundary[i]; + i--; + } + else + break; + } + } + read_inputfiles(input_filename); + } + } + + ~UIestimator() {} + + // called from MD engine every step + bool update(const int step, std::vector<double> x, std::vector<double> y) { + + int i; + + if (step % output_freq == 0) { + calc_pmf(); + write_files(); + //write_interal_data(); + } + + for (i = 0; i < dimension; i++) { + // for dihedral RC, it is possible that x = 179 and y = -179, should correct it + // may have problem, need to fix + if (x[i] > 150 && y[i] < -150) { + y[i] += 360; + } + if (x[i] < -150 && y[i] > 150) { + y[i] -= 360; + } + + if (x[i] < lowerboundary[i] - EXTENDED_X_SIZE * width[i] + EPSILON || x[i] > upperboundary[i] + EXTENDED_X_SIZE * width[i] - EPSILON \ + || y[i] - x[i] < -HALF_Y_SIZE * width[i] + EPSILON || y[i] - x[i] > HALF_Y_SIZE * width[i] - EPSILON \ + || y[i] - lowerboundary[i] < -HALF_Y_SIZE * width[i] + EPSILON || y[i] - upperboundary[i] > HALF_Y_SIZE * width[i] - EPSILON) + return false; + } + + for (i = 0; i < dimension; i++) { + sum_x[i].increase_value(y, x[i]); + sum_x_square[i].increase_value(y, x[i] * x[i]); + } + count_y.increase_value(y, 1); + + for (i = 0; i < dimension; i++) { + // adapt colvars precision + if (x[i] < lowerboundary[i] + EPSILON || x[i] > upperboundary[i] - EPSILON) + return false; + } + distribution_x_y.increase_value(x, y, 1); + + return true; + } + + // update the output_filename + void update_output_filename(const std::string& filename) { + output_filename = filename; + } + + private: + std::vector<n_vector<double> > sum_x; // the sum of x in each y bin + std::vector<n_vector<double> > sum_x_square; // the sum of x in each y bin + n_vector<int> count_y; // the distribution of y + n_matrix distribution_x_y; // the distribution of <x, y> pair + + int dimension; + + std::vector<double> lowerboundary; + std::vector<double> upperboundary; + std::vector<double> width; + std::vector<double> krestr; + std::string output_filename; + int output_freq; + bool restart; + std::vector<std::string> input_filename; + double temperature; + + n_vector<std::vector<double> > grad; + n_vector<int> count; + + n_vector<double> oneD_pmf; + + n_vector<std::vector<double> > input_grad; + n_vector<int> input_count; + + // used in double integration + std::vector<n_vector<double> > x_av; + std::vector<n_vector<double> > sigma_square; + + bool written; + bool written_1D; + + // calculate gradients from the internal variables + void calc_pmf() { + int norm; + int i, j, k; + + std::vector<double> loop_flag(dimension, 0); + for (i = 0; i < dimension; i++) { + loop_flag[i] = lowerboundary[i] - HALF_Y_SIZE * width[i]; + } + + i = 0; + while (i >= 0) { + norm = count_y.get_value(loop_flag) > 0 ? count_y.get_value(loop_flag) : 1; + for (j = 0; j < dimension; j++) { + x_av[j].set_value(loop_flag, sum_x[j].get_value(loop_flag) / norm); + sigma_square[j].set_value(loop_flag, sum_x_square[j].get_value(loop_flag) / norm - x_av[j].get_value(loop_flag) * x_av[j].get_value(loop_flag)); + } + + // iterate over any dimensions + i = dimension - 1; + while (i >= 0) { + loop_flag[i] += width[i]; + if (loop_flag[i] > upperboundary[i] + HALF_Y_SIZE * width[i] - width[i] + EPSILON) { + loop_flag[i] = lowerboundary[i] - HALF_Y_SIZE * width[i]; + i--; + } + else + break; + } + } + + // double integration + std::vector<double> av(dimension, 0); + std::vector<double> diff_av(dimension, 0); + + std::vector<double> loop_flag_x(dimension, 0); + std::vector<double> loop_flag_y(dimension, 0); + for (i = 0; i < dimension; i++) { + loop_flag_x[i] = lowerboundary[i]; + loop_flag_y[i] = loop_flag_x[i] - HALF_Y_SIZE * width[i]; + } + + i = 0; + while (i >= 0) { + norm = 0; + for (k = 0; k < dimension; k++) { + av[k] = 0; + diff_av[k] = 0; + loop_flag_y[k] = loop_flag_x[k] - HALF_Y_SIZE * width[k]; + } + + int j = 0; + while (j >= 0) { + norm += distribution_x_y.get_value(loop_flag_x, loop_flag_y); + for (k = 0; k < dimension; k++) { + if (sigma_square[k].get_value(loop_flag_y) > EPSILON || sigma_square[k].get_value(loop_flag_y) < -EPSILON) + av[k] += distribution_x_y.get_value(loop_flag_x, loop_flag_y) * ( (loop_flag_x[k] + 0.5 * width[k]) - x_av[k].get_value(loop_flag_y)) / sigma_square[k].get_value(loop_flag_y); + + diff_av[k] += distribution_x_y.get_value(loop_flag_x, loop_flag_y) * (loop_flag_x[k] - loop_flag_y[k]); + } + + // iterate over any dimensions + j = dimension - 1; + while (j >= 0) { + loop_flag_y[j] += width[j]; + if (loop_flag_y[j] > loop_flag_x[j] + HALF_Y_SIZE * width[j] - width[j] + EPSILON) { + loop_flag_y[j] = loop_flag_x[j] - HALF_Y_SIZE * width[j]; + j--; + } + else + break; + } + } + + std::vector<double> grad_temp(dimension, 0); + for (k = 0; k < dimension; k++) { + diff_av[k] /= (norm > 0 ? norm : 1); + av[k] = cvm::boltzmann() * temperature * av[k] / (norm > 0 ? norm : 1); + grad_temp[k] = av[k] - krestr[k] * diff_av[k]; + } + grad.set_value(loop_flag_x, grad_temp); + count.set_value(loop_flag_x, norm); + + // iterate over any dimensions + i = dimension - 1; + while (i >= 0) { + loop_flag_x[i] += width[i]; + if (loop_flag_x[i] > upperboundary[i] - width[i] + EPSILON) { + loop_flag_x[i] = lowerboundary[i]; + i--; + } + else + break; + } + } + } + + + // calculate 1D pmf + void calc_1D_pmf() + { + std::vector<double> last_position(1, 0); + std::vector<double> position(1, 0); + + double min = 0; + double dG = 0; + double i; + + oneD_pmf.set_value(lowerboundary, 0); + last_position = lowerboundary; + for (i = lowerboundary[0] + width[0]; i < upperboundary[0] + EPSILON; i += width[0]) { + position[0] = i + EPSILON; + if (restart == false || input_count.get_value(last_position) == 0) { + dG = oneD_pmf.get_value(last_position) + grad.get_value(last_position)[0] * width[0]; + } + else { + dG = oneD_pmf.get_value(last_position) + ((grad.get_value(last_position)[0] * count.get_value(last_position) + input_grad.get_value(last_position)[0] * input_count.get_value(last_position)) / (count.get_value(last_position) + input_count.get_value(last_position))) * width[0]; + } + if (dG < min) + min = dG; + oneD_pmf.set_value(position, dG); + last_position[0] = i + EPSILON; + } + + for (i = lowerboundary[0]; i < upperboundary[0] + EPSILON; i += width[0]) { + position[0] = i + EPSILON; + oneD_pmf.set_value(position, oneD_pmf.get_value(position) - min); + } + } + + // write 1D pmf + void write_1D_pmf() { + std::string pmf_filename = output_filename + ".UI.pmf"; + + // only for colvars module! + if (written_1D) cvm::backup_file(pmf_filename.c_str()); + + std::ostream* ofile_pmf = cvm::proxy->output_stream(pmf_filename.c_str()); + + std::vector<double> position(1, 0); + for (double i = lowerboundary[0]; i < upperboundary[0] + EPSILON; i += width[0]) { + *ofile_pmf << i << " "; + position[0] = i + EPSILON; + *ofile_pmf << oneD_pmf.get_value(position) << std::endl; + } + cvm::proxy->close_output_stream(pmf_filename.c_str()); + + written_1D = true; + } + + // write heads of the output files + void writehead(std::ostream& os) const { + os << "# " << dimension << std::endl; + for (int i = 0; i < dimension; i++) { + os << "# " << lowerboundary[i] << " " << width[i] << " " << int((upperboundary[i] - lowerboundary[i]) / width[i] + EPSILON) << " " << 0 << std::endl; + } + os << std::endl; + } + + // write interal data, used for testing + void write_interal_data() { + std::string internal_filename = output_filename + ".UI.internal"; + + std::ostream* ofile_internal = cvm::proxy->output_stream(internal_filename.c_str()); + + std::vector<double> loop_flag(dimension, 0); + for (int i = 0; i < dimension; i++) { + loop_flag[i] = lowerboundary[i]; + } + + int n = 0; + while (n >= 0) { + for (int j = 0; j < dimension; j++) { + *ofile_internal << loop_flag[j] + 0.5 * width[j] << " "; + } + + for (int k = 0; k < dimension; k++) { + *ofile_internal << grad.get_value(loop_flag)[k] << " "; + } + + std::vector<double> ii(dimension,0); + for (double i = loop_flag[0] - 10; i < loop_flag[0] + 10 + EPSILON; i+= width[0]) { + for (double j = loop_flag[1] - 10; j< loop_flag[1] + 10 + EPSILON; j+=width[1]) { + ii[0] = i; + ii[1] = j; + *ofile_internal << i <<" "<<j<<" "<< distribution_x_y.get_value(loop_flag,ii)<< " "; + } + } + *ofile_internal << std::endl; + + // iterate over any dimensions + n = dimension - 1; + while (n >= 0) { + loop_flag[n] += width[n]; + if (loop_flag[n] > upperboundary[n] - width[n] + EPSILON) { + loop_flag[n] = lowerboundary[n]; + n--; + } + else + break; + } + } + cvm::proxy->close_output_stream(internal_filename.c_str()); + } + + // write output files + void write_files() { + std::string grad_filename = output_filename + ".UI.grad"; + std::string hist_filename = output_filename + ".UI.hist.grad"; + std::string count_filename = output_filename + ".UI.count"; + + int i, j; +// + // only for colvars module! + if (written) cvm::backup_file(grad_filename.c_str()); + //if (written) cvm::backup_file(hist_filename.c_str()); + if (written) cvm::backup_file(count_filename.c_str()); + + std::ostream* ofile = cvm::proxy->output_stream(grad_filename.c_str()); + std::ostream* ofile_hist = cvm::proxy->output_stream(hist_filename.c_str(), std::ios::app); + std::ostream* ofile_count = cvm::proxy->output_stream(count_filename.c_str()); + + writehead(*ofile); + writehead(*ofile_hist); + writehead(*ofile_count); + + if (dimension == 1) { + calc_1D_pmf(); + write_1D_pmf(); + } + + std::vector<double> loop_flag(dimension, 0); + for (i = 0; i < dimension; i++) { + loop_flag[i] = lowerboundary[i]; + } + + i = 0; + while (i >= 0) { + for (j = 0; j < dimension; j++) { + *ofile << loop_flag[j] + 0.5 * width[j] << " "; + *ofile_hist << loop_flag[j] + 0.5 * width[j] << " "; + *ofile_count << loop_flag[j] + 0.5 * width[j] << " "; + } + + if (restart == false) { + for (j = 0; j < dimension; j++) { + *ofile << grad.get_value(loop_flag)[j] << " "; + *ofile_hist << grad.get_value(loop_flag)[j] << " "; + } + *ofile << std::endl; + *ofile_hist << std::endl; + *ofile_count << count.get_value(loop_flag) << " " <<std::endl; + } + else { + double final_grad = 0; + for (j = 0; j < dimension; j++) { + int total_count_temp = (count.get_value(loop_flag) + input_count.get_value(loop_flag)); + if (input_count.get_value(loop_flag) == 0) + final_grad = grad.get_value(loop_flag)[j]; + else + final_grad = ((grad.get_value(loop_flag)[j] * count.get_value(loop_flag) + input_grad.get_value(loop_flag)[j] * input_count.get_value(loop_flag)) / total_count_temp); + *ofile << final_grad << " "; + *ofile_hist << final_grad << " "; + } + *ofile << std::endl; + *ofile_hist << std::endl; + *ofile_count << (count.get_value(loop_flag) + input_count.get_value(loop_flag)) << " " <<std::endl; + } + + // iterate over any dimensions + i = dimension - 1; + while (i >= 0) { + loop_flag[i] += width[i]; + if (loop_flag[i] > upperboundary[i] - width[i] + EPSILON) { + loop_flag[i] = lowerboundary[i]; + i--; + *ofile << std::endl; + *ofile_hist << std::endl; + *ofile_count << std::endl; + } + else + break; + } + } + cvm::proxy->close_output_stream(grad_filename.c_str()); + cvm::proxy->close_output_stream(hist_filename.c_str()); + cvm::proxy->close_output_stream(count_filename.c_str()); + + written = true; + } + + // read input files + void read_inputfiles(const std::vector<std::string> input_filename) + { + char sharp; + double nothing; + int dimension_temp; + int i, j, k, l, m; + + std::vector<double> loop_bin_size(dimension, 0); + std::vector<double> position_temp(dimension, 0); + std::vector<double> grad_temp(dimension, 0); + int count_temp = 0; + for (i = 0; i < int(input_filename.size()); i++) { + int size = 1 , size_temp = 0; + + std::string count_filename = input_filename[i] + ".UI.count"; + std::string grad_filename = input_filename[i] + ".UI.grad"; + + std::ifstream count_file(count_filename.c_str(), std::ios::in); + std::ifstream grad_file(grad_filename.c_str(), std::ios::in); + + count_file >> sharp >> dimension_temp; + grad_file >> sharp >> dimension_temp; + + for (j = 0; j < dimension; j++) { + count_file >> sharp >> nothing >> nothing >> size_temp >> nothing; + grad_file >> sharp >> nothing >> nothing >> nothing >> nothing; + size *= size_temp; + } + + for (j = 0; j < size; j++) { + do { + for (k = 0; k < dimension; k++) { + count_file >> position_temp[k]; + grad_file >> nothing; + } + + for (l = 0; l < dimension; l++) { + grad_file >> grad_temp[l]; + } + count_file >> count_temp; + } + while (position_temp[i] < lowerboundary[i] - EPSILON || position_temp[i] > upperboundary[i] + EPSILON); + + if (count_temp == 0) { + continue; + } + + for (m = 0; m < dimension; m++) { + grad_temp[m] = (grad_temp[m] * count_temp + input_grad.get_value(position_temp)[m] * input_count.get_value(position_temp)) / (count_temp + input_count.get_value(position_temp)); + } + input_grad.set_value(position_temp, grad_temp); + input_count.increase_value(position_temp, count_temp); + } + + count_file.close(); + grad_file.close(); + } + } + }; +}; + +#endif diff --git a/lib/colvars/colvaratoms.cpp b/lib/colvars/colvaratoms.cpp index 9b4a922e3fbb2322455c17adca9194d2032ab6a1..d2a0f0a807d2727500d96e6a19a56eaa0af23f39 100644 --- a/lib/colvars/colvaratoms.cpp +++ b/lib/colvars/colvaratoms.cpp @@ -817,6 +817,18 @@ int cvm::atom_group::create_sorted_ids(void) } +int cvm::atom_group::overlap(const atom_group &g1, const atom_group &g2){ + for (cvm::atom_const_iter ai1 = g1.begin(); ai1 != g1.end(); ai1++) { + for (cvm::atom_const_iter ai2 = g2.begin(); ai2 != g2.end(); ai2++) { + if (ai1->id == ai2->id) { + return (ai1->id + 1); // 1-based index to allow boolean usage + } + } + } + return 0; +} + + void cvm::atom_group::center_ref_pos() { ref_pos_cog = cvm::atom_pos(0.0, 0.0, 0.0); diff --git a/lib/colvars/colvaratoms.h b/lib/colvars/colvaratoms.h index 6113fb38a938b37a56d54825c51d43252d5b54db..71c587e23084e516c6b66358e97327c8d404822f 100644 --- a/lib/colvars/colvaratoms.h +++ b/lib/colvars/colvaratoms.h @@ -214,6 +214,12 @@ public: { return ag_features; } + static void delete_features() { + for (size_t i=0; i < ag_features.size(); i++) { + delete ag_features[i]; + } + ag_features.clear(); + } protected: @@ -280,6 +286,10 @@ public: /// Allocates and populates the sorted list of atom ids int create_sorted_ids(void); + /// Detect whether two groups share atoms + /// If yes, returns 1-based number of a common atom; else, returns 0 + static int overlap(const atom_group &g1, const atom_group &g2); + /// \brief When updating atomic coordinates, translate them to align with the /// center of mass of the reference coordinates bool b_center; diff --git a/lib/colvars/colvarbias.cpp b/lib/colvars/colvarbias.cpp index 636727ca39b4f90b8b9686a64b51845857843c13..902ea4605d2f72aec3d79a546463ef2321d5180d 100644 --- a/lib/colvars/colvarbias.cpp +++ b/lib/colvars/colvarbias.cpp @@ -10,6 +10,7 @@ #include "colvarmodule.h" #include "colvarvalue.h" #include "colvarbias.h" +#include "colvargrid.h" colvarbias::colvarbias(char const *key) @@ -31,12 +32,14 @@ int colvarbias::init(std::string const &conf) { colvarparse::init(conf); + size_t i = 0; + if (name.size() == 0) { // first initialization cvm::log("Initializing a new \""+bias_type+"\" instance.\n"); - rank = cvm::num_biases_type(bias_type); + rank = cvm::main()->num_biases_type(bias_type); get_keyval(conf, "name", name, bias_type+cvm::to_str(rank)); { @@ -62,7 +65,7 @@ int colvarbias::init(std::string const &conf) INPUT_ERROR); return INPUT_ERROR; } - for (size_t i = 0; i < colvar_names.size(); i++) { + for (i = 0; i < colvar_names.size(); i++) { add_colvar(colvar_names[i]); } } @@ -148,6 +151,13 @@ int colvarbias::clear() } +int colvarbias::clear_state_data() +{ + // no mutable content to delete for base class + return COLVARS_OK; +} + + int colvarbias::add_colvar(std::string const &cv_name) { if (colvar *cv = cvm::colvar_by_name(cv_name)) { @@ -164,6 +174,8 @@ int colvarbias::add_colvar(std::string const &cv_name) colvar_forces.back().is_derivative(); // colvar constraints are not applied to the force colvar_forces.back().reset(); + previous_colvar_forces.push_back(colvar_forces.back()); + cv->biases.push_back(this); // add back-reference to this bias to colvar if (is_enabled(f_cvb_apply_force)) { @@ -204,7 +216,8 @@ int colvarbias::update() void colvarbias::communicate_forces() { - for (size_t i = 0; i < num_variables(); i++) { + size_t i = 0; + for (i = 0; i < num_variables(); i++) { if (cvm::debug()) { cvm::log("Communicating a force to colvar \""+ variables(i)->name+"\".\n"); @@ -216,6 +229,9 @@ void colvarbias::communicate_forces() // aware of this bias' time_step_factor variables(i)->add_bias_force(cvm::real(time_step_factor) * colvar_forces[i]); } + for (i = 0; i < num_variables(); i++) { + previous_colvar_forces[i] = colvar_forces[i]; + } } @@ -389,6 +405,248 @@ std::ostream & colvarbias::write_traj(std::ostream &os) return os; } + + +colvarbias_ti::colvarbias_ti(char const *key) + : colvarbias(key) +{ + provide(f_cvb_calc_ti_samples); + ti_avg_forces = NULL; + ti_count = NULL; +} + + +colvarbias_ti::~colvarbias_ti() +{ + colvarbias_ti::clear_state_data(); +} + + +int colvarbias_ti::clear_state_data() +{ + if (ti_avg_forces != NULL) { + delete ti_avg_forces; + ti_avg_forces = NULL; + } + if (ti_count != NULL) { + delete ti_count; + ti_count = NULL; + } + return COLVARS_OK; +} + + +int colvarbias_ti::init(std::string const &conf) +{ + int error_code = COLVARS_OK; + + get_keyval_feature(this, conf, "writeTISamples", + f_cvb_write_ti_samples, + is_enabled(f_cvb_write_ti_samples)); + + get_keyval_feature(this, conf, "writeTIPMF", + f_cvb_write_ti_pmf, + is_enabled(f_cvb_write_ti_pmf)); + + if ((num_variables() > 1) && is_enabled(f_cvb_write_ti_pmf)) { + return cvm::error("Error: only 1-dimensional PMFs can be written " + "on the fly.\n" + "Consider using writeTISamples instead and " + "post-processing the sampled free-energy gradients.\n", + COLVARS_NOT_IMPLEMENTED); + } else { + error_code |= init_grids(); + } + + if (is_enabled(f_cvb_write_ti_pmf)) { + enable(f_cvb_write_ti_samples); + } + + if (is_enabled(f_cvb_calc_ti_samples)) { + std::vector<std::string> const time_biases = + cvm::main()->time_dependent_biases(); + if (time_biases.size() > 0) { + if ((time_biases.size() > 1) || (time_biases[0] != this->name)) { + for (size_t i = 0; i < num_variables(); i++) { + if (! variables(i)->is_enabled(f_cv_subtract_applied_force)) { + return cvm::error("Error: cannot collect TI samples while other " + "time-dependent biases are active and not all " + "variables have subtractAppliedForces on.\n", + INPUT_ERROR); + } + } + } + } + } + + return error_code; +} + + +int colvarbias_ti::init_grids() +{ + if (is_enabled(f_cvb_calc_ti_samples)) { + if (ti_avg_forces == NULL) { + ti_bin.resize(num_variables()); + ti_system_forces.resize(num_variables()); + for (size_t icv = 0; icv < num_variables(); icv++) { + ti_system_forces[icv].type(variables(icv)->value()); + ti_system_forces[icv].is_derivative(); + ti_system_forces[icv].reset(); + } + ti_avg_forces = new colvar_grid_gradient(colvars); + ti_count = new colvar_grid_count(colvars); + ti_avg_forces->samples = ti_count; + ti_count->has_parent_data = true; + } + } + + return COLVARS_OK; +} + + +int colvarbias_ti::update() +{ + return update_system_forces(NULL); +} + + +int colvarbias_ti::update_system_forces(std::vector<colvarvalue> const + *subtract_forces) +{ + if (! is_enabled(f_cvb_calc_ti_samples)) { + return COLVARS_OK; + } + + has_data = true; + + if (cvm::debug()) { + cvm::log("Updating system forces for bias "+this->name+"\n"); + } + + // Collect total colvar forces from the previous step + size_t i; + if (cvm::step_relative() > 0) { + if (ti_avg_forces->index_ok(ti_bin)) { + for (i = 0; i < num_variables(); i++) { + if (variables(i)->is_enabled(f_cv_subtract_applied_force)) { + // this colvar is already subtracting all applied forces + ti_system_forces[i] = variables(i)->total_force(); + } else { + ti_system_forces[i] = variables(i)->total_force() - + ((subtract_forces != NULL) ? + (*subtract_forces)[i] : previous_colvar_forces[i]); + } + } + ti_avg_forces->acc_value(ti_bin, ti_system_forces); + } + } + + // Set the index to be used in the next iteration, when total forces come in + for (i = 0; i < num_variables(); i++) { + ti_bin[i] = ti_avg_forces->current_bin_scalar(i); + } + + return COLVARS_OK; +} + + +std::string const colvarbias_ti::get_state_params() const +{ + return std::string(""); +} + + +int colvarbias_ti::set_state_params(std::string const &state_conf) +{ + return COLVARS_OK; +} + + +std::ostream & colvarbias_ti::write_state_data(std::ostream &os) +{ + if (! is_enabled(f_cvb_calc_ti_samples)) { + return os; + } + os << "\nhistogram\n"; + ti_count->write_raw(os); + os << "\nsystem_forces\n"; + ti_avg_forces->write_raw(os); + return os; +} + + +std::istream & colvarbias_ti::read_state_data(std::istream &is) +{ + if (! is_enabled(f_cvb_calc_ti_samples)) { + return is; + } + if (cvm::debug()) { + cvm::log("Reading state data for the TI estimator.\n"); + } + if (! read_state_data_key(is, "histogram")) { + return is; + } + if (! ti_count->read_raw(is)) { + return is; + } + if (! read_state_data_key(is, "system_forces")) { + return is; + } + if (! ti_avg_forces->read_raw(is)) { + return is; + } + if (cvm::debug()) { + cvm::log("Done reading state data for the TI estimator.\n"); + } + return is; +} + + +int colvarbias_ti::write_output_files() +{ + if (!has_data) { + // nothing to write + return COLVARS_OK; + } + + std::string const ti_output_prefix = cvm::output_prefix()+"."+this->name; + + std::ostream *os = NULL; + + if (is_enabled(f_cvb_write_ti_samples)) { + std::string const ti_count_file_name(ti_output_prefix+".ti.count"); + os = cvm::proxy->output_stream(ti_count_file_name); + if (os) { + ti_count->write_multicol(*os); + cvm::proxy->close_output_stream(ti_count_file_name); + } + + std::string const ti_grad_file_name(ti_output_prefix+".ti.grad"); + os = cvm::proxy->output_stream(ti_grad_file_name); + if (os) { + ti_avg_forces->write_multicol(*os); + cvm::proxy->close_output_stream(ti_grad_file_name); + } + } + + if (is_enabled(f_cvb_write_ti_pmf)) { + std::string const pmf_file_name(ti_output_prefix+".ti.pmf"); + cvm::log("Writing TI PMF to file \""+pmf_file_name+"\".\n"); + os = cvm::proxy->output_stream(pmf_file_name); + if (os) { + // get the FE gradient + ti_avg_forces->multiply_constant(-1.0); + ti_avg_forces->write_1D_integral(*os); + ti_avg_forces->multiply_constant(-1.0); + cvm::proxy->close_output_stream(pmf_file_name); + } + } + + return COLVARS_OK; +} + + // Static members std::vector<colvardeps::feature *> colvarbias::cvb_features; diff --git a/lib/colvars/colvarbias.h b/lib/colvars/colvarbias.h index a147cd3210486cc74d80f5e0c0e5f859e0ea1b70..083b9d73036da056a2c76d3cfb8cef600049c1f3 100644 --- a/lib/colvars/colvarbias.h +++ b/lib/colvars/colvarbias.h @@ -109,6 +109,9 @@ public: /// \brief Delete everything virtual int clear(); + /// \brief Delete only the allocatable data (save memory) + virtual int clear_state_data(); + /// Destructor virtual ~colvarbias(); @@ -183,6 +186,12 @@ public: { return cvb_features; } + static void delete_features() { + for (size_t i=0; i < cvb_features.size(); i++) { + delete cvb_features[i]; + } + cvb_features.clear(); + } protected: @@ -194,6 +203,9 @@ protected: /// \brief Current forces from this bias to the variables std::vector<colvarvalue> colvar_forces; + /// \brief Forces last applied by this bias to the variables + std::vector<colvarvalue> previous_colvar_forces; + /// \brief Current energy of this bias (colvar_forces should be obtained by deriving this) cvm::real bias_energy; @@ -209,4 +221,48 @@ protected: }; + +class colvar_grid_gradient; +class colvar_grid_count; + +/// \brief Base class for unconstrained thermodynamic-integration FE estimator +class colvarbias_ti : public virtual colvarbias { +public: + + colvarbias_ti(char const *key); + virtual ~colvarbias_ti(); + + virtual int clear_state_data(); + + virtual int init(std::string const &conf); + virtual int init_grids(); + virtual int update(); + + /// Subtract applied forces (either last forces or argument) from the total + /// forces + virtual int update_system_forces(std::vector<colvarvalue> const + *subtract_forces); + + virtual std::string const get_state_params() const; + virtual int set_state_params(std::string const &state_conf); + virtual std::ostream & write_state_data(std::ostream &os); + virtual std::istream & read_state_data(std::istream &is); + virtual int write_output_files(); + +protected: + + /// \brief Forces exerted from the system to the associated variables + std::vector<colvarvalue> ti_system_forces; + + /// Averaged system forces + colvar_grid_gradient *ti_avg_forces; + + /// Histogram of sampled data + colvar_grid_count *ti_count; + + /// Because total forces may be from the last simulation step, + /// store the index of the variables then + std::vector<int> ti_bin; +}; + #endif diff --git a/lib/colvars/colvarbias_abf.cpp b/lib/colvars/colvarbias_abf.cpp index a96fc21d644e750d1a574596e4c3f0b99c87930f..5f5fe149784362e95807c2cfc7e6ef207ca90726 100644 --- a/lib/colvars/colvarbias_abf.cpp +++ b/lib/colvars/colvarbias_abf.cpp @@ -14,6 +14,8 @@ colvarbias_abf::colvarbias_abf(char const *key) : colvarbias(key), + b_UI_estimator(false), + b_CZAR_estimator(false), system_force(NULL), gradients(NULL), samples(NULL), @@ -159,6 +161,7 @@ int colvarbias_abf::init(std::string const &conf) // Data for eABF 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); @@ -187,8 +190,38 @@ int colvarbias_abf::init(std::string const &conf) read_gradients_samples(); } - cvm::log("Finished ABF setup.\n"); + // if extendedLangrangian is on, then call UI estimator + if (b_extended) { + get_keyval(conf, "UIestimator", b_UI_estimator, false); + + if (b_UI_estimator) { + std::vector<double> UI_lowerboundary; + std::vector<double> UI_upperboundary; + std::vector<double> UI_width; + std::vector<double> UI_krestr; + + bool UI_restart = (input_prefix.size() > 0); + + for (size_t i = 0; i < colvars.size(); i++) + { + UI_lowerboundary.push_back(colvars[i]->lower_boundary); + UI_upperboundary.push_back(colvars[i]->upper_boundary); + UI_width.push_back(colvars[i]->width); + UI_krestr.push_back(colvars[i]->force_constant()); + } + eabf_UI = UIestimator::UIestimator(UI_lowerboundary, + UI_upperboundary, + UI_width, + UI_krestr, // force constant in eABF + output_prefix, // the prefix of output files + cvm::restart_out_freq, + UI_restart, // whether restart from a .count and a .grad file + input_prefix, // the prefixes of input files + cvm::temperature()); + } + } + cvm::log("Finished ABF setup.\n"); return COLVARS_OK; } @@ -332,7 +365,7 @@ int colvarbias_abf::update() } // update the output prefix; TODO: move later to setup_output() function - if (cvm::num_biases_feature(colvardeps::f_cvb_calc_pmf) == 1) { + if (cvm::main()->num_biases_feature(colvardeps::f_cvb_calc_pmf) == 1) { // This is the only bias computing PMFs output_prefix = cvm::output_prefix(); } else { @@ -364,6 +397,20 @@ int colvarbias_abf::update() cvm::log("Prepared sample and gradient buffers at step "+cvm::to_str(cvm::step_absolute())+"."); } + // 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++) + { + x[i] = colvars[i]->actual_value(); + y[i] = colvars[i]->value(); + } + eabf_UI.update_output_filename(output_prefix); + eabf_UI.update(cvm::step_absolute(), x, y); + } + return COLVARS_OK; } @@ -479,8 +526,8 @@ void colvarbias_abf::write_gradients_samples(const std::string &prefix, bool app cvm::proxy->close_output_stream(pmf_out_name); } - if (z_gradients) { - // Write eABF-related quantities + if (b_CZAR_estimator) { + // Write eABF CZAR-related quantities std::string z_samples_out_name = prefix + ".zcount"; @@ -588,7 +635,7 @@ void colvarbias_abf::read_gradients_samples() is.close(); } - if (z_gradients) { + if (b_CZAR_estimator) { // Read eABF z-averaged data for CZAR cvm::log("Reading z-histogram from " + z_samples_in_name + " and z-gradient from " + z_gradients_in_name); @@ -621,7 +668,7 @@ std::ostream & colvarbias_abf::write_state_data(std::ostream& os) os << "\ngradient\n"; gradients->write_raw(os, 8); - if (z_gradients) { + if (b_CZAR_estimator) { os.setf(std::ios::fmtflags(0), std::ios::floatfield); // default floating-point format os << "\nz_samples\n"; z_samples->write_raw(os, 8); @@ -655,7 +702,7 @@ std::istream & colvarbias_abf::read_state_data(std::istream& is) return is; } - if (z_gradients) { + if (b_CZAR_estimator) { if (! read_state_data_key(is, "z_samples")) { return is; diff --git a/lib/colvars/colvarbias_abf.h b/lib/colvars/colvarbias_abf.h index 41a5475fa70087ca709e4ca2be9f0f118874a311..1defe72268c487e82a536d2f5e441e1067897272 100644 --- a/lib/colvars/colvarbias_abf.h +++ b/lib/colvars/colvarbias_abf.h @@ -17,6 +17,7 @@ #include "colvarbias.h" #include "colvargrid.h" +#include "colvar_UIestimator.h" typedef cvm::real* gradient_t; @@ -50,6 +51,12 @@ private: /// Write CZAR output file for stratified eABF (.zgrad) 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? bool cap_force; diff --git a/lib/colvars/colvarbias_meta.cpp b/lib/colvars/colvarbias_meta.cpp index 66806fc9fca01d57a596344ff2fd62a120272442..b0d154dfc9ebf591f49b15ba517f4bcf7ba10317 100644 --- a/lib/colvars/colvarbias_meta.cpp +++ b/lib/colvars/colvarbias_meta.cpp @@ -33,7 +33,7 @@ colvarbias_meta::colvarbias_meta(char const *key) - : colvarbias(key) + : colvarbias(key), colvarbias_ti(key) { new_hills_begin = hills.end(); hills_traj_os = NULL; @@ -44,6 +44,7 @@ colvarbias_meta::colvarbias_meta(char const *key) int colvarbias_meta::init(std::string const &conf) { colvarbias::init(conf); + colvarbias_ti::init(conf); enable(f_cvb_calc_pmf); @@ -104,7 +105,7 @@ int colvarbias_meta::init(std::string const &conf) get_keyval(conf, "dumpFreeEnergyFile", dump_fes, true, colvarparse::parse_silent); if (get_keyval(conf, "saveFreeEnergyFile", dump_fes_save, false, colvarparse::parse_silent)) { cvm::log("Option \"saveFreeEnergyFile\" is deprecated, " - "please use \"keepFreeEnergyFile\" instead."); + "please use \"keepFreeEnergyFiles\" instead."); } get_keyval(conf, "keepFreeEnergyFiles", dump_fes_save, dump_fes_save); @@ -230,15 +231,7 @@ int colvarbias_meta::init_ebmeta_params(std::string const &conf) colvarbias_meta::~colvarbias_meta() { - if (hills_energy) { - delete hills_energy; - hills_energy = NULL; - } - - if (hills_energy_gradients) { - delete hills_energy_gradients; - hills_energy_gradients = NULL; - } + colvarbias_meta::clear_state_data(); if (replica_hills_os) { cvm::proxy->close_output_stream(replica_hills_file); @@ -250,13 +243,31 @@ colvarbias_meta::~colvarbias_meta() hills_traj_os = NULL; } - if(target_dist) { + if (target_dist) { delete target_dist; target_dist = NULL; } } +int colvarbias_meta::clear_state_data() +{ + if (hills_energy) { + delete hills_energy; + hills_energy = NULL; + } + + if (hills_energy_gradients) { + delete hills_energy_gradients; + hills_energy_gradients = NULL; + } + + hills.clear(); + hills_off_grid.clear(); + + return COLVARS_OK; +} + // ********************************************************************** // Hill management member functions @@ -336,6 +347,9 @@ int colvarbias_meta::update() // update base class error_code |= colvarbias::update(); + // update the TI estimator (if defined) + error_code |= colvarbias_ti::update(); + // update grid definition, if needed error_code |= update_grid_params(); // add new biasing energy/forces @@ -1000,6 +1014,10 @@ void colvarbias_meta::update_replicas_registry() (replicas.back())->hills_energy = new colvar_grid_scalar(colvars); (replicas.back())->hills_energy_gradients = new colvar_grid_gradient(colvars); } + if (is_enabled(f_cvb_calc_ti_samples)) { + (replicas.back())->enable(f_cvb_calc_ti_samples); + (replicas.back())->colvarbias_ti::init_grids(); + } } } } else { @@ -1374,6 +1392,8 @@ std::istream & colvarbias_meta::read_state_data(std::istream& is) } } + colvarbias_ti::read_state_data(is); + if (cvm::debug()) cvm::log("colvarbias_meta::read_restart() done\n"); @@ -1474,7 +1494,7 @@ std::istream & colvarbias_meta::read_hill(std::istream &is) int colvarbias_meta::setup_output() { output_prefix = cvm::output_prefix(); - if (cvm::num_biases_feature(colvardeps::f_cvb_calc_pmf) > 1) { + if (cvm::main()->num_biases_feature(colvardeps::f_cvb_calc_pmf) > 1) { // if this is not the only free energy integrator, append // this bias's name, to distinguish it from the output of the other // biases producing a .pmf file @@ -1631,6 +1651,7 @@ std::ostream & colvarbias_meta::write_state_data(std::ostream& os) } } + colvarbias_ti::write_state_data(os); return os; } @@ -1651,6 +1672,7 @@ int colvarbias_meta::write_state_to_replicas() int colvarbias_meta::write_output_files() { + colvarbias_ti::write_output_files(); if (dump_fes) { write_pmf(); } diff --git a/lib/colvars/colvarbias_meta.h b/lib/colvars/colvarbias_meta.h index 249f7342bc4b982b3747c56a11f9362b63914a41..78b2d35d41bb581d9b6da2297cf5886285415de5 100644 --- a/lib/colvars/colvarbias_meta.h +++ b/lib/colvars/colvarbias_meta.h @@ -19,7 +19,10 @@ #include "colvargrid.h" /// Metadynamics bias (implementation of \link colvarbias \endlink) -class colvarbias_meta : public colvarbias { +class colvarbias_meta + : public virtual colvarbias, + public virtual colvarbias_ti +{ public: @@ -35,10 +38,13 @@ public: Communication comm; colvarbias_meta(char const *key); + virtual ~colvarbias_meta(); + virtual int init(std::string const &conf); virtual int init_well_tempered_params(std::string const &conf); virtual int init_ebmeta_params(std::string const &conf); - virtual ~colvarbias_meta(); + + virtual int clear_state_data(); virtual int update(); virtual int update_grid_params(); diff --git a/lib/colvars/colvarbias_restraint.cpp b/lib/colvars/colvarbias_restraint.cpp index 70beca29fa79eb25b328c0afa1d3c8cefdf8226a..23534f56eb43c1cd0a2e257ed8581449026d0c8f 100644 --- a/lib/colvars/colvarbias_restraint.cpp +++ b/lib/colvars/colvarbias_restraint.cpp @@ -14,7 +14,7 @@ colvarbias_restraint::colvarbias_restraint(char const *key) - : colvarbias(key) + : colvarbias(key), colvarbias_ti(key) { } @@ -24,6 +24,8 @@ int colvarbias_restraint::init(std::string const &conf) colvarbias::init(conf); enable(f_cvb_apply_force); + colvarbias_ti::init(conf); + if (cvm::debug()) cvm::log("Initializing a new restraint bias.\n"); @@ -86,7 +88,7 @@ std::ostream & colvarbias_restraint::write_traj(std::ostream &os) colvarbias_restraint_centers::colvarbias_restraint_centers(char const *key) - : colvarbias(key), colvarbias_restraint(key) + : colvarbias(key), colvarbias_ti(key), colvarbias_restraint(key) { } @@ -145,7 +147,7 @@ int colvarbias_restraint_centers::change_configuration(std::string const &conf) colvarbias_restraint_k::colvarbias_restraint_k(char const *key) - : colvarbias(key), colvarbias_restraint(key) + : colvarbias(key), colvarbias_ti(key), colvarbias_restraint(key) { force_k = -1.0; } @@ -237,6 +239,7 @@ int colvarbias_restraint_moving::set_state_params(std::string const &conf) colvarbias_restraint_centers_moving::colvarbias_restraint_centers_moving(char const *key) : colvarbias(key), + colvarbias_ti(key), colvarbias_restraint(key), colvarbias_restraint_centers(key), colvarbias_restraint_moving(key) @@ -284,14 +287,17 @@ int colvarbias_restraint_centers_moving::init(std::string const &conf) target_centers[i], 0.5); } + + get_keyval(conf, "outputAccumulatedWork", b_output_acc_work, + b_output_acc_work); // TODO this conflicts with stages + } else { target_centers.clear(); - return COLVARS_OK; } + // Output restraint centers even when they do not change; some NAMD REUS + // scripts expect this behavior get_keyval(conf, "outputCenters", b_output_centers, b_output_centers); - get_keyval(conf, "outputAccumulatedWork", b_output_acc_work, - b_output_acc_work); // TODO this conflicts with stages return COLVARS_OK; } @@ -475,6 +481,7 @@ std::ostream & colvarbias_restraint_centers_moving::write_traj(std::ostream &os) colvarbias_restraint_k_moving::colvarbias_restraint_k_moving(char const *key) : colvarbias(key), + colvarbias_ti(key), colvarbias_restraint(key), colvarbias_restraint_k(key), colvarbias_restraint_moving(key) @@ -712,6 +719,7 @@ std::ostream & colvarbias_restraint::write_state(std::ostream &os) colvarbias_restraint_harmonic::colvarbias_restraint_harmonic(char const *key) : colvarbias(key), + colvarbias_ti(key), colvarbias_restraint(key), colvarbias_restraint_centers(key), colvarbias_restraint_moving(key), @@ -743,17 +751,22 @@ int colvarbias_restraint_harmonic::init(std::string const &conf) int colvarbias_restraint_harmonic::update() { + int error_code = COLVARS_OK; + + // update the TI estimator (if defined) + error_code |= colvarbias_ti::update(); + // update parameters (centers or force constant) - colvarbias_restraint_centers_moving::update(); - colvarbias_restraint_k_moving::update(); + error_code |= colvarbias_restraint_centers_moving::update(); + error_code |= colvarbias_restraint_k_moving::update(); // update restraint energy and forces - colvarbias_restraint::update(); + error_code |= colvarbias_restraint::update(); // update accumulated work using the current forces - colvarbias_restraint_centers_moving::update_acc_work(); + error_code |= colvarbias_restraint_centers_moving::update_acc_work(); - return COLVARS_OK; + return error_code; } @@ -798,6 +811,18 @@ int colvarbias_restraint_harmonic::set_state_params(std::string const &conf) } +std::ostream & colvarbias_restraint_harmonic::write_state_data(std::ostream &os) +{ + return colvarbias_ti::write_state_data(os); +} + + +std::istream & colvarbias_restraint_harmonic::read_state_data(std::istream &is) +{ + return colvarbias_ti::read_state_data(is); +} + + std::ostream & colvarbias_restraint_harmonic::write_traj_label(std::ostream &os) { colvarbias_restraint::write_traj_label(os); @@ -845,6 +870,7 @@ cvm::real colvarbias_restraint_harmonic::energy_difference(std::string const &co colvarbias_restraint_harmonic_walls::colvarbias_restraint_harmonic_walls(char const *key) : colvarbias(key), + colvarbias_ti(key), colvarbias_restraint(key), colvarbias_restraint_k(key), colvarbias_restraint_moving(key), @@ -967,11 +993,15 @@ int colvarbias_restraint_harmonic_walls::init(std::string const &conf) int colvarbias_restraint_harmonic_walls::update() { - colvarbias_restraint_k_moving::update(); + int error_code = COLVARS_OK; - colvarbias_restraint::update(); + error_code |= colvarbias_ti::update(); - return COLVARS_OK; + error_code |= colvarbias_restraint_k_moving::update(); + + error_code |= colvarbias_restraint::update(); + + return error_code; } @@ -1065,6 +1095,18 @@ int colvarbias_restraint_harmonic_walls::set_state_params(std::string const &con } +std::ostream & colvarbias_restraint_harmonic_walls::write_state_data(std::ostream &os) +{ + return colvarbias_ti::write_state_data(os); +} + + +std::istream & colvarbias_restraint_harmonic_walls::read_state_data(std::istream &is) +{ + return colvarbias_ti::read_state_data(is); +} + + std::ostream & colvarbias_restraint_harmonic_walls::write_traj_label(std::ostream &os) { colvarbias_restraint::write_traj_label(os); @@ -1084,6 +1126,7 @@ std::ostream & colvarbias_restraint_harmonic_walls::write_traj(std::ostream &os) colvarbias_restraint_linear::colvarbias_restraint_linear(char const *key) : colvarbias(key), + colvarbias_ti(key), colvarbias_restraint(key), colvarbias_restraint_centers(key), colvarbias_restraint_moving(key), @@ -1120,17 +1163,22 @@ int colvarbias_restraint_linear::init(std::string const &conf) int colvarbias_restraint_linear::update() { + int error_code = COLVARS_OK; + + // update the TI estimator (if defined) + error_code |= colvarbias_ti::update(); + // update parameters (centers or force constant) - colvarbias_restraint_centers_moving::update(); - colvarbias_restraint_k_moving::update(); + error_code |= colvarbias_restraint_centers_moving::update(); + error_code |= colvarbias_restraint_k_moving::update(); // update restraint energy and forces - colvarbias_restraint::update(); + error_code |= colvarbias_restraint::update(); // update accumulated work using the current forces - colvarbias_restraint_centers_moving::update_acc_work(); + error_code |= colvarbias_restraint_centers_moving::update_acc_work(); - return COLVARS_OK; + return error_code; } @@ -1196,6 +1244,18 @@ int colvarbias_restraint_linear::set_state_params(std::string const &conf) } +std::ostream & colvarbias_restraint_linear::write_state_data(std::ostream &os) +{ + return colvarbias_ti::write_state_data(os); +} + + +std::istream & colvarbias_restraint_linear::read_state_data(std::istream &is) +{ + return colvarbias_ti::read_state_data(is); +} + + std::ostream & colvarbias_restraint_linear::write_traj_label(std::ostream &os) { colvarbias_restraint::write_traj_label(os); diff --git a/lib/colvars/colvarbias_restraint.h b/lib/colvars/colvarbias_restraint.h index 8c3a1537fc881cbca3dc8ef66fcf3caeb4d49c90..b10649cab112f8a10bfedaff0c10823cc7a43f04 100644 --- a/lib/colvars/colvarbias_restraint.h +++ b/lib/colvars/colvarbias_restraint.h @@ -16,7 +16,8 @@ /// see derived classes for specific types /// (implementation of \link colvarbias \endlink) class colvarbias_restraint - : public virtual colvarbias + : public virtual colvarbias, + public virtual colvarbias_ti { public: @@ -95,7 +96,7 @@ protected: /// Options to change the restraint configuration over time (shared between centers and k moving) class colvarbias_restraint_moving - : public virtual colvarparse { + : public virtual colvarparse, public virtual colvardeps { public: colvarbias_restraint_moving(char const *key); @@ -226,6 +227,8 @@ public: virtual int update(); virtual std::string const get_state_params() const; virtual int set_state_params(std::string const &conf); + virtual std::ostream & write_state_data(std::ostream &os); + virtual std::istream & read_state_data(std::istream &os); virtual std::ostream & write_traj_label(std::ostream &os); virtual std::ostream & write_traj(std::ostream &os); virtual int change_configuration(std::string const &conf); @@ -252,6 +255,8 @@ public: virtual void communicate_forces(); virtual std::string const get_state_params() const; virtual int set_state_params(std::string const &conf); + virtual std::ostream & write_state_data(std::ostream &os); + virtual std::istream & read_state_data(std::istream &os); virtual std::ostream & write_traj_label(std::ostream &os); virtual std::ostream & write_traj(std::ostream &os); @@ -292,6 +297,8 @@ public: virtual std::string const get_state_params() const; virtual int set_state_params(std::string const &conf); + virtual std::ostream & write_state_data(std::ostream &os); + virtual std::istream & read_state_data(std::istream &os); virtual std::ostream & write_traj_label(std::ostream &os); virtual std::ostream & write_traj(std::ostream &os); diff --git a/lib/colvars/colvarcomp.h b/lib/colvars/colvarcomp.h index 3c1ec2495c9c5af29c5549b9c9ddaa355d2f1502..b94d798be9f0a3cd49abc27bb8cdaf41f88e8c87 100644 --- a/lib/colvars/colvarcomp.h +++ b/lib/colvars/colvarcomp.h @@ -140,7 +140,12 @@ public: { return cvc_features; } - + static void delete_features() { + for (size_t i=0; i < cvc_features.size(); i++) { + delete cvc_features[i]; + } + cvc_features.clear(); + } /// \brief Obtain data needed for the calculation for the backend virtual void read_data(); diff --git a/lib/colvars/colvarcomp_coordnums.cpp b/lib/colvars/colvarcomp_coordnums.cpp index 369d489e279c04d5051d8fef83ca0d16e5cf67ed..edfea96795b77d7dbd13fd174594d2b9ff007fc5 100644 --- a/lib/colvars/colvarcomp_coordnums.cpp +++ b/lib/colvars/colvarcomp_coordnums.cpp @@ -87,6 +87,12 @@ colvar::coordnum::coordnum(std::string const &conf) group1 = parse_group(conf, "group1"); group2 = parse_group(conf, "group2"); + if (int atom_number = cvm::atom_group::overlap(*group1, *group2)) { + cvm::error("Error: group1 and group2 share a common atom (number: " + + cvm::to_str(atom_number) + ")\n"); + return; + } + if (group1->b_dummy) { cvm::error("Error: only group2 is allowed to be a dummy atom\n"); return; diff --git a/lib/colvars/colvarcomp_distances.cpp b/lib/colvars/colvarcomp_distances.cpp index 18d154515a3cfee7ade375083f70ab932da50067..ce8055843f93d389117f38926e81d97c66bc7954 100644 --- a/lib/colvars/colvarcomp_distances.cpp +++ b/lib/colvars/colvarcomp_distances.cpp @@ -1066,8 +1066,9 @@ void colvar::rmsd::calc_force_invgrads() void colvar::rmsd::calc_Jacobian_derivative() { // divergence of the rotated coordinates (including only derivatives of the rotation matrix) - cvm::real divergence = 0.0; + cvm::real rotation_term = 0.0; + // The rotation term only applies is coordinates are rotated if (atoms->b_rotate) { // gradient of the rotation matrix @@ -1104,7 +1105,7 @@ void colvar::rmsd::calc_Jacobian_derivative() for (size_t alpha = 0; alpha < 3; alpha++) { for (size_t beta = 0; beta < 3; beta++) { - divergence += grad_rot_mat[beta][alpha][alpha] * y[beta]; + rotation_term += grad_rot_mat[beta][alpha][alpha] * y[beta]; // Note: equation was derived for inverse rotation (see colvars paper) // so here the matrix is transposed // (eq would give divergence += grad_rot_mat[alpha][beta][alpha] * y[beta];) @@ -1112,7 +1113,13 @@ void colvar::rmsd::calc_Jacobian_derivative() } } } - jd.real_value = x.real_value > 0.0 ? (3.0 * atoms->size() - 4.0 - divergence) / x.real_value : 0.0; + + // The translation term only applies is coordinates are centered + cvm::real translation_term = atoms->b_center ? 3.0 : 0.0; + + jd.real_value = x.real_value > 0.0 ? + (3.0 * atoms->size() - 1.0 - translation_term - rotation_term) / x.real_value : + 0.0; } diff --git a/lib/colvars/colvardeps.cpp b/lib/colvars/colvardeps.cpp index 8f241a6255953f2ae0d4815a050daa12c077f866..ac906e7be755fc382d1cb383730522da38860092 100644 --- a/lib/colvars/colvardeps.cpp +++ b/lib/colvars/colvardeps.cpp @@ -413,15 +413,27 @@ void colvardeps::init_cvb_requires() { init_feature(f_cvb_apply_force, "apply force", f_type_user); f_req_children(f_cvb_apply_force, f_cv_gradient); - init_feature(f_cvb_get_total_force, "obtain total force"); + 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_history_dependent, "history-dependent", f_type_static); + init_feature(f_cvb_time_dependent, "time-dependent", f_type_static); + init_feature(f_cvb_scalar_variables, "require scalar variables", f_type_static); f_req_children(f_cvb_scalar_variables, f_cv_scalar); init_feature(f_cvb_calc_pmf, "calculate a PMF", f_type_static); + + init_feature(f_cvb_calc_ti_samples, "calculate TI samples", f_type_dynamic); + f_req_self(f_cvb_calc_ti_samples, f_cvb_get_total_force); + f_req_children(f_cvb_calc_ti_samples, f_cv_grid); + + init_feature(f_cvb_write_ti_samples, "write TI samples ", f_type_user); + f_req_self(f_cvb_write_ti_samples, f_cvb_calc_ti_samples); + + init_feature(f_cvb_write_ti_pmf, "write TI PMF", f_type_user); + f_req_self(f_cvb_write_ti_pmf, f_cvb_calc_ti_samples); } // Initialize feature_states for each instance @@ -431,6 +443,9 @@ void colvardeps::init_cvb_requires() { // Most features are available, so we set them so // and list exceptions below } + + // only compute TI samples when deriving from colvarbias_ti + feature_states[f_cvb_calc_ti_samples].available = false; } @@ -504,9 +519,6 @@ void colvardeps::init_cv_requires() { init_feature(f_cv_subtract_applied_force, "subtract applied force from total force", f_type_user); f_req_self(f_cv_subtract_applied_force, f_cv_total_force); - // There is no well-defined way to implement f_cv_subtract_applied_force - // in the case of extended-Lagrangian colvars - f_req_exclude(f_cv_subtract_applied_force, f_cv_extended_Lagrangian); init_feature(f_cv_lower_boundary, "lower boundary", f_type_user); f_req_self(f_cv_lower_boundary, f_cv_scalar); @@ -514,7 +526,7 @@ void colvardeps::init_cv_requires() { init_feature(f_cv_upper_boundary, "upper boundary", f_type_user); f_req_self(f_cv_upper_boundary, f_cv_scalar); - init_feature(f_cv_grid, "grid", f_type_user); + init_feature(f_cv_grid, "grid", f_type_dynamic); f_req_self(f_cv_grid, f_cv_lower_boundary); f_req_self(f_cv_grid, f_cv_upper_boundary); @@ -693,7 +705,6 @@ void colvardeps::print_state() { } - void colvardeps::add_child(colvardeps *child) { children.push_back(child); diff --git a/lib/colvars/colvardeps.h b/lib/colvars/colvardeps.h index dfb10d00e421f7635fe4bce5ec88b5f45cfc39eb..bd892fbca8cef746bb4dfad818b1e4577507a735 100644 --- a/lib/colvars/colvardeps.h +++ b/lib/colvars/colvardeps.h @@ -180,8 +180,6 @@ public: protected: - - /// Parse a keyword and enable a feature accordingly bool get_keyval_feature(colvarparse *cvp, std::string const &conf, char const *key, @@ -229,10 +227,18 @@ public: f_cvb_get_total_force, /// \brief depends on simulation history f_cvb_history_dependent, + /// \brief depends on time + f_cvb_time_dependent, /// \brief requires scalar colvars f_cvb_scalar_variables, /// \brief whether this bias will compute a PMF f_cvb_calc_pmf, + /// \brief whether this bias will compute TI samples + f_cvb_calc_ti_samples, + /// \brief whether this bias will write TI samples + f_cvb_write_ti_samples, + /// \brief whether this bias should write the TI PMF + f_cvb_write_ti_pmf, f_cvb_ntot }; diff --git a/lib/colvars/colvargrid.h b/lib/colvars/colvargrid.h index 6f06cb1066fffbc4ad8982a643264f496264657a..a01104dba819c2745a9d63d434b8e6832c9d85b7 100644 --- a/lib/colvars/colvargrid.h +++ b/lib/colvars/colvargrid.h @@ -1403,6 +1403,15 @@ public: /// Constructor from a vector of colvars colvar_grid_gradient(std::vector<colvar *> &colvars); + /// \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++) { + data[address(ix) + imult] += values[imult].real_value; + } + if (samples) + samples->incr_count(ix); + } + /// \brief Accumulate the gradient inline void acc_grad(std::vector<int> const &ix, cvm::real const *grads) { for (size_t imult = 0; imult < mult; imult++) { diff --git a/lib/colvars/colvarmodule.cpp b/lib/colvars/colvarmodule.cpp index 780dc28afaebd4c6db1788c0495e73f8b7904d5b..f293aac63f2ce433410898098127ab856cb89b84 100644 --- a/lib/colvars/colvarmodule.cpp +++ b/lib/colvars/colvarmodule.cpp @@ -22,7 +22,7 @@ #include "colvarbias_restraint.h" #include "colvarscript.h" #include "colvaratoms.h" - +#include "colvarcomp.h" colvarmodule::colvarmodule(colvarproxy *proxy_in) { @@ -274,9 +274,9 @@ int colvarmodule::parse_global_params(std::string const &conf) parse->get_keyval(conf, "colvarsRestartFrequency", restart_out_freq, restart_out_freq); - // if this is true when initializing, it means - // we are continuing after a reset(): default to true - parse->get_keyval(conf, "colvarsTrajAppend", cv_traj_append, cv_traj_append); + // Deprecate append flag + parse->get_keyval(conf, "colvarsTrajAppend", + cv_traj_append, cv_traj_append, colvarparse::parse_silent); parse->get_keyval(conf, "scriptedColvarForces", use_scripted_forces, false); @@ -409,22 +409,12 @@ int colvarmodule::parse_biases(std::string const &conf) cvm::decrease_depth(); } - size_t i; - - size_t n_hist_dep_biases = 0; - std::vector<std::string> hist_dep_biases_names; - for (i = 0; i < biases.size(); i++) { - if (biases[i]->is_enabled(colvardeps::f_cvb_apply_force) && - biases[i]->is_enabled(colvardeps::f_cvb_history_dependent)) { - n_hist_dep_biases++; - hist_dep_biases_names.push_back(biases[i]->name); - } - } - if (n_hist_dep_biases > 1) { - cvm::log("WARNING: there are "+cvm::to_str(n_hist_dep_biases)+ - " history-dependent biases with non-zero force parameters:\n"+ - cvm::to_str(hist_dep_biases_names)+"\n"+ - "Please make sure that their forces do not counteract each other.\n"); + std::vector<std::string> const time_biases = time_dependent_biases(); + if (time_biases.size() > 1) { + cvm::log("WARNING: there are "+cvm::to_str(time_biases.size())+ + " time-dependent biases with non-zero force parameters:\n"+ + cvm::to_str(time_biases)+"\n"+ + "Please ensure that their forces do not counteract each other.\n"); } if (biases.size() || use_scripted_forces) { @@ -441,7 +431,7 @@ int colvarmodule::parse_biases(std::string const &conf) } -int colvarmodule::num_biases_feature(int feature_id) +int colvarmodule::num_biases_feature(int feature_id) const { colvarmodule *cv = cvm::main(); size_t n = 0; @@ -456,7 +446,7 @@ int colvarmodule::num_biases_feature(int feature_id) } -int colvarmodule::num_biases_type(std::string const &type) +int colvarmodule::num_biases_type(std::string const &type) const { colvarmodule *cv = cvm::main(); size_t n = 0; @@ -471,6 +461,22 @@ int colvarmodule::num_biases_type(std::string const &type) } +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++) { + 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) || + biases[i]->is_enabled(colvardeps::f_cvb_time_dependent))) { + biases_names.push_back(biases[i]->name); + } + } + return biases_names; +} + + int colvarmodule::catch_input_errors(int result) { if (result != COLVARS_OK || get_error()) { @@ -673,8 +679,15 @@ int colvarmodule::calc() } // write restart files, if needed - if (restart_out_freq && restart_out_name.size()) { - error_code |= write_restart_files(); + if (restart_out_freq && (cvm::step_relative() > 0) && + ((cvm::step_absolute() % restart_out_freq) == 0) ) { + if (restart_out_name.size()) { + // Write restart file, if different from main output + error_code |= write_restart_file(restart_out_name); + } else { + error_code |= write_restart_file(output_prefix()+".colvars.state"); + } + write_output_files(); } return error_code; @@ -916,21 +929,16 @@ int colvarmodule::calc_scripted_forces() } -int colvarmodule::write_restart_files() +int colvarmodule::write_restart_file(std::string const &out_name) { - if ( (cvm::step_relative() > 0) && - ((cvm::step_absolute() % restart_out_freq) == 0) ) { - cvm::log("Writing the state file \""+ - restart_out_name+"\".\n"); - proxy->backup_file(restart_out_name); - std::ostream *restart_out_os = proxy->output_stream(restart_out_name); - if (!restart_out_os) return cvm::get_error(); - if (!write_restart(*restart_out_os)) { - return cvm::error("Error: in writing restart file.\n", FILE_ERROR); - } - proxy->close_output_stream(restart_out_name); + cvm::log("Saving collective variables state to \""+out_name+"\".\n"); + proxy->backup_file(out_name); + std::ostream *restart_out_os = proxy->output_stream(out_name); + if (!restart_out_os) return cvm::get_error(); + if (!write_restart(*restart_out_os)) { + return cvm::error("Error: in writing restart file.\n", FILE_ERROR); } - + proxy->close_output_stream(out_name); return (cvm::get_error() ? COLVARS_ERROR : COLVARS_OK); } @@ -1011,7 +1019,15 @@ colvarmodule::~colvarmodule() { if ((proxy->smp_thread_id() == COLVARS_NOT_IMPLEMENTED) || (proxy->smp_thread_id() == 0)) { + + // Delete contents of static arrays + colvarbias::delete_features(); + colvar::delete_features(); + colvar::cvc::delete_features(); + atom_group::delete_features(); + reset(); + delete parse; parse = NULL; proxy = NULL; @@ -1261,7 +1277,7 @@ continue the previous simulation.\n\n"); to:\n\ \""+ proxy->input_prefix()+".colvars.state\"\n"); output_prefix() = output_prefix()+".tmp"; - write_output_files(); + write_restart_file(output_prefix()+".colvars.state"); cvm::error("Exiting with error until issue is addressed.\n", FATAL_ERROR); } @@ -1277,24 +1293,13 @@ int colvarmodule::backup_file(char const *filename) int colvarmodule::write_output_files() { - // if this is a simulation run (i.e. not a postprocessing), output data - // must be written to be able to restart the simulation - std::string const out_name = - (output_prefix().size() ? - std::string(output_prefix()+".colvars.state") : - std::string("colvars.state")); - cvm::log("Saving collective variables state to \""+out_name+"\".\n"); - - std::ostream * os = proxy->output_stream(out_name); - os->setf(std::ios::scientific, std::ios::floatfield); - this->write_restart(*os); - proxy->close_output_stream(out_name); + int error_code = COLVARS_OK; cvm::increase_depth(); for (std::vector<colvar *>::iterator cvi = colvars.begin(); cvi != colvars.end(); cvi++) { - (*cvi)->write_output_files(); + error_code |= (*cvi)->write_output_files(); } cvm::decrease_depth(); @@ -1302,8 +1307,8 @@ int colvarmodule::write_output_files() for (std::vector<colvarbias *>::iterator bi = biases.begin(); bi != biases.end(); bi++) { - (*bi)->write_output_files(); - (*bi)->write_state_to_replicas(); + error_code |= (*bi)->write_output_files(); + error_code |= (*bi)->write_state_to_replicas(); } cvm::decrease_depth(); @@ -1403,15 +1408,12 @@ std::ostream & colvarmodule::write_restart(std::ostream &os) cvi != colvars.end(); cvi++) { (*cvi)->write_restart(os); - error_code |= (*cvi)->write_output_files(); } for (std::vector<colvarbias *>::iterator bi = biases.begin(); bi != biases.end(); bi++) { (*bi)->write_state(os); - error_code |= (*bi)->write_state_to_replicas(); - error_code |= (*bi)->write_output_files(); } cvm::decrease_depth(); diff --git a/lib/colvars/colvarmodule.h b/lib/colvars/colvarmodule.h index 0f6efd14c4ea1b7574304c7bc16f067b735b5076..3f13ae88cf07cb438db71f5716f8bc465090c0ae 100644 --- a/lib/colvars/colvarmodule.h +++ b/lib/colvars/colvarmodule.h @@ -293,10 +293,13 @@ private: public: /// Return how many biases have this feature enabled - static int num_biases_feature(int feature_id); + int num_biases_feature(int feature_id) const; /// Return how many biases are defined with this type - static int num_biases_type(std::string const &type); + int num_biases_type(std::string const &type) const; + + /// Return the names of time-dependent biases with forces enabled + std::vector<std::string> const time_dependent_biases() const; private: /// Useful wrapper to interrupt parsing if any error occurs @@ -334,9 +337,9 @@ public: /// Write all trajectory files int write_traj_files(); - /// Write all restart files - int write_restart_files(); - /// Write all FINAL output files + /// Write a state file useful to resume the simulation + int write_restart_file(std::string const &out_name); + /// Write all other output files int write_output_files(); /// Backup a file before writing it static int backup_file(char const *filename); @@ -580,7 +583,7 @@ public: /// from static functions in the colvarmodule class static colvarproxy *proxy; - /// \brief Accessor for the above + /// \brief Access the one instance of the Colvars module static colvarmodule *main(); }; diff --git a/lib/colvars/colvarproxy.cpp b/lib/colvars/colvarproxy.cpp index fa24091d5233da18a1af36f0dffceca3f89a26fb..8160144c6bab1e11d36f4d1b7712e75bce90e6c8 100644 --- a/lib/colvars/colvarproxy.cpp +++ b/lib/colvars/colvarproxy.cpp @@ -10,6 +10,10 @@ #include <sstream> #include <string.h> +#if defined(_OPENMP) +#include <omp.h> +#endif + #include "colvarmodule.h" #include "colvarproxy.h" #include "colvarscript.h" @@ -40,6 +44,12 @@ bool colvarproxy_system::total_forces_enabled() const } +bool colvarproxy_system::total_forces_same_step() const +{ + return false; +} + + cvm::real colvarproxy_system::position_dist2(cvm::atom_pos const &pos1, cvm::atom_pos const &pos2) { @@ -204,7 +214,13 @@ void colvarproxy_atom_groups::clear_atom_group(int index) colvarproxy_smp::colvarproxy_smp() { - b_smp_active = true; + b_smp_active = true; // May be disabled by user option + omp_lock_state = NULL; +#if defined(_OPENMP) + if (smp_thread_id() == 0) { + omp_init_lock(reinterpret_cast<omp_lock_t *>(omp_lock_state)); + } +#endif } @@ -213,60 +229,143 @@ colvarproxy_smp::~colvarproxy_smp() {} int colvarproxy_smp::smp_enabled() { +#if defined(_OPENMP) + if (b_smp_active) { + return COLVARS_OK; + } + return COLVARS_ERROR; +#else return COLVARS_NOT_IMPLEMENTED; +#endif } int colvarproxy_smp::smp_colvars_loop() { +#if defined(_OPENMP) + colvarmodule *cv = cvm::main(); + colvarproxy *proxy = cv->proxy; +#pragma omp parallel for + for (size_t i = 0; i < cv->variables_active_smp()->size(); i++) { + colvar *x = (*(cv->variables_active_smp()))[i]; + int x_item = (*(cv->variables_active_smp_items()))[i]; + if (cvm::debug()) { + cvm::log("["+cvm::to_str(proxy->smp_thread_id())+"/"+ + cvm::to_str(proxy->smp_num_threads())+ + "]: calc_colvars_items_smp(), i = "+cvm::to_str(i)+", cv = "+ + x->name+", cvc = "+cvm::to_str(x_item)+"\n"); + } + x->calc_cvcs(x_item, 1); + } + return cvm::get_error(); +#else return COLVARS_NOT_IMPLEMENTED; +#endif } int colvarproxy_smp::smp_biases_loop() { +#if defined(_OPENMP) + colvarmodule *cv = cvm::main(); +#pragma omp parallel + { +#pragma omp for + for (size_t i = 0; i < cv->biases_active()->size(); i++) { + colvarbias *b = (*(cv->biases_active()))[i]; + if (cvm::debug()) { + cvm::log("Calculating bias \""+b->name+"\" on thread "+ + cvm::to_str(smp_thread_id())+"\n"); + } + b->update(); + } + } + return cvm::get_error(); +#else return COLVARS_NOT_IMPLEMENTED; +#endif } int colvarproxy_smp::smp_biases_script_loop() { +#if defined(_OPENMP) + colvarmodule *cv = cvm::main(); +#pragma omp parallel + { +#pragma omp single nowait + { + cv->calc_scripted_forces(); + } +#pragma omp for + for (size_t i = 0; i < cv->biases_active()->size(); i++) { + colvarbias *b = (*(cv->biases_active()))[i]; + if (cvm::debug()) { + cvm::log("Calculating bias \""+b->name+"\" on thread "+ + cvm::to_str(smp_thread_id())+"\n"); + } + b->update(); + } + } + return cvm::get_error(); +#else return COLVARS_NOT_IMPLEMENTED; +#endif } + + int colvarproxy_smp::smp_thread_id() { +#if defined(_OPENMP) + return omp_get_thread_num(); +#else return COLVARS_NOT_IMPLEMENTED; +#endif } int colvarproxy_smp::smp_num_threads() { +#if defined(_OPENMP) + return omp_get_max_threads(); +#else return COLVARS_NOT_IMPLEMENTED; +#endif } int colvarproxy_smp::smp_lock() { +#if defined(_OPENMP) + omp_set_lock(reinterpret_cast<omp_lock_t *>(omp_lock_state)); +#endif return COLVARS_OK; } int colvarproxy_smp::smp_trylock() { +#if defined(_OPENMP) + return omp_test_lock(reinterpret_cast<omp_lock_t *>(omp_lock_state)) ? + COLVARS_OK : COLVARS_ERROR; +#else return COLVARS_OK; +#endif } int colvarproxy_smp::smp_unlock() { +#if defined(_OPENMP) + omp_unset_lock(reinterpret_cast<omp_lock_t *>(omp_lock_state)); +#endif return COLVARS_OK; } - colvarproxy_replicas::colvarproxy_replicas() {} diff --git a/lib/colvars/colvarproxy.h b/lib/colvars/colvarproxy.h index 95d13cd7e05c15ba209333f4a6550e11d29b5977..e51ddfbe3bfc16a9d9a77264b86912a2cd8aa34f 100644 --- a/lib/colvars/colvarproxy.h +++ b/lib/colvars/colvarproxy.h @@ -80,6 +80,9 @@ public: /// Are total forces being used? virtual bool total_forces_enabled() const; + + /// Are total forces from the current step available? + virtual bool total_forces_same_step() const; }; @@ -372,6 +375,11 @@ public: /// Release the lock virtual int smp_unlock(); + +protected: + + /// Lock state for OpenMP + void *omp_lock_state; }; diff --git a/lib/colvars/colvars_version.h b/lib/colvars/colvars_version.h index 312c0fd1a0c86e832ccbac15605867ddd101c824..9dfeb17898295ee80f33832738eac202fe660041 100644 --- a/lib/colvars/colvars_version.h +++ b/lib/colvars/colvars_version.h @@ -1,5 +1,5 @@ #ifndef COLVARS_VERSION -#define COLVARS_VERSION "2017-08-06" +#define COLVARS_VERSION "2017-10-11" // 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 89302a16a2ead1c29bbc501ec6d8e2aa9d043cb8..9570acd8327252a99e6394f9bd29d6f6a0bd2f4e 100644 --- a/lib/colvars/colvarscript.cpp +++ b/lib/colvars/colvarscript.cpp @@ -11,7 +11,10 @@ #include <stdlib.h> #include <string.h> +#define COLVARSCRIPT_CPP #include "colvarscript.h" +#undef COLVARSCRIPT_CPP + #include "colvarproxy.h" #include "colvardeps.h" @@ -21,6 +24,11 @@ colvarscript::colvarscript(colvarproxy *p) colvars(p->colvars), proxy_error(0) { + comm_help.resize(colvarscript::cv_n_commands); + comm_fns.resize(colvarscript::cv_n_commands); +#define COLVARSCRIPT_INIT_FN +#include "colvarscript.h" +#undef COLVARSCRIPT_INIT_FN } @@ -66,8 +74,7 @@ int colvarscript::run(int objc, unsigned char *const objv[]) } if (objc < 2) { - result = help_string(); - return COLVARS_OK; + return exec_command(cv_help, NULL, objc, objv); } std::string const cmd(obj_to_str(objv[1])); @@ -167,17 +174,7 @@ int colvarscript::run(int objc, unsigned char *const objv[]) /// Parse config from string if (cmd == "config") { - if (objc < 3) { - result = "Missing arguments\n" + help_string(); - return COLVARSCRIPT_ERROR; - } - std::string const conf(obj_to_str(objv[2])); - if (colvars->read_config_string(conf) == COLVARS_OK) { - return COLVARS_OK; - } else { - result = "Error parsing configuration string"; - return COLVARSCRIPT_ERROR; - } + return exec_command(cv_config, NULL, objc, objv); } /// Load an input state file @@ -204,6 +201,8 @@ int colvarscript::run(int objc, unsigned char *const objv[]) proxy->output_prefix() = obj_to_str(objv[2]); int error = 0; error |= colvars->setup_output(); + error |= colvars->write_restart_file(colvars->output_prefix()+ + ".colvars.state"); error |= colvars->write_output_files(); return error ? COLVARSCRIPT_ERROR : COLVARS_OK; } @@ -255,6 +254,10 @@ int colvarscript::run(int objc, unsigned char *const objv[]) } } + if (cmd == "help") { + return exec_command(cv_help, NULL, objc, objv); + } + result = "Syntax error\n" + help_string(); return COLVARSCRIPT_ERROR; } @@ -295,7 +298,9 @@ int colvarscript::proc_colvar(colvar *cv, int objc, unsigned char *const objv[]) // colvar destructor is tasked with the cleanup delete cv; // TODO this could be done by the destructors - colvars->write_traj_label(*(colvars->cv_traj_os)); + if (colvars->cv_traj_os != NULL) { + colvars->write_traj_label(*(colvars->cv_traj_os)); + } return COLVARS_OK; } @@ -374,7 +379,6 @@ int colvarscript::proc_colvar(colvar *cv, int objc, unsigned char *const objv[]) int colvarscript::proc_bias(colvarbias *b, int objc, unsigned char *const objv[]) { - std::string const key(obj_to_str(objv[0])); std::string const subcmd(obj_to_str(objv[2])); if (subcmd == "energy") { @@ -425,7 +429,9 @@ int colvarscript::proc_bias(colvarbias *b, int objc, unsigned char *const objv[] // the bias destructor takes care of the cleanup at cvm level delete b; // TODO this could be done by the destructors - colvars->write_traj_label(*(colvars->cv_traj_os)); + if (colvars->cv_traj_os != NULL) { + colvars->write_traj_label(*(colvars->cv_traj_os)); + } return COLVARS_OK; } @@ -528,7 +534,7 @@ int colvarscript::proc_features(colvardeps *obj, } -std::string colvarscript::help_string() +std::string colvarscript::help_string() const { std::string buf; buf = "Usage: cv <subcommand> [args...]\n\ @@ -538,7 +544,7 @@ Managing the Colvars module:\n\ config <string> -- read configuration from the given string\n\ reset -- delete all internal configuration\n\ delete -- delete this Colvars module instance\n\ - version -- return version of colvars code\n\ + version -- return version of Colvars code\n\ \n\ Input and output:\n\ list -- return a list of all variables\n\ @@ -564,6 +570,8 @@ Accessing collective variables:\n\ colvar <name> type -- return the type of colvar <name>\n\ colvar <name> delete -- delete colvar <name>\n\ colvar <name> addforce <F> -- apply given force on colvar <name>\n\ + colvar <name> getappliedforce -- return applied force of colvar <name>\n\ + colvar <name> gettotalforce -- return total force of colvar <name>\n\ colvar <name> getconfig -- return config string of colvar <name>\n\ colvar <name> cvcflags <fl> -- enable or disable cvcs according to 0/1 flags\n\ colvar <name> get <f> -- get the value of the colvar feature <f>\n\ diff --git a/lib/colvars/colvarscript.h b/lib/colvars/colvarscript.h index 94d451809cdd9feb853dbf5462342a52140d907a..39cd08934059f25e0fe598dc0a915ec79036631a 100644 --- a/lib/colvars/colvarscript.h +++ b/lib/colvars/colvarscript.h @@ -8,21 +8,27 @@ // Colvars repository at GitHub. #ifndef COLVARSCRIPT_H -#define COLVARSCRIPT_H +//#define COLVARSCRIPT_H // Delay definition until later #include <string> +#include <vector> +#include <map> + #include "colvarmodule.h" #include "colvarvalue.h" #include "colvarbias.h" #include "colvarproxy.h" + // Only these error values are part of the scripting interface #define COLVARSCRIPT_ERROR -1 #define COLVARSCRIPT_OK 0 + class colvarscript { private: + colvarproxy *proxy; colvarmodule *colvars; @@ -35,16 +41,93 @@ public: colvarscript(colvarproxy * p); inline ~colvarscript() {} - /// If an error is caught by the proxy through fatal_error(), this is set to COLVARSCRIPT_ERROR + /// If an error is caught by the proxy through fatal_error(), this is set to + /// COLVARSCRIPT_ERROR int proxy_error; - /// If an error is returned by one of the methods, it should set this to the error message + /// If an error is returned by one of the methods, it should set this to the + /// error message std::string result; /// Run script command with given positional arguments (objects) int run(int objc, unsigned char *const objv[]); + /// Set the return value of the script command to the given string + inline void set_str_result(std::string const &s) + { + result = s; + } + + /// Build and return a short help + std::string help_string(void) const; + + /// Use scripting language to get the string representation of an object + inline char const *obj_to_str(unsigned char *const obj) + { + return cvm::proxy->script_obj_to_str(obj); + } + + enum command { + cv_help, + cv_version, + cv_config, + cv_configfile, + cv_reset, + cv_delete, + cv_list, + cv_list_biases, + cv_load, + cv_save, + cv_update, + cv_addenergy, + cv_getenergy, + cv_printframe, + cv_printframelabels, + cv_frame, + cv_colvar, + cv_colvar_value, + cv_colvar_update, + cv_colvar_type, + cv_colvar_delete, + cv_colvar_addforce, + cv_colvar_getappliedforce, + cv_colvar_gettotalforce, + cv_colvar_cvcflags, + cv_colvar_getconfig, + cv_colvar_get, + cv_colvar_set, + cv_bias, + cv_bias_energy, + cv_bias_update, + cv_bias_delete, + cv_bias_getconfig, + cv_bias_get, + cv_bias_set, + cv_n_commands + }; + + /// Execute a script command + inline int exec_command(command c, + void *pobj, + int objc, unsigned char * const *objv) + { + return (*(comm_fns[c]))(pobj, objc, objv); + } + + /// Get help for a command (TODO reformat for each language?) + inline std::string command_help(colvarscript::command c) const + { + return comm_help[c]; + } + + /// Clear all object results + inline void clear_results() + { + result.clear(); + } + private: + /// Run subcommands on colvar int proc_colvar(colvar *cv, int argc, unsigned char *const argv[]); @@ -55,17 +138,146 @@ private: int proc_features(colvardeps *obj, int argc, unsigned char *const argv[]); - /// Build and return a short help - std::string help_string(void); + /// Internal identifiers of command strings + std::map<std::string, command> comm_str_map; -public: + /// Help strings for each command + std::vector<std::string> comm_help; - inline char const *obj_to_str(unsigned char *const obj) - { - return cvm::proxy->script_obj_to_str(obj); - } + /// Number of arguments for each command + std::vector<size_t> comm_n_args; + + /// Arguments for each command + std::vector< std::vector<std::string> > comm_args; + + /// Implementations of each command + std::vector<int (*)(void *, int, unsigned char * const *)> comm_fns; }; +/// Get a pointer to the main colvarscript object +inline static colvarscript *colvarscript_obj() +{ + return cvm::main()->proxy->script; +} + +/// Get a pointer to the colvar object pointed to by pobj +inline static colvar *colvar_obj(void *pobj) +{ + return reinterpret_cast<colvar *>(pobj); +} + +/// Get a pointer to the colvarbias object pointed to by pobj +inline static colvarbias *colvarbias_obj(void *pobj) +{ + return reinterpret_cast<colvarbias *>(pobj); +} + + +#define CVSCRIPT_COMM_FNAME(COMM) cvscript_ ## COMM + +#define CVSCRIPT_COMM_PROTO(COMM) \ + int CVSCRIPT_COMM_FNAME(COMM)(void *, int, unsigned char *const *); + +#define CVSCRIPT(COMM,HELP,N_ARGS_MIN,N_ARGS_MAX,ARGS,FN_BODY) \ + CVSCRIPT_COMM_PROTO(COMM) + +#undef COLVARSCRIPT_H +#endif // #ifndef COLVARSCRIPT_H + + +#ifdef COLVARSCRIPT_CPP +#define CVSCRIPT_COMM_FN(COMM,N_ARGS_MIN,N_ARGS_MAX,ARGS,FN_BODY) \ + int CVSCRIPT_COMM_FNAME(COMM)(void *pobj, \ + int objc, unsigned char *const objv[]) \ + { \ + colvarscript *script = colvarscript_obj(); \ + script->clear_results(); \ + if (objc < 2+N_ARGS_MIN) /* "cv" and "COMM" are 1st and 2nd */ { \ + script->set_str_result("Missing arguments\n" + \ + script->command_help(colvarscript::COMM)); \ + return COLVARSCRIPT_ERROR; \ + } \ + if (objc > 2+N_ARGS_MAX) { \ + script->set_str_result("Too many arguments\n" + \ + script->command_help(colvarscript::COMM)); \ + return COLVARSCRIPT_ERROR; \ + } \ + FN_BODY; \ + } +#undef CVSCRIPT +#define CVSCRIPT(COMM,HELP,N_ARGS_MIN,N_ARGS_MAX,ARGS,FN_BODY) \ + CVSCRIPT_COMM_FN(COMM,N_ARGS_MIN,N_ARGS_MAX,ARGS,FN_BODY) +#endif // #ifdef COLVARSCRIPT_CPP + + +#ifdef COLVARSCRIPT_INIT_FN +#define CVSCRIPT_COMM_INIT(COMM,HELP,ARGS) { \ + comm_str_map[#COMM] = COMM; \ + comm_help[COMM] = HELP; \ + comm_fns[COMM] = &(CVSCRIPT_COMM_FNAME(COMM)); \ + } +#undef CVSCRIPT +#define CVSCRIPT(COMM,HELP,N_ARGS_MIN,N_ARGS_MAX,ARGS,FN_BODY) \ + CVSCRIPT_COMM_INIT(COMM,HELP,ARGS) #endif + + +#if !defined(COLVARSCRIPT_H) || defined(COLVARSCRIPT_INIT_FN) +#define COLVARSCRIPT_H + +#ifndef COLVARSCRIPT_INIT_FN +#ifdef __cplusplus +extern "C" { +#endif +#endif + + // Add optional arguments for command-specific help? + CVSCRIPT(cv_help, + "Print the help message", + 0, 0, + {}, + script->set_str_result(script->help_string()); + return COLVARS_OK; + ) + + CVSCRIPT(cv_config, + "Read configuration from the given string", + 1, 1, + { "conf (str) - Configuration string" }, + std::string const conf(script->obj_to_str(objv[2])); + if (cvm::main()->read_config_string(conf) == COLVARS_OK) { + return COLVARS_OK; + } + script->set_str_result("Error parsing configuration string"); + return COLVARSCRIPT_ERROR; + ) + + CVSCRIPT(cv_addenergy, + "Add an energy to the MD engine", + 1, 1, + { "E (float) - Amount of energy to add" }, + cvm::main()->total_bias_energy += + strtod(script->obj_to_str(objv[2]), NULL); + return COLVARS_OK; + ) + + CVSCRIPT(cv_getenergy, + "Get the current Colvars energy", + 1, 1, + { "E (float) - Store the energy in this variable" }, + double *energy = reinterpret_cast<double *>(objv[2]); + *energy = cvm::main()->total_bias_energy; + return COLVARS_OK; + ) + +#ifndef COLVARSCRIPT_INIT_FN +#ifdef __cplusplus +} // extern "C" +#endif +#endif + +#undef CVSCRIPT + +#endif // #ifndef COLVARSCRIPT_H diff --git a/lib/colvars/colvartypes.h b/lib/colvars/colvartypes.h index fe3160eb4b3238ef036c2ea56d6b3954d73cb121..97257d18ad66d1da99b0b74586190e735687850b 100644 --- a/lib/colvars/colvartypes.h +++ b/lib/colvars/colvartypes.h @@ -705,7 +705,7 @@ public: { std::stringstream stream(s); size_t i = 0; - while ((stream >> data[i]) && (i < data.size())) { + while ((i < data.size()) && (stream >> data[i])) { i++; } if (i < data.size()) { diff --git a/src/USER-COLVARS/colvarproxy_lammps.cpp b/src/USER-COLVARS/colvarproxy_lammps.cpp index 17dff305673456b1312ef2be36393f6c8e54281e..ad2366f3d4963162e86f2ba10053aedf1b671455 100644 --- a/src/USER-COLVARS/colvarproxy_lammps.cpp +++ b/src/USER-COLVARS/colvarproxy_lammps.cpp @@ -120,12 +120,6 @@ colvarproxy_lammps::colvarproxy_lammps(LAMMPS_NS::LAMMPS *lmp, if (restart_output_prefix_str.rfind(".*") != std::string::npos) restart_output_prefix_str.erase(restart_output_prefix_str.rfind(".*"),2); -#if defined(_OPENMP) - if (smp_thread_id() == 0) { - omp_init_lock(&smp_lock_state); - } -#endif - // initialize multi-replica support, if available if (replica_enabled()) { MPI_Comm_rank(inter_comm, &inter_me); @@ -239,6 +233,9 @@ void colvarproxy_lammps::serialize_status(std::string &rst) std::ostringstream os; colvars->write_restart(os); rst = os.str(); + + // TODO separate this as its own function? + colvars->write_output_files(); } // set status from string @@ -331,89 +328,6 @@ int colvarproxy_lammps::backup_file(char const *filename) } -#if defined(_OPENMP) - - -// SMP support - -int colvarproxy_lammps::smp_enabled() -{ - if (b_smp_active) { - return COLVARS_OK; - } - return COLVARS_ERROR; -} - - -int colvarproxy_lammps::smp_colvars_loop() -{ - colvarmodule *cv = this->colvars; - colvarproxy_lammps *proxy = (colvarproxy_lammps *) cv->proxy; -#pragma omp parallel for - for (size_t i = 0; i < cv->variables_active_smp()->size(); i++) { - colvar *x = (*(cv->variables_active_smp()))[i]; - int x_item = (*(cv->variables_active_smp_items()))[i]; - if (cvm::debug()) { - cvm::log("["+cvm::to_str(proxy->smp_thread_id())+"/"+cvm::to_str(proxy->smp_num_threads())+ - "]: calc_colvars_items_smp(), i = "+cvm::to_str(i)+", cv = "+ - x->name+", cvc = "+cvm::to_str(x_item)+"\n"); - } - x->calc_cvcs(x_item, 1); - } - return cvm::get_error(); -} - - -int colvarproxy_lammps::smp_biases_loop() -{ - colvarmodule *cv = this->colvars; -#pragma omp parallel for - for (size_t i = 0; i < cv->biases_active()->size(); i++) { - colvarbias *b = (*(cv->biases_active()))[i]; - if (cvm::debug()) { - cvm::log("Calculating bias \""+b->name+"\" on thread "+ - cvm::to_str(smp_thread_id())+"\n"); - } - b->update(); - } - return cvm::get_error(); -} - - -int colvarproxy_lammps::smp_thread_id() -{ - return omp_get_thread_num(); -} - - -int colvarproxy_lammps::smp_num_threads() -{ - return omp_get_max_threads(); -} - - -int colvarproxy_lammps::smp_lock() -{ - omp_set_lock(&smp_lock_state); - return COLVARS_OK; -} - - -int colvarproxy_lammps::smp_trylock() -{ - return omp_test_lock(&smp_lock_state) ? COLVARS_OK : COLVARS_ERROR; -} - - -int colvarproxy_lammps::smp_unlock() -{ - omp_unset_lock(&smp_lock_state); - return COLVARS_OK; -} - -#endif - - // multi-replica support void colvarproxy_lammps::replica_comm_barrier() { diff --git a/src/USER-COLVARS/colvarproxy_lammps.h b/src/USER-COLVARS/colvarproxy_lammps.h index 6cdf0edfe8b97272aab39841d8229e8f08d37331..849af410f216894309d98ba158a4b3e2d1012d9f 100644 --- a/src/USER-COLVARS/colvarproxy_lammps.h +++ b/src/USER-COLVARS/colvarproxy_lammps.h @@ -25,10 +25,6 @@ #include <vector> #include <iostream> -#if defined(_OPENMP) -#include <omp.h> -#endif - /* struct for packed data communication of coordinates and forces. */ struct commdata { int tag,type; @@ -91,7 +87,8 @@ class colvarproxy_lammps : public colvarproxy { // methods for lammps to move data or trigger actions in the proxy public: void set_temperature(double t) { t_target = t; }; - bool total_forces_enabled() const { return total_force_requested; }; + bool total_forces_enabled() const { return total_force_requested; }; + bool total_forces_same_step() const { return true; }; bool want_exit() const { return do_exit; }; // perform colvars computation. returns biasing energy @@ -140,21 +137,6 @@ class colvarproxy_lammps : public colvarproxy { // implementation of optional methods from base class public: -#if defined(_OPENMP) - // SMP support - int smp_enabled(); - int smp_colvars_loop(); - int smp_biases_loop(); - int smp_thread_id(); - int smp_num_threads(); -protected: - omp_lock_t smp_lock_state; -public: - int smp_lock(); - int smp_trylock(); - int smp_unlock(); -#endif - // Multi-replica support // Indicate if multi-replica support is available and active virtual bool replica_enabled() { return (inter_comm != MPI_COMM_NULL); } diff --git a/src/USER-COLVARS/colvarproxy_lammps_version.h b/src/USER-COLVARS/colvarproxy_lammps_version.h index 0eb6f2d95ac6bbb4c98fe5c75fc2308a7758342c..e89e00ba039e3c03b32557d28f857216780d6519 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-07-19" +#define COLVARPROXY_VERSION "2017-10-11" // 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/src/USER-COLVARS/fix_colvars.cpp b/src/USER-COLVARS/fix_colvars.cpp index 59e6c46b76113bd1594dcf2acb983ee75212b15b..5897b9c5f0191ae2bb77d3ca1870fa595d987a0c 100644 --- a/src/USER-COLVARS/fix_colvars.cpp +++ b/src/USER-COLVARS/fix_colvars.cpp @@ -913,6 +913,7 @@ void FixColvars::write_restart(FILE *fp) if (me == 0) { std::string rest_text(""); proxy->serialize_status(rest_text); + // TODO call write_output_files() const char *cvm_state = rest_text.c_str(); int len = strlen(cvm_state) + 1; // need to include terminating NULL byte. fwrite(&len,sizeof(int),1,fp);