Files
lammps/lib/colvars/colvarcomp_protein.cpp
Giacomo Fiorin 1220bea011 Update Colvars to version 2022-05-09
This update includes one new feature (neural-network based collective
variables), several small enhancements (including an automatic definition of
grid boundaries for angle-based CVs, and a normalization option for
eigenvector-based CVs), bugfixes and documentation improvements.

Usage information for specific features included in the Colvars library
(i.e. not just the library as a whole) is now also reported to the screen or
LAMMPS logfile (as is done already in other LAMMPS classes).

Notable to LAMMPS code development are the removals of duplicated code and of
ambiguously-named preprocessor defines in the Colvars headers.  Since the
last PR, the existing regression tests have also been running automatically
via GitHub Actions.

The following pull requests in the Colvars repository are relevant to LAMMPS:

- 475 Remove fatal error condition
  https://github.com/Colvars/colvars/pull/475 (@jhenin, @giacomofiorin)

- 474 Allow normalizing eigenvector vector components to deal with unit change
  https://github.com/Colvars/colvars/pull/474 (@giacomofiorin, @jhenin)

- 470 Better error handling in the initialization of NeuralNetwork CV
  https://github.com/Colvars/colvars/pull/470 (@HanatoK)

- 468 Add examples of histogram configuration, with and without explicit grid parameters
  https://github.com/Colvars/colvars/pull/468 (@giacomofiorin)

- 464 Fix #463 using more fine-grained features
  https://github.com/Colvars/colvars/pull/464 (@jhenin, @giacomofiorin)

- 447 [RFC] New option "scaledBiasingForce" for colvarbias
  https://github.com/Colvars/colvars/pull/447 (@HanatoK, @jhenin)

- 444 [RFC] Implementation of dense neural network as CV
  https://github.com/Colvars/colvars/pull/444 (@HanatoK, @giacomofiorin, @jhenin)

- 443 Fix explicit gradient dependency of sub-CVs
  https://github.com/Colvars/colvars/pull/443 (@HanatoK, @jhenin)

- 442 Persistent bias count
  https://github.com/Colvars/colvars/pull/442 (@jhenin, @giacomofiorin)

- 437 Return type of bias from scripting interface
  https://github.com/Colvars/colvars/pull/437 (@giacomofiorin)

- 434 More flexible use of boundaries from colvars by grids
  https://github.com/Colvars/colvars/pull/434 (@jhenin)

- 433 Prevent double-free in linearCombination
  https://github.com/Colvars/colvars/pull/433 (@HanatoK)

- 428 More complete documentation for index file format (NDX)
  https://github.com/Colvars/colvars/pull/428 (@giacomofiorin)

- 426 Integrate functional version of backup_file() into base proxy class
  https://github.com/Colvars/colvars/pull/426 (@giacomofiorin)

- 424 Track CVC inheritance when documenting feature usage
  https://github.com/Colvars/colvars/pull/424 (@giacomofiorin)

- 419 Generate citation report while running computations
  https://github.com/Colvars/colvars/pull/419 (@giacomofiorin, @jhenin)

- 415 Rebin metadynamics bias from explicit hills when available
  https://github.com/Colvars/colvars/pull/415 (@giacomofiorin)

- 312 Ignore a keyword if it has content to the left of it (regardless of braces)
  https://github.com/Colvars/colvars/pull/312 (@giacomofiorin)

Authors: @giacomofiorin, @HanatoK, @jhenin
2022-05-10 11:24:54 -04:00

496 lines
14 KiB
C++

// -*- c++ -*-
// This file is part of the Collective Variables module (Colvars).
// The original version of Colvars and its updates are located at:
// https://github.com/Colvars/colvars
// Please update all Colvars source files before making any changes.
// If you wish to distribute your changes, please submit them to the
// Colvars repository at GitHub.
#include <algorithm>
#include "colvarmodule.h"
#include "colvarvalue.h"
#include "colvarparse.h"
#include "colvar.h"
#include "colvarcomp.h"
colvar::alpha_angles::alpha_angles(std::string const &conf)
: cvc(conf)
{
set_function_type("alpha");
enable(f_cvc_explicit_gradient);
x.type(colvarvalue::type_scalar);
colvarproxy *proxy = cvm::main()->proxy;
std::string segment_id;
get_keyval(conf, "psfSegID", segment_id, std::string("MAIN"));
std::vector<int> residues;
{
std::string residues_conf = "";
key_lookup(conf, "residueRange", &residues_conf);
if (residues_conf.size()) {
std::istringstream is(residues_conf);
int initial, final;
char dash;
if ( (is >> initial) && (initial > 0) &&
(is >> dash) && (dash == '-') &&
(is >> final) && (final > 0) ) {
for (int rnum = initial; rnum <= final; rnum++) {
residues.push_back(rnum);
}
}
} else {
cvm::error("Error: no residues defined in \"residueRange\".\n");
return;
}
}
if (residues.size() < 5) {
cvm::error("Error: not enough residues defined in \"residueRange\".\n");
return;
}
std::string const &sid = segment_id;
std::vector<int> const &r = residues;
get_keyval(conf, "hBondCoeff", hb_coeff, 0.5);
if ( (hb_coeff < 0.0) || (hb_coeff > 1.0) ) {
cvm::error("Error: hBondCoeff must be defined between 0 and 1.\n");
return;
}
get_keyval(conf, "angleRef", theta_ref, 88.0);
get_keyval(conf, "angleTol", theta_tol, 15.0);
if (hb_coeff < 1.0) {
for (size_t i = 0; i < residues.size()-2; i++) {
theta.push_back(new colvar::angle(cvm::atom(r[i ], "CA", sid),
cvm::atom(r[i+1], "CA", sid),
cvm::atom(r[i+2], "CA", sid)));
register_atom_group(theta.back()->atom_groups[0]);
register_atom_group(theta.back()->atom_groups[1]);
register_atom_group(theta.back()->atom_groups[2]);
}
} else {
cvm::log("The hBondCoeff specified will disable the Calpha-Calpha-Calpha angle terms.\n");
}
{
cvm::real r0;
size_t en, ed;
get_keyval(conf, "hBondCutoff", r0, (3.3 * proxy->angstrom_value));
get_keyval(conf, "hBondExpNumer", en, 6);
get_keyval(conf, "hBondExpDenom", ed, 8);
if (hb_coeff > 0.0) {
for (size_t i = 0; i < residues.size()-4; i++) {
hb.push_back(new colvar::h_bond(cvm::atom(r[i ], "O", sid),
cvm::atom(r[i+4], "N", sid),
r0, en, ed));
register_atom_group(hb.back()->atom_groups[0]);
}
} else {
cvm::log("The hBondCoeff specified will disable the hydrogen bond terms.\n");
}
}
}
colvar::alpha_angles::alpha_angles()
: cvc()
{
set_function_type("alphaAngles");
enable(f_cvc_explicit_gradient);
x.type(colvarvalue::type_scalar);
}
colvar::alpha_angles::~alpha_angles()
{
while (theta.size() != 0) {
delete theta.back();
theta.pop_back();
}
while (hb.size() != 0) {
delete hb.back();
hb.pop_back();
}
// Our references to atom groups have become invalid now that children cvcs are deleted
atom_groups.clear();
}
void colvar::alpha_angles::calc_value()
{
x.real_value = 0.0;
if (theta.size()) {
cvm::real const theta_norm =
(1.0-hb_coeff) / cvm::real(theta.size());
for (size_t i = 0; i < theta.size(); i++) {
(theta[i])->calc_value();
cvm::real const t = ((theta[i])->value().real_value-theta_ref)/theta_tol;
cvm::real const f = ( (1.0 - (t*t)) /
(1.0 - (t*t*t*t)) );
x.real_value += theta_norm * f;
if (cvm::debug())
cvm::log("Calpha-Calpha angle no. "+cvm::to_str(i+1)+" in \""+
this->name+"\" has a value of "+
(cvm::to_str((theta[i])->value().real_value))+
" degrees, f = "+cvm::to_str(f)+".\n");
}
}
if (hb.size()) {
cvm::real const hb_norm =
hb_coeff / cvm::real(hb.size());
for (size_t i = 0; i < hb.size(); i++) {
(hb[i])->calc_value();
x.real_value += hb_norm * (hb[i])->value().real_value;
if (cvm::debug())
cvm::log("Hydrogen bond no. "+cvm::to_str(i+1)+" in \""+
this->name+"\" has a value of "+
(cvm::to_str((hb[i])->value().real_value))+".\n");
}
}
}
void colvar::alpha_angles::calc_gradients()
{
size_t i;
for (i = 0; i < theta.size(); i++)
(theta[i])->calc_gradients();
for (i = 0; i < hb.size(); i++)
(hb[i])->calc_gradients();
}
void colvar::alpha_angles::collect_gradients(std::vector<int> const &atom_ids, std::vector<cvm::rvector> &atomic_gradients)
{
cvm::real cvc_coeff = sup_coeff * cvm::real(sup_np) * cvm::integer_power(value().real_value, sup_np-1);
if (theta.size()) {
cvm::real const theta_norm = (1.0-hb_coeff) / cvm::real(theta.size());
for (size_t i = 0; i < theta.size(); i++) {
cvm::real const t = ((theta[i])->value().real_value-theta_ref)/theta_tol;
cvm::real const f = ( (1.0 - (t*t)) /
(1.0 - (t*t*t*t)) );
cvm::real const dfdt =
1.0/(1.0 - (t*t*t*t)) *
( (-2.0 * t) + (-1.0*f)*(-4.0 * (t*t*t)) );
// Coefficient of this CVC's gradient in the colvar gradient, times coefficient of this
// angle's gradient in the CVC's gradient
cvm::real const coeff = cvc_coeff * theta_norm * dfdt * (1.0/theta_tol);
for (size_t j = 0; j < theta[i]->atom_groups.size(); j++) {
cvm::atom_group &ag = *(theta[i]->atom_groups[j]);
for (size_t k = 0; k < ag.size(); k++) {
size_t a = std::lower_bound(atom_ids.begin(), atom_ids.end(),
ag[k].id) - atom_ids.begin();
atomic_gradients[a] += coeff * ag[k].grad;
}
}
}
}
if (hb.size()) {
cvm::real const hb_norm = hb_coeff / cvm::real(hb.size());
for (size_t i = 0; i < hb.size(); i++) {
// Coefficient of this CVC's gradient in the colvar gradient, times coefficient of this
// hbond's gradient in the CVC's gradient
cvm::real const coeff = cvc_coeff * 0.5 * hb_norm;
for (size_t j = 0; j < hb[i]->atom_groups.size(); j++) {
cvm::atom_group &ag = *(hb[i]->atom_groups[j]);
for (size_t k = 0; k < ag.size(); k++) {
size_t a = std::lower_bound(atom_ids.begin(), atom_ids.end(),
ag[k].id) - atom_ids.begin();
atomic_gradients[a] += coeff * ag[k].grad;
}
}
}
}
}
void colvar::alpha_angles::apply_force(colvarvalue const &force)
{
if (theta.size()) {
cvm::real const theta_norm =
(1.0-hb_coeff) / cvm::real(theta.size());
for (size_t i = 0; i < theta.size(); i++) {
cvm::real const t = ((theta[i])->value().real_value-theta_ref)/theta_tol;
cvm::real const f = ( (1.0 - (t*t)) /
(1.0 - (t*t*t*t)) );
cvm::real const dfdt =
1.0/(1.0 - (t*t*t*t)) *
( (-2.0 * t) + (-1.0*f)*(-4.0 * (t*t*t)) );
(theta[i])->apply_force(theta_norm *
dfdt * (1.0/theta_tol) *
force.real_value );
}
}
if (hb.size()) {
cvm::real const hb_norm =
hb_coeff / cvm::real(hb.size());
for (size_t i = 0; i < hb.size(); i++) {
(hb[i])->apply_force(0.5 * hb_norm * force.real_value);
}
}
}
simple_scalar_dist_functions(alpha_angles)
//////////////////////////////////////////////////////////////////////
// dihedral principal component
//////////////////////////////////////////////////////////////////////
colvar::dihedPC::dihedPC(std::string const &conf)
: cvc(conf)
{
if (cvm::debug())
cvm::log("Initializing dihedral PC object.\n");
set_function_type("dihedPC");
// Supported through references to atom groups of children cvcs
enable(f_cvc_explicit_gradient);
x.type(colvarvalue::type_scalar);
std::string segment_id;
get_keyval(conf, "psfSegID", segment_id, std::string("MAIN"));
std::vector<int> residues;
{
std::string residues_conf = "";
key_lookup(conf, "residueRange", &residues_conf);
if (residues_conf.size()) {
std::istringstream is(residues_conf);
int initial, final;
char dash;
if ( (is >> initial) && (initial > 0) &&
(is >> dash) && (dash == '-') &&
(is >> final) && (final > 0) ) {
for (int rnum = initial; rnum <= final; rnum++) {
residues.push_back(rnum);
}
}
} else {
cvm::error("Error: no residues defined in \"residueRange\".\n");
return;
}
}
if (residues.size() < 2) {
cvm::error("Error: dihedralPC requires at least two residues.\n");
return;
}
std::string const &sid = segment_id;
std::vector<int> const &r = residues;
std::string vecFileName;
int vecNumber;
if (get_keyval(conf, "vectorFile", vecFileName, vecFileName)) {
get_keyval(conf, "vectorNumber", vecNumber, 0);
if (vecNumber < 1) {
cvm::error("A positive value of vectorNumber is required.");
return;
}
std::ifstream vecFile;
vecFile.open(vecFileName.c_str());
if (!vecFile.good()) {
cvm::error("Error opening dihedral PCA vector file " + vecFileName + " for reading");
}
// TODO: adapt to different formats by setting this flag
bool eigenvectors_as_columns = true;
if (eigenvectors_as_columns) {
// Carma-style dPCA file
std::string line;
cvm::real c;
while (vecFile.good()) {
getline(vecFile, line);
if (line.length() < 2) break;
std::istringstream ls(line);
for (int i=0; i<vecNumber; i++) ls >> c;
coeffs.push_back(c);
}
}
/* TODO Uncomment this when different formats are recognized
else {
// Eigenvectors as lines
// Skip to the right line
for (int i = 1; i<vecNumber; i++)
vecFile.ignore(999999, '\n');
if (!vecFile.good()) {
cvm::error("Error reading dihedral PCA vector file " + vecFileName);
}
std::string line;
getline(vecFile, line);
std::istringstream ls(line);
cvm::real c;
while (ls.good()) {
ls >> c;
coeffs.push_back(c);
}
}
*/
vecFile.close();
} else {
get_keyval(conf, "vector", coeffs, coeffs);
}
if ( coeffs.size() != 4 * (residues.size() - 1)) {
cvm::error("Error: wrong number of coefficients: " +
cvm::to_str(coeffs.size()) + ". Expected " +
cvm::to_str(4 * (residues.size() - 1)) +
" (4 coeffs per residue, minus one residue).\n");
return;
}
for (size_t i = 0; i < residues.size()-1; i++) {
// Psi
theta.push_back(new colvar::dihedral(cvm::atom(r[i ], "N", sid),
cvm::atom(r[i ], "CA", sid),
cvm::atom(r[i ], "C", sid),
cvm::atom(r[i+1], "N", sid)));
register_atom_group(theta.back()->atom_groups[0]);
register_atom_group(theta.back()->atom_groups[1]);
register_atom_group(theta.back()->atom_groups[2]);
register_atom_group(theta.back()->atom_groups[3]);
// Phi (next res)
theta.push_back(new colvar::dihedral(cvm::atom(r[i ], "C", sid),
cvm::atom(r[i+1], "N", sid),
cvm::atom(r[i+1], "CA", sid),
cvm::atom(r[i+1], "C", sid)));
register_atom_group(theta.back()->atom_groups[0]);
register_atom_group(theta.back()->atom_groups[1]);
register_atom_group(theta.back()->atom_groups[2]);
register_atom_group(theta.back()->atom_groups[3]);
}
if (cvm::debug())
cvm::log("Done initializing dihedPC object.\n");
}
colvar::dihedPC::dihedPC()
: cvc()
{
set_function_type("dihedPC");
// Supported through references to atom groups of children cvcs
enable(f_cvc_explicit_gradient);
x.type(colvarvalue::type_scalar);
}
colvar::dihedPC::~dihedPC()
{
while (theta.size() != 0) {
delete theta.back();
theta.pop_back();
}
// Our references to atom groups have become invalid now that children cvcs are deleted
atom_groups.clear();
}
void colvar::dihedPC::calc_value()
{
x.real_value = 0.0;
for (size_t i = 0; i < theta.size(); i++) {
theta[i]->calc_value();
cvm::real const t = (PI / 180.) * theta[i]->value().real_value;
x.real_value += coeffs[2*i ] * cvm::cos(t)
+ coeffs[2*i+1] * cvm::sin(t);
}
}
void colvar::dihedPC::calc_gradients()
{
for (size_t i = 0; i < theta.size(); i++) {
theta[i]->calc_gradients();
}
}
void colvar::dihedPC::collect_gradients(std::vector<int> const &atom_ids, std::vector<cvm::rvector> &atomic_gradients)
{
cvm::real cvc_coeff = sup_coeff * cvm::real(sup_np) * cvm::integer_power(value().real_value, sup_np-1);
for (size_t i = 0; i < theta.size(); i++) {
cvm::real const t = (PI / 180.) * theta[i]->value().real_value;
cvm::real const dcosdt = - (PI / 180.) * cvm::sin(t);
cvm::real const dsindt = (PI / 180.) * cvm::cos(t);
// Coefficient of this dihedPC's gradient in the colvar gradient, times coefficient of this
// dihedral's gradient in the dihedPC's gradient
cvm::real const coeff = cvc_coeff * (coeffs[2*i] * dcosdt + coeffs[2*i+1] * dsindt);
for (size_t j = 0; j < theta[i]->atom_groups.size(); j++) {
cvm::atom_group &ag = *(theta[i]->atom_groups[j]);
for (size_t k = 0; k < ag.size(); k++) {
size_t a = std::lower_bound(atom_ids.begin(), atom_ids.end(),
ag[k].id) - atom_ids.begin();
atomic_gradients[a] += coeff * ag[k].grad;
}
}
}
}
void colvar::dihedPC::apply_force(colvarvalue const &force)
{
for (size_t i = 0; i < theta.size(); i++) {
cvm::real const t = (PI / 180.) * theta[i]->value().real_value;
cvm::real const dcosdt = - (PI / 180.) * cvm::sin(t);
cvm::real const dsindt = (PI / 180.) * cvm::cos(t);
theta[i]->apply_force((coeffs[2*i ] * dcosdt +
coeffs[2*i+1] * dsindt) * force);
}
}
simple_scalar_dist_functions(dihedPC)