forked from lijiext/lammps
536 lines
17 KiB
C++
536 lines
17 KiB
C++
/// -*- c++ -*-
|
|
|
|
#include "colvarmodule.h"
|
|
#include "colvar.h"
|
|
#include "colvarbias_abf.h"
|
|
|
|
/// ABF bias constructor; parses the config file
|
|
|
|
colvarbias_abf::colvarbias_abf(std::string const &conf, char const *key)
|
|
: colvarbias(conf, key),
|
|
gradients(NULL),
|
|
samples(NULL)
|
|
{
|
|
// TODO relax this in case of VMD plugin
|
|
if (cvm::temperature() == 0.0)
|
|
cvm::log("WARNING: ABF should not be run without a thermostat or at 0 Kelvin!\n");
|
|
|
|
// ************* parsing general ABF options ***********************
|
|
|
|
get_keyval(conf, "applyBias", apply_bias, true);
|
|
if (!apply_bias) cvm::log("WARNING: ABF biases will *not* be applied!\n");
|
|
|
|
get_keyval(conf, "updateBias", update_bias, true);
|
|
if (!update_bias) cvm::log("WARNING: ABF biases will *not* be updated!\n");
|
|
|
|
get_keyval(conf, "hideJacobian", hide_Jacobian, false);
|
|
if (hide_Jacobian) {
|
|
cvm::log("Jacobian (geometric) forces will be handled internally.\n");
|
|
} else {
|
|
cvm::log("Jacobian (geometric) forces will be included in reported free energy gradients.\n");
|
|
}
|
|
|
|
get_keyval(conf, "fullSamples", full_samples, 200);
|
|
if ( full_samples <= 1 ) full_samples = 1;
|
|
min_samples = full_samples / 2;
|
|
// full_samples - min_samples >= 1 is guaranteed
|
|
|
|
get_keyval(conf, "inputPrefix", input_prefix, std::vector<std::string> ());
|
|
get_keyval(conf, "outputFreq", output_freq, cvm::restart_out_freq);
|
|
get_keyval(conf, "historyFreq", history_freq, 0);
|
|
b_history_files = (history_freq > 0);
|
|
|
|
// shared ABF
|
|
get_keyval(conf, "shared", shared_on, false);
|
|
if (shared_on) {
|
|
if (!cvm::replica_enabled() || cvm::replica_num() <= 1)
|
|
cvm::error("Error: shared ABF requires more than one replica.");
|
|
else
|
|
cvm::log("shared ABF will be applied among "+ cvm::to_str(cvm::replica_num()) + " replicas.\n");
|
|
|
|
// If shared_freq is not set, we default to output_freq
|
|
get_keyval(conf, "sharedFreq", shared_freq, output_freq);
|
|
}
|
|
|
|
// ************* checking the associated colvars *******************
|
|
|
|
if (colvars.size() == 0) {
|
|
cvm::error("Error: no collective variables specified for the ABF bias.\n");
|
|
}
|
|
|
|
if (update_bias) {
|
|
// Request calculation of system force (which also checks for availability)
|
|
enable(f_cvb_get_system_force);
|
|
}
|
|
if (apply_bias) {
|
|
enable(f_cvb_apply_force);
|
|
}
|
|
|
|
for (size_t i = 0; i < colvars.size(); i++) {
|
|
|
|
if (colvars[i]->value().type() != colvarvalue::type_scalar) {
|
|
cvm::error("Error: ABF bias can only use scalar-type variables.\n");
|
|
}
|
|
colvars[i]->enable(f_cv_grid);
|
|
if (hide_Jacobian) {
|
|
colvars[i]->enable(f_cv_hide_Jacobian);
|
|
}
|
|
|
|
// Here we could check for orthogonality of the Cartesian coordinates
|
|
// and make it just a warning if some parameter is set?
|
|
}
|
|
|
|
if (get_keyval(conf, "maxForce", max_force)) {
|
|
if (max_force.size() != colvars.size()) {
|
|
cvm::error("Error: Number of parameters to maxForce does not match number of colvars.");
|
|
}
|
|
for (size_t i=0; i<colvars.size(); i++) {
|
|
if (max_force[i] < 0.0) {
|
|
cvm::error("Error: maxForce should be non-negative.");
|
|
}
|
|
}
|
|
cap_force = true;
|
|
} else {
|
|
cap_force = false;
|
|
}
|
|
|
|
bin.assign(colvars.size(), 0);
|
|
force_bin.assign(colvars.size(), 0);
|
|
force = new cvm::real [colvars.size()];
|
|
|
|
// Construct empty grids based on the colvars
|
|
if (cvm::debug()) {
|
|
cvm::log("Allocating count and free energy gradient grids.\n");
|
|
}
|
|
|
|
samples = new colvar_grid_count(colvars);
|
|
gradients = new colvar_grid_gradient(colvars);
|
|
gradients->samples = samples;
|
|
samples->has_parent_data = true;
|
|
|
|
// For shared ABF, we store a second set of grids.
|
|
// This used to be only if "shared" was defined,
|
|
// but now we allow calling share externally (e.g. from Tcl).
|
|
last_samples = new colvar_grid_count(colvars);
|
|
last_gradients = new colvar_grid_gradient(colvars);
|
|
last_gradients->samples = last_samples;
|
|
last_samples->has_parent_data = true;
|
|
shared_last_step = -1;
|
|
|
|
// If custom grids are provided, read them
|
|
if ( input_prefix.size() > 0 ) {
|
|
read_gradients_samples();
|
|
}
|
|
|
|
cvm::log("Finished ABF setup.\n");
|
|
}
|
|
|
|
/// Destructor
|
|
colvarbias_abf::~colvarbias_abf()
|
|
{
|
|
if (samples) {
|
|
delete samples;
|
|
samples = NULL;
|
|
}
|
|
|
|
if (gradients) {
|
|
delete gradients;
|
|
gradients = NULL;
|
|
}
|
|
|
|
// shared ABF
|
|
// We used to only do this if "shared" was defined,
|
|
// but now we can call shared externally
|
|
if (last_samples) {
|
|
delete last_samples;
|
|
last_samples = NULL;
|
|
}
|
|
|
|
if (last_gradients) {
|
|
delete last_gradients;
|
|
last_gradients = NULL;
|
|
}
|
|
|
|
delete [] force;
|
|
|
|
if (cvm::n_abf_biases > 0)
|
|
cvm::n_abf_biases -= 1;
|
|
}
|
|
|
|
|
|
/// Update the FE gradient, compute and apply biasing force
|
|
/// also output data to disk if needed
|
|
|
|
int colvarbias_abf::update()
|
|
{
|
|
if (cvm::debug()) cvm::log("Updating ABF bias " + this->name);
|
|
|
|
if (cvm::step_relative() == 0) {
|
|
|
|
// At first timestep, do only:
|
|
// initialization stuff (file operations relying on n_abf_biases
|
|
// compute current value of colvars
|
|
|
|
for (size_t i=0; i<colvars.size(); i++) {
|
|
bin[i] = samples->current_bin_scalar(i);
|
|
}
|
|
|
|
} else {
|
|
|
|
for (size_t i=0; i<colvars.size(); i++) {
|
|
bin[i] = samples->current_bin_scalar(i);
|
|
}
|
|
|
|
if ( update_bias && samples->index_ok(force_bin) ) {
|
|
// Only if requested and within bounds of the grid...
|
|
|
|
for (size_t i=0; i<colvars.size(); i++) { // get forces(lagging by 1 timestep) from colvars
|
|
force[i] = colvars[i]->system_force();
|
|
}
|
|
gradients->acc_force(force_bin, force);
|
|
}
|
|
}
|
|
|
|
// save bin for next timestep
|
|
force_bin = bin;
|
|
|
|
// Reset biasing forces from previous timestep
|
|
for (size_t i=0; i<colvars.size(); i++) {
|
|
colvar_forces[i].reset();
|
|
}
|
|
|
|
// Compute and apply the new bias, if applicable
|
|
if ( apply_bias && samples->index_ok(bin) ) {
|
|
|
|
size_t count = samples->value(bin);
|
|
cvm::real fact = 1.0;
|
|
|
|
// Factor that ensures smooth introduction of the force
|
|
if ( count < full_samples ) {
|
|
fact = ( count < min_samples) ? 0.0 :
|
|
(cvm::real(count - min_samples)) / (cvm::real(full_samples - min_samples));
|
|
}
|
|
|
|
const cvm::real * grad = &(gradients->value(bin));
|
|
|
|
if ( fact != 0.0 ) {
|
|
if ( (colvars.size() == 1) && colvars[0]->periodic_boundaries() ) {
|
|
// Enforce a zero-mean bias on periodic, 1D coordinates
|
|
// in other words: boundary condition is that the biasing potential is periodic
|
|
colvar_forces[0].real_value = fact * (grad[0] / cvm::real(count) - gradients->average());
|
|
} else {
|
|
for (size_t i=0; i<colvars.size(); i++) {
|
|
// subtracting the mean force (opposite of the FE gradient) means adding the gradient
|
|
colvar_forces[i].real_value = fact * grad[i] / cvm::real(count);
|
|
}
|
|
}
|
|
if (cap_force) {
|
|
for (size_t i=0; i<colvars.size(); i++) {
|
|
if ( colvar_forces[i].real_value * colvar_forces[i].real_value > max_force[i] * max_force[i] ) {
|
|
colvar_forces[i].real_value = (colvar_forces[i].real_value > 0 ? max_force[i] : -1.0 * max_force[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// update the output prefix; TODO: move later to setup_output() function
|
|
if ( cvm::n_abf_biases == 1 && cvm::n_meta_biases == 0 ) {
|
|
// This is the only ABF bias
|
|
output_prefix = cvm::output_prefix;
|
|
} else {
|
|
output_prefix = cvm::output_prefix + "." + this->name;
|
|
}
|
|
|
|
if (output_freq && (cvm::step_absolute() % output_freq) == 0) {
|
|
if (cvm::debug()) cvm::log("ABF bias trying to write gradients and samples to disk");
|
|
write_gradients_samples(output_prefix);
|
|
}
|
|
|
|
if (b_history_files && (cvm::step_absolute() % history_freq) == 0) {
|
|
cvm::log("ABFHISTORYFILE "+cvm::to_str(cvm::step_absolute()));
|
|
// file already exists iff cvm::step_relative() > 0
|
|
// otherwise, backup and replace
|
|
write_gradients_samples(output_prefix + ".hist", (cvm::step_relative() > 0));
|
|
}
|
|
|
|
if (shared_on && shared_last_step >= 0 && cvm::step_absolute() % shared_freq == 0) {
|
|
// Share gradients and samples for shared ABF.
|
|
replica_share();
|
|
}
|
|
|
|
// Prepare for the first sharing.
|
|
if (shared_last_step < 0) {
|
|
// Copy the current gradient and count values into last.
|
|
last_gradients->copy_grid(*gradients);
|
|
last_samples->copy_grid(*samples);
|
|
shared_last_step = cvm::step_absolute();
|
|
cvm::log("Prepared sample and gradient buffers at step "+cvm::to_str(cvm::step_absolute())+".");
|
|
}
|
|
|
|
return COLVARS_OK;
|
|
}
|
|
|
|
int colvarbias_abf::replica_share() {
|
|
int p;
|
|
|
|
if ( !cvm::replica_enabled() ) {
|
|
cvm::error("Error: shared ABF: No replicas.\n");
|
|
return COLVARS_ERROR;
|
|
}
|
|
// We must have stored the last_gradients and last_samples.
|
|
if (shared_last_step < 0 ) {
|
|
cvm::error("Error: shared ABF: Tried to apply shared ABF before any sampling had occurred.\n");
|
|
return COLVARS_ERROR;
|
|
}
|
|
|
|
// Share gradients for shared ABF.
|
|
cvm::log("shared ABF: Sharing gradient and samples among replicas at step "+cvm::to_str(cvm::step_absolute()) );
|
|
|
|
// Count of data items.
|
|
size_t data_n = gradients->raw_data_num();
|
|
size_t samp_start = data_n*sizeof(cvm::real);
|
|
size_t msg_total = data_n*sizeof(size_t) + samp_start;
|
|
char* msg_data = new char[msg_total];
|
|
|
|
if (cvm::replica_index() == 0) {
|
|
// Replica 0 collects the delta gradient and count from the others.
|
|
for (p = 1; p < cvm::replica_num(); p++) {
|
|
// Receive the deltas.
|
|
cvm::replica_comm_recv(msg_data, msg_total, p);
|
|
|
|
// Map the deltas from the others into the grids.
|
|
last_gradients->raw_data_in((cvm::real*)(&msg_data[0]));
|
|
last_samples->raw_data_in((size_t*)(&msg_data[samp_start]));
|
|
|
|
// Combine the delta gradient and count of the other replicas
|
|
// with Replica 0's current state (including its delta).
|
|
gradients->add_grid( *last_gradients );
|
|
samples->add_grid( *last_samples );
|
|
}
|
|
|
|
// Now we must send the combined gradient to the other replicas.
|
|
gradients->raw_data_out((cvm::real*)(&msg_data[0]));
|
|
samples->raw_data_out((size_t*)(&msg_data[samp_start]));
|
|
for (p = 1; p < cvm::replica_num(); p++) {
|
|
cvm::replica_comm_send(msg_data, msg_total, p);
|
|
}
|
|
|
|
} else {
|
|
// All other replicas send their delta gradient and count.
|
|
// Calculate the delta gradient and count.
|
|
last_gradients->delta_grid(*gradients);
|
|
last_samples->delta_grid(*samples);
|
|
|
|
// Cast the raw char data to the gradient and samples.
|
|
last_gradients->raw_data_out((cvm::real*)(&msg_data[0]));
|
|
last_samples->raw_data_out((size_t*)(&msg_data[samp_start]));
|
|
cvm::replica_comm_send(msg_data, msg_total, 0);
|
|
|
|
// We now receive the combined gradient from Replica 0.
|
|
cvm::replica_comm_recv(msg_data, msg_total, 0);
|
|
// We sync to the combined gradient computed by Replica 0.
|
|
gradients->raw_data_in((cvm::real*)(&msg_data[0]));
|
|
samples->raw_data_in((size_t*)(&msg_data[samp_start]));
|
|
}
|
|
|
|
// Without a barrier it's possible that one replica starts
|
|
// share 2 when other replicas haven't finished share 1.
|
|
cvm::replica_comm_barrier();
|
|
// Done syncing the replicas.
|
|
delete[] msg_data;
|
|
|
|
// Copy the current gradient and count values into last.
|
|
last_gradients->copy_grid(*gradients);
|
|
last_samples->copy_grid(*samples);
|
|
shared_last_step = cvm::step_absolute();
|
|
|
|
return COLVARS_OK;
|
|
}
|
|
|
|
void colvarbias_abf::write_gradients_samples(const std::string &prefix, bool append)
|
|
{
|
|
std::string samples_out_name = prefix + ".count";
|
|
std::string gradients_out_name = prefix + ".grad";
|
|
std::ios::openmode mode = (append ? std::ios::app : std::ios::out);
|
|
|
|
cvm::ofstream samples_os;
|
|
cvm::ofstream gradients_os;
|
|
|
|
if (!append) cvm::backup_file(samples_out_name.c_str());
|
|
samples_os.open(samples_out_name.c_str(), mode);
|
|
if (!samples_os.is_open()) {
|
|
cvm::error("Error opening ABF samples file " + samples_out_name + " for writing");
|
|
}
|
|
samples->write_multicol(samples_os);
|
|
samples_os.close();
|
|
|
|
if (!append) cvm::backup_file(gradients_out_name.c_str());
|
|
gradients_os.open(gradients_out_name.c_str(), mode);
|
|
if (!gradients_os.is_open()) {
|
|
cvm::error("Error opening ABF gradient file " + gradients_out_name + " for writing");
|
|
}
|
|
gradients->write_multicol(gradients_os);
|
|
gradients_os.close();
|
|
|
|
if (colvars.size() == 1) {
|
|
std::string pmf_out_name = prefix + ".pmf";
|
|
if (!append) cvm::backup_file(pmf_out_name.c_str());
|
|
cvm::ofstream pmf_os;
|
|
// Do numerical integration and output a PMF
|
|
pmf_os.open(pmf_out_name.c_str(), mode);
|
|
if (!pmf_os.is_open()) cvm::error("Error opening pmf file " + pmf_out_name + " for writing");
|
|
gradients->write_1D_integral(pmf_os);
|
|
pmf_os << std::endl;
|
|
pmf_os.close();
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
// For Tcl implementation of selection rules.
|
|
/// Give the total number of bins for a given bias.
|
|
int colvarbias_abf::bin_num() {
|
|
return samples->number_of_points(0);
|
|
}
|
|
/// Calculate the bin index for a given bias.
|
|
int colvarbias_abf::current_bin() {
|
|
return samples->current_bin_scalar(0);
|
|
}
|
|
/// Give the count at a given bin index.
|
|
int colvarbias_abf::bin_count(int bin_index) {
|
|
if (bin_index < 0 || bin_index >= bin_num()) {
|
|
cvm::error("Error: Tried to get bin count from invalid bin index "+cvm::to_str(bin_index));
|
|
return -1;
|
|
}
|
|
std::vector<int> ix(1,(int)bin_index);
|
|
return samples->value(ix);
|
|
}
|
|
|
|
|
|
void colvarbias_abf::read_gradients_samples()
|
|
{
|
|
std::string samples_in_name, gradients_in_name;
|
|
|
|
for ( size_t i = 0; i < input_prefix.size(); i++ ) {
|
|
samples_in_name = input_prefix[i] + ".count";
|
|
gradients_in_name = input_prefix[i] + ".grad";
|
|
// For user-provided files, the per-bias naming scheme may not apply
|
|
|
|
std::ifstream is;
|
|
|
|
cvm::log("Reading sample count from " + samples_in_name + " and gradients from " + gradients_in_name);
|
|
is.open(samples_in_name.c_str());
|
|
if (!is.is_open()) cvm::error("Error opening ABF samples file " + samples_in_name + " for reading");
|
|
samples->read_multicol(is, true);
|
|
is.close();
|
|
is.clear();
|
|
|
|
is.open(gradients_in_name.c_str());
|
|
if (!is.is_open()) cvm::error("Error opening ABF gradient file " + gradients_in_name + " for reading");
|
|
gradients->read_multicol(is, true);
|
|
is.close();
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
std::ostream & colvarbias_abf::write_restart(std::ostream& os)
|
|
{
|
|
|
|
std::ios::fmtflags flags(os.flags());
|
|
os.setf(std::ios::fmtflags(0), std::ios::floatfield); // default floating-point format
|
|
|
|
os << "abf {\n"
|
|
<< " configuration {\n"
|
|
<< " name " << this->name << "\n";
|
|
os << " }\n";
|
|
|
|
os << "samples\n";
|
|
samples->write_raw(os, 8);
|
|
|
|
os << "\ngradient\n";
|
|
gradients->write_raw(os);
|
|
|
|
os << "}\n\n";
|
|
|
|
os.flags(flags);
|
|
return os;
|
|
}
|
|
|
|
|
|
std::istream & colvarbias_abf::read_restart(std::istream& is)
|
|
{
|
|
if ( input_prefix.size() > 0 ) {
|
|
cvm::error("ERROR: cannot provide both inputPrefix and restart information(colvarsInput)");
|
|
}
|
|
|
|
size_t const start_pos = is.tellg();
|
|
|
|
cvm::log("Restarting ABF bias \""+
|
|
this->name+"\".\n");
|
|
std::string key, brace, conf;
|
|
|
|
if ( !(is >> key) || !(key == "abf") ||
|
|
!(is >> brace) || !(brace == "{") ||
|
|
!(is >> colvarparse::read_block("configuration", conf)) ) {
|
|
cvm::log("Error: in reading restart configuration for ABF bias \""+
|
|
this->name+"\" at position "+
|
|
cvm::to_str(is.tellg())+" in stream.\n");
|
|
is.clear();
|
|
is.seekg(start_pos, std::ios::beg);
|
|
is.setstate(std::ios::failbit);
|
|
return is;
|
|
}
|
|
|
|
std::string name = "";
|
|
if ( (colvarparse::get_keyval(conf, "name", name, std::string(""), colvarparse::parse_silent)) &&
|
|
(name != this->name) )
|
|
cvm::error("Error: in the restart file, the "
|
|
"\"abf\" block has wrong name(" + name + ")\n");
|
|
if ( name == "" ) {
|
|
cvm::error("Error: \"abf\" block in the restart file has no name.\n");
|
|
}
|
|
|
|
if ( !(is >> key) || !(key == "samples")) {
|
|
cvm::log("Error: in reading restart configuration for ABF bias \""+
|
|
this->name+"\" at position "+
|
|
cvm::to_str(is.tellg())+" in stream.\n");
|
|
is.clear();
|
|
is.seekg(start_pos, std::ios::beg);
|
|
is.setstate(std::ios::failbit);
|
|
return is;
|
|
}
|
|
if (! samples->read_raw(is)) {
|
|
is.clear();
|
|
is.seekg(start_pos, std::ios::beg);
|
|
is.setstate(std::ios::failbit);
|
|
return is;
|
|
}
|
|
|
|
if ( !(is >> key) || !(key == "gradient")) {
|
|
cvm::log("Error: in reading restart configuration for ABF bias \""+
|
|
this->name+"\" at position "+
|
|
cvm::to_str(is.tellg())+" in stream.\n");
|
|
is.clear();
|
|
is.seekg(start_pos, std::ios::beg);
|
|
is.setstate(std::ios::failbit);
|
|
return is;
|
|
}
|
|
if (! gradients->read_raw(is)) {
|
|
is.clear();
|
|
is.seekg(start_pos, std::ios::beg);
|
|
is.setstate(std::ios::failbit);
|
|
return is;
|
|
}
|
|
|
|
is >> brace;
|
|
if (brace != "}") {
|
|
cvm::error("Error: corrupt restart information for ABF bias \""+
|
|
this->name+"\": no matching brace at position "+
|
|
cvm::to_str(is.tellg())+" in the restart file.\n");
|
|
is.setstate(std::ios::failbit);
|
|
}
|
|
return is;
|
|
}
|