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
668 lines
22 KiB
C++
668 lines
22 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 "colvar.h"
|
|
#include "colvarcomp.h"
|
|
|
|
|
|
|
|
colvar::cvc::cvc()
|
|
{
|
|
description = "uninitialized colvar component";
|
|
b_try_scalable = true;
|
|
sup_coeff = 1.0;
|
|
sup_np = 1;
|
|
period = 0.0;
|
|
wrap_center = 0.0;
|
|
width = 0.0;
|
|
cvc::init_dependencies();
|
|
}
|
|
|
|
|
|
colvar::cvc::cvc(std::string const &conf)
|
|
{
|
|
description = "uninitialized colvar component";
|
|
b_try_scalable = true;
|
|
sup_coeff = 1.0;
|
|
sup_np = 1;
|
|
period = 0.0;
|
|
wrap_center = 0.0;
|
|
width = 0.0;
|
|
init_dependencies();
|
|
colvar::cvc::init(conf);
|
|
}
|
|
|
|
|
|
int colvar::cvc::set_function_type(std::string const &type)
|
|
{
|
|
function_type = type;
|
|
if (function_types.size() == 0) {
|
|
function_types.push_back(function_type);
|
|
} else {
|
|
if (function_types.back() != function_type) {
|
|
function_types.push_back(function_type);
|
|
}
|
|
}
|
|
for (size_t i = function_types.size()-1; i > 0; i--) {
|
|
cvm::main()->cite_feature(function_types[i]+" colvar component"+
|
|
" (derived from "+function_types[i-1]+")");
|
|
}
|
|
cvm::main()->cite_feature(function_types[0]+" colvar component");
|
|
return COLVARS_OK;
|
|
}
|
|
|
|
|
|
int colvar::cvc::init(std::string const &conf)
|
|
{
|
|
if (cvm::debug())
|
|
cvm::log("Initializing cvc base object.\n");
|
|
|
|
std::string const old_name(name);
|
|
|
|
if (name.size() > 0) {
|
|
cvm::log("Updating configuration for component \""+name+"\"\n");
|
|
}
|
|
|
|
if (get_keyval(conf, "name", name, name)) {
|
|
if (name.size() > 0) {
|
|
description = "cvc \"" + name + "\" of type " + function_type;
|
|
} else {
|
|
description = "unnamed cvc";
|
|
}
|
|
if ((name != old_name) && (old_name.size() > 0)) {
|
|
cvm::error("Error: cannot rename component \""+old_name+
|
|
"\" after initialization (new name = \""+name+"\")",
|
|
COLVARS_INPUT_ERROR);
|
|
name = old_name;
|
|
}
|
|
}
|
|
|
|
get_keyval(conf, "componentCoeff", sup_coeff, sup_coeff);
|
|
get_keyval(conf, "componentExp", sup_np, sup_np);
|
|
if (sup_coeff != 1.0 || sup_np != 1) {
|
|
cvm::main()->cite_feature("Linear and polynomial combination of colvar components");
|
|
}
|
|
// TODO these could be condensed into get_keyval()
|
|
register_param("componentCoeff", reinterpret_cast<void *>(&sup_coeff));
|
|
register_param("componentExp", reinterpret_cast<void *>(&sup_np));
|
|
|
|
get_keyval(conf, "period", period, period);
|
|
get_keyval(conf, "wrapAround", wrap_center, wrap_center);
|
|
// TODO when init() is called after all constructors, check periodic flag
|
|
register_param("period", reinterpret_cast<void *>(&period));
|
|
register_param("wrapAround", reinterpret_cast<void *>(&wrap_center));
|
|
|
|
get_keyval_feature(this, conf, "debugGradients",
|
|
f_cvc_debug_gradient, false, parse_silent);
|
|
|
|
bool b_no_PBC = !is_enabled(f_cvc_pbc_minimum_image); // Enabled by default
|
|
get_keyval(conf, "forceNoPBC", b_no_PBC, b_no_PBC);
|
|
if (b_no_PBC) {
|
|
disable(f_cvc_pbc_minimum_image);
|
|
} else {
|
|
enable(f_cvc_pbc_minimum_image);
|
|
}
|
|
|
|
// Attempt scalable calculations when in parallel? (By default yes, if available)
|
|
get_keyval(conf, "scalable", b_try_scalable, b_try_scalable);
|
|
|
|
if (cvm::debug())
|
|
cvm::log("Done initializing cvc base object.\n");
|
|
|
|
return cvm::get_error();
|
|
}
|
|
|
|
|
|
int colvar::cvc::init_total_force_params(std::string const &conf)
|
|
{
|
|
if (cvm::get_error()) return COLVARS_ERROR;
|
|
|
|
if (get_keyval_feature(this, conf, "oneSiteSystemForce",
|
|
f_cvc_one_site_total_force, is_enabled(f_cvc_one_site_total_force))) {
|
|
cvm::log("Warning: keyword \"oneSiteSystemForce\" is deprecated: "
|
|
"please use \"oneSiteTotalForce\" instead.\n");
|
|
}
|
|
if (get_keyval_feature(this, conf, "oneSiteTotalForce",
|
|
f_cvc_one_site_total_force, is_enabled(f_cvc_one_site_total_force))) {
|
|
cvm::log("Computing total force on group 1 only\n");
|
|
}
|
|
|
|
if (! is_enabled(f_cvc_one_site_total_force)) {
|
|
// check whether any of the other atom groups is dummy
|
|
std::vector<cvm::atom_group *>::iterator agi = atom_groups.begin();
|
|
agi++;
|
|
for ( ; agi != atom_groups.end(); agi++) {
|
|
if ((*agi)->b_dummy) {
|
|
provide(f_cvc_inv_gradient, false);
|
|
provide(f_cvc_Jacobian, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
return COLVARS_OK;
|
|
}
|
|
|
|
|
|
cvm::atom_group *colvar::cvc::parse_group(std::string const &conf,
|
|
char const *group_key,
|
|
bool optional)
|
|
{
|
|
cvm::atom_group *group = NULL;
|
|
std::string group_conf;
|
|
|
|
if (key_lookup(conf, group_key, &group_conf)) {
|
|
group = new cvm::atom_group(group_key);
|
|
|
|
if (b_try_scalable) {
|
|
if (is_available(f_cvc_scalable_com)
|
|
&& is_enabled(f_cvc_com_based)
|
|
&& !is_enabled(f_cvc_debug_gradient)) {
|
|
disable(f_cvc_explicit_gradient);
|
|
enable(f_cvc_scalable_com);
|
|
// The CVC makes the feature available;
|
|
// the atom group will enable it unless it needs to compute a rotational fit
|
|
group->provide(f_ag_scalable_com);
|
|
}
|
|
|
|
// TODO check for other types of parallelism here
|
|
}
|
|
|
|
if (group_conf.size() == 0) {
|
|
cvm::error("Error: atom group \""+group->key+
|
|
"\" is set, but has no definition.\n",
|
|
COLVARS_INPUT_ERROR);
|
|
return group;
|
|
}
|
|
|
|
cvm::increase_depth();
|
|
if (group->parse(group_conf) == COLVARS_OK) {
|
|
register_atom_group(group);
|
|
}
|
|
group->check_keywords(group_conf, group_key);
|
|
if (cvm::get_error()) {
|
|
cvm::error("Error parsing definition for atom group \""+
|
|
std::string(group_key)+"\".", COLVARS_INPUT_ERROR);
|
|
}
|
|
cvm::decrease_depth();
|
|
|
|
} else {
|
|
if (! optional) {
|
|
cvm::error("Error: definition for atom group \""+
|
|
std::string(group_key)+"\" not found.\n");
|
|
}
|
|
}
|
|
|
|
return group;
|
|
}
|
|
|
|
|
|
int colvar::cvc::init_dependencies() {
|
|
size_t i;
|
|
// Initialize static array once and for all
|
|
if (features().size() == 0) {
|
|
for (i = 0; i < colvardeps::f_cvc_ntot; i++) {
|
|
modify_features().push_back(new feature);
|
|
}
|
|
|
|
init_feature(f_cvc_active, "active", f_type_dynamic);
|
|
// The dependency below may become useful if we use dynamic atom groups
|
|
// require_feature_children(f_cvc_active, f_ag_active);
|
|
|
|
init_feature(f_cvc_scalar, "scalar", f_type_static);
|
|
|
|
init_feature(f_cvc_periodic, "periodic", f_type_static);
|
|
|
|
init_feature(f_cvc_width, "defined_width", f_type_static);
|
|
|
|
init_feature(f_cvc_lower_boundary, "defined_lower_boundary", f_type_static);
|
|
|
|
init_feature(f_cvc_upper_boundary, "defined_upper_boundary", f_type_static);
|
|
|
|
init_feature(f_cvc_gradient, "gradient", f_type_dynamic);
|
|
|
|
init_feature(f_cvc_explicit_gradient, "explicit_gradient", f_type_static);
|
|
require_feature_children(f_cvc_explicit_gradient, f_ag_explicit_gradient);
|
|
|
|
init_feature(f_cvc_inv_gradient, "inverse_gradient", f_type_dynamic);
|
|
require_feature_self(f_cvc_inv_gradient, f_cvc_gradient);
|
|
|
|
init_feature(f_cvc_debug_gradient, "debug_gradient", f_type_user);
|
|
require_feature_self(f_cvc_debug_gradient, f_cvc_gradient);
|
|
require_feature_self(f_cvc_debug_gradient, f_cvc_explicit_gradient);
|
|
|
|
init_feature(f_cvc_Jacobian, "Jacobian_derivative", f_type_dynamic);
|
|
require_feature_self(f_cvc_Jacobian, f_cvc_inv_gradient);
|
|
|
|
// Compute total force on first site only to avoid unwanted
|
|
// coupling to other colvars (see e.g. Ciccotti et al., 2005)
|
|
init_feature(f_cvc_one_site_total_force, "total_force_from_one_group", f_type_user);
|
|
require_feature_self(f_cvc_one_site_total_force, f_cvc_com_based);
|
|
|
|
init_feature(f_cvc_com_based, "function_of_centers_of_mass", f_type_static);
|
|
|
|
init_feature(f_cvc_pbc_minimum_image, "use_minimum-image_with_PBCs", f_type_user);
|
|
|
|
init_feature(f_cvc_scalable, "scalable_calculation", f_type_dynamic);
|
|
require_feature_self(f_cvc_scalable_com, f_cvc_scalable);
|
|
// CVC cannot compute atom-level gradients on rank 0 if colvar computation is distributed
|
|
exclude_feature_self(f_cvc_scalable, f_cvc_explicit_gradient);
|
|
|
|
init_feature(f_cvc_scalable_com, "scalable_calculation_of_centers_of_mass", f_type_static);
|
|
require_feature_self(f_cvc_scalable_com, f_cvc_com_based);
|
|
// CVC cannot compute atom-level gradients if computed on atom group COM
|
|
exclude_feature_self(f_cvc_scalable_com, f_cvc_explicit_gradient);
|
|
|
|
init_feature(f_cvc_collect_atom_ids, "collect_atom_ids", f_type_dynamic);
|
|
require_feature_children(f_cvc_collect_atom_ids, f_ag_collect_atom_ids);
|
|
|
|
// TODO only enable this when f_ag_scalable can be turned on for a pre-initialized group
|
|
// require_feature_children(f_cvc_scalable, f_ag_scalable);
|
|
// require_feature_children(f_cvc_scalable_com, f_ag_scalable_com);
|
|
|
|
// check that everything is initialized
|
|
for (i = 0; i < colvardeps::f_cvc_ntot; i++) {
|
|
if (is_not_set(i)) {
|
|
cvm::error("Uninitialized feature " + cvm::to_str(i) + " in " + description);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Initialize feature_states for each instance
|
|
// default as available, not enabled
|
|
// except dynamic features which default as unavailable
|
|
feature_states.reserve(f_cvc_ntot);
|
|
for (i = 0; i < colvardeps::f_cvc_ntot; i++) {
|
|
bool avail = is_dynamic(i) ? false : true;
|
|
feature_states.push_back(feature_state(avail, false));
|
|
}
|
|
|
|
// Features that are implemented by all cvcs by default
|
|
// Each cvc specifies what other features are available
|
|
feature_states[f_cvc_active].available = true;
|
|
feature_states[f_cvc_gradient].available = true;
|
|
feature_states[f_cvc_collect_atom_ids].available = true;
|
|
|
|
// CVCs are enabled from the start - get disabled based on flags
|
|
enable(f_cvc_active);
|
|
|
|
// Explicit gradients are implemented in most CVCs. Exceptions must be specified explicitly.
|
|
enable(f_cvc_explicit_gradient);
|
|
|
|
// Use minimum-image distances by default
|
|
enable(f_cvc_pbc_minimum_image);
|
|
|
|
// Features that are implemented by default if their requirements are
|
|
feature_states[f_cvc_one_site_total_force].available = true;
|
|
|
|
// Features That are implemented only for certain simulation engine configurations
|
|
feature_states[f_cvc_scalable_com].available = (cvm::proxy->scalable_group_coms() == COLVARS_OK);
|
|
feature_states[f_cvc_scalable].available = feature_states[f_cvc_scalable_com].available;
|
|
|
|
return COLVARS_OK;
|
|
}
|
|
|
|
|
|
int colvar::cvc::setup()
|
|
{
|
|
description = "cvc " + name;
|
|
return COLVARS_OK;
|
|
}
|
|
|
|
|
|
colvar::cvc::~cvc()
|
|
{
|
|
free_children_deps();
|
|
remove_all_children();
|
|
for (size_t i = 0; i < atom_groups.size(); i++) {
|
|
if (atom_groups[i] != NULL) delete atom_groups[i];
|
|
}
|
|
}
|
|
|
|
|
|
void colvar::cvc::init_as_distance()
|
|
{
|
|
x.type(colvarvalue::type_scalar);
|
|
enable(f_cvc_lower_boundary);
|
|
lower_boundary.type(colvarvalue::type_scalar);
|
|
lower_boundary.real_value = 0.0;
|
|
register_param("lowerBoundary", reinterpret_cast<void *>(&lower_boundary));
|
|
}
|
|
|
|
|
|
void colvar::cvc::init_as_angle()
|
|
{
|
|
x.type(colvarvalue::type_scalar);
|
|
init_scalar_boundaries(0.0, 180.0);
|
|
}
|
|
|
|
|
|
void colvar::cvc::init_as_periodic_angle()
|
|
{
|
|
x.type(colvarvalue::type_scalar);
|
|
enable(f_cvc_periodic);
|
|
period = 360.0;
|
|
init_scalar_boundaries(-180.0, 180.0);
|
|
}
|
|
|
|
|
|
void colvar::cvc::init_scalar_boundaries(cvm::real lb, cvm::real ub)
|
|
{
|
|
enable(f_cvc_lower_boundary);
|
|
lower_boundary.type(colvarvalue::type_scalar);
|
|
lower_boundary.real_value = lb;
|
|
enable(f_cvc_upper_boundary);
|
|
upper_boundary.type(colvarvalue::type_scalar);
|
|
upper_boundary.real_value = ub;
|
|
register_param("lowerBoundary", reinterpret_cast<void *>(&lower_boundary));
|
|
register_param("upperBoundary", reinterpret_cast<void *>(&upper_boundary));
|
|
}
|
|
|
|
|
|
void colvar::cvc::register_atom_group(cvm::atom_group *ag)
|
|
{
|
|
atom_groups.push_back(ag);
|
|
add_child(ag);
|
|
}
|
|
|
|
|
|
colvarvalue const *colvar::cvc::get_param_grad(std::string const ¶m_name)
|
|
{
|
|
colvarvalue const *ptr =
|
|
reinterpret_cast<colvarvalue const *>(get_param_grad_ptr(param_name));
|
|
return ptr != NULL ? ptr : NULL;
|
|
}
|
|
|
|
|
|
int colvar::cvc::set_param(std::string const ¶m_name,
|
|
void const *new_value)
|
|
{
|
|
if (param_map.count(param_name) > 0) {
|
|
|
|
// TODO When we can use C++11, make this a proper function map
|
|
if (param_name.compare("componentCoeff") == 0) {
|
|
sup_coeff = *(reinterpret_cast<cvm::real const *>(new_value));
|
|
}
|
|
if (param_name.compare("componentExp") == 0) {
|
|
sup_np = *(reinterpret_cast<int const *>(new_value));
|
|
}
|
|
if (is_enabled(f_cvc_periodic)) {
|
|
if (param_name.compare("period") == 0) {
|
|
period = *(reinterpret_cast<cvm::real const *>(new_value));
|
|
}
|
|
if (param_name.compare("wrapAround") == 0) {
|
|
wrap_center = *(reinterpret_cast<cvm::real const *>(new_value));
|
|
}
|
|
}
|
|
}
|
|
|
|
return colvarparams::set_param(param_name, new_value);
|
|
}
|
|
|
|
|
|
void colvar::cvc::read_data()
|
|
{
|
|
size_t ig;
|
|
for (ig = 0; ig < atom_groups.size(); ig++) {
|
|
cvm::atom_group &atoms = *(atom_groups[ig]);
|
|
atoms.reset_atoms_data();
|
|
atoms.read_positions();
|
|
atoms.calc_required_properties();
|
|
// each atom group will take care of its own fitting_group, if defined
|
|
}
|
|
|
|
//// Don't try to get atom velocities, as no back-end currently implements it
|
|
// if (tasks[task_output_velocity] && !tasks[task_fdiff_velocity]) {
|
|
// for (i = 0; i < cvcs.size(); i++) {
|
|
// for (ig = 0; ig < cvcs[i]->atom_groups.size(); ig++) {
|
|
// cvcs[i]->atom_groups[ig]->read_velocities();
|
|
// }
|
|
// }
|
|
// }
|
|
}
|
|
|
|
|
|
std::vector<std::vector<int> > colvar::cvc::get_atom_lists()
|
|
{
|
|
std::vector<std::vector<int> > lists;
|
|
|
|
std::vector<cvm::atom_group *>::iterator agi = atom_groups.begin();
|
|
for ( ; agi != atom_groups.end(); ++agi) {
|
|
(*agi)->create_sorted_ids();
|
|
lists.push_back((*agi)->sorted_ids());
|
|
if ((*agi)->is_enabled(f_ag_fitting_group) && (*agi)->is_enabled(f_ag_fit_gradients)) {
|
|
cvm::atom_group &fg = *((*agi)->fitting_group);
|
|
fg.create_sorted_ids();
|
|
lists.push_back(fg.sorted_ids());
|
|
}
|
|
}
|
|
return lists;
|
|
}
|
|
|
|
|
|
void colvar::cvc::collect_gradients(std::vector<int> const &atom_ids, std::vector<cvm::rvector> &atomic_gradients)
|
|
{
|
|
// Coefficient: d(a * x^n) = a * n * x^(n-1) * dx
|
|
cvm::real coeff = sup_coeff * cvm::real(sup_np) *
|
|
cvm::integer_power(value().real_value, sup_np-1);
|
|
|
|
for (size_t j = 0; j < atom_groups.size(); j++) {
|
|
|
|
cvm::atom_group &ag = *(atom_groups[j]);
|
|
|
|
// If necessary, apply inverse rotation to get atomic
|
|
// gradient in the laboratory frame
|
|
if (ag.is_enabled(f_ag_rotate)) {
|
|
cvm::rotation const rot_inv = ag.rot.inverse();
|
|
|
|
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 * rot_inv.rotate(ag[k].grad);
|
|
}
|
|
|
|
} else {
|
|
|
|
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 (ag.is_enabled(f_ag_fitting_group) && ag.is_enabled(f_ag_fit_gradients)) {
|
|
cvm::atom_group const &fg = *(ag.fitting_group);
|
|
for (size_t k = 0; k < fg.size(); k++) {
|
|
size_t a = std::lower_bound(atom_ids.begin(), atom_ids.end(),
|
|
fg[k].id) - atom_ids.begin();
|
|
// fit gradients are in the unrotated (simulation) frame
|
|
atomic_gradients[a] += coeff * fg.fit_gradients[k];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void colvar::cvc::calc_force_invgrads()
|
|
{
|
|
cvm::error("Error: calculation of inverse gradients is not implemented "
|
|
"for colvar components of type \""+function_type+"\".\n",
|
|
COLVARS_NOT_IMPLEMENTED);
|
|
}
|
|
|
|
|
|
void colvar::cvc::calc_Jacobian_derivative()
|
|
{
|
|
cvm::error("Error: calculation of inverse gradients is not implemented "
|
|
"for colvar components of type \""+function_type+"\".\n",
|
|
COLVARS_NOT_IMPLEMENTED);
|
|
}
|
|
|
|
|
|
void colvar::cvc::calc_fit_gradients()
|
|
{
|
|
for (size_t ig = 0; ig < atom_groups.size(); ig++) {
|
|
atom_groups[ig]->calc_fit_gradients();
|
|
}
|
|
}
|
|
|
|
|
|
void colvar::cvc::debug_gradients()
|
|
{
|
|
// this function should work for any scalar cvc:
|
|
// the only difference will be the name of the atom group (here, "group")
|
|
// NOTE: this assumes that groups for this cvc are non-overlapping,
|
|
// since atom coordinates are modified only within the current group
|
|
|
|
cvm::log("Debugging gradients for " + description);
|
|
|
|
for (size_t ig = 0; ig < atom_groups.size(); ig++) {
|
|
cvm::atom_group *group = atom_groups[ig];
|
|
if (group->b_dummy) continue;
|
|
|
|
cvm::rotation const rot_0 = group->rot;
|
|
cvm::rotation const rot_inv = group->rot.inverse();
|
|
|
|
cvm::real x_0 = x.real_value;
|
|
if ((x.type() == colvarvalue::type_vector) && (x.size() == 1)) x_0 = x[0];
|
|
|
|
// cvm::log("gradients = "+cvm::to_str (gradients)+"\n");
|
|
|
|
cvm::atom_group *group_for_fit = group->fitting_group ? group->fitting_group : group;
|
|
cvm::atom_pos fit_gradient_sum, gradient_sum;
|
|
|
|
// print the values of the fit gradients
|
|
if (group->is_enabled(f_ag_center) || group->is_enabled(f_ag_rotate)) {
|
|
if (group->is_enabled(f_ag_fit_gradients)) {
|
|
size_t j;
|
|
|
|
// fit_gradients are in the simulation frame: we should print them in the rotated frame
|
|
cvm::log("Fit gradients:\n");
|
|
for (j = 0; j < group_for_fit->fit_gradients.size(); j++) {
|
|
cvm::log((group->fitting_group ? std::string("refPosGroup") : group->key) +
|
|
"[" + cvm::to_str(j) + "] = " +
|
|
(group->is_enabled(f_ag_rotate) ?
|
|
cvm::to_str(rot_0.rotate(group_for_fit->fit_gradients[j])) :
|
|
cvm::to_str(group_for_fit->fit_gradients[j])));
|
|
}
|
|
}
|
|
}
|
|
|
|
// debug the gradients
|
|
for (size_t ia = 0; ia < group->size(); ia++) {
|
|
|
|
// tests are best conducted in the unrotated (simulation) frame
|
|
cvm::rvector const atom_grad = (group->is_enabled(f_ag_rotate) ?
|
|
rot_inv.rotate((*group)[ia].grad) :
|
|
(*group)[ia].grad);
|
|
gradient_sum += atom_grad;
|
|
|
|
for (size_t id = 0; id < 3; id++) {
|
|
// (re)read original positions
|
|
group->read_positions();
|
|
// change one coordinate
|
|
(*group)[ia].pos[id] += cvm::debug_gradients_step_size;
|
|
group->calc_required_properties();
|
|
calc_value();
|
|
cvm::real x_1 = x.real_value;
|
|
if ((x.type() == colvarvalue::type_vector) && (x.size() == 1)) x_1 = x[0];
|
|
cvm::log("Atom "+cvm::to_str(ia)+", component "+cvm::to_str(id)+":\n");
|
|
cvm::log("dx(actual) = "+cvm::to_str(x_1 - x_0,
|
|
21, 14)+"\n");
|
|
cvm::real const dx_pred = (group->fit_gradients.size()) ?
|
|
(cvm::debug_gradients_step_size * (atom_grad[id] + group->fit_gradients[ia][id])) :
|
|
(cvm::debug_gradients_step_size * atom_grad[id]);
|
|
cvm::log("dx(interp) = "+cvm::to_str(dx_pred,
|
|
21, 14)+"\n");
|
|
cvm::log("|dx(actual) - dx(interp)|/|dx(actual)| = "+
|
|
cvm::to_str(cvm::fabs(x_1 - x_0 - dx_pred) /
|
|
cvm::fabs(x_1 - x_0), 12, 5)+"\n");
|
|
}
|
|
}
|
|
|
|
if ((group->is_enabled(f_ag_fit_gradients)) && (group->fitting_group != NULL)) {
|
|
cvm::atom_group *ref_group = group->fitting_group;
|
|
group->read_positions();
|
|
group->calc_required_properties();
|
|
|
|
for (size_t ia = 0; ia < ref_group->size(); ia++) {
|
|
|
|
// fit gradients are in the unrotated (simulation) frame
|
|
cvm::rvector const atom_grad = ref_group->fit_gradients[ia];
|
|
fit_gradient_sum += atom_grad;
|
|
|
|
for (size_t id = 0; id < 3; id++) {
|
|
// (re)read original positions
|
|
group->read_positions();
|
|
ref_group->read_positions();
|
|
// change one coordinate
|
|
(*ref_group)[ia].pos[id] += cvm::debug_gradients_step_size;
|
|
group->calc_required_properties();
|
|
calc_value();
|
|
|
|
cvm::real const x_1 = x.real_value;
|
|
cvm::log("refPosGroup atom "+cvm::to_str(ia)+", component "+cvm::to_str (id)+":\n");
|
|
cvm::log("dx(actual) = "+cvm::to_str (x_1 - x_0,
|
|
21, 14)+"\n");
|
|
|
|
cvm::real const dx_pred = cvm::debug_gradients_step_size * atom_grad[id];
|
|
|
|
cvm::log("dx(interp) = "+cvm::to_str (dx_pred,
|
|
21, 14)+"\n");
|
|
cvm::log ("|dx(actual) - dx(interp)|/|dx(actual)| = "+
|
|
cvm::to_str(cvm::fabs (x_1 - x_0 - dx_pred) /
|
|
cvm::fabs (x_1 - x_0),
|
|
12, 5)+
|
|
".\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
cvm::log("Gradient sum: " + cvm::to_str(gradient_sum) +
|
|
" Fit gradient sum: " + cvm::to_str(fit_gradient_sum) +
|
|
" Total " + cvm::to_str(gradient_sum + fit_gradient_sum));
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
cvm::real colvar::cvc::dist2(colvarvalue const &x1,
|
|
colvarvalue const &x2) const
|
|
{
|
|
return x1.dist2(x2);
|
|
}
|
|
|
|
|
|
colvarvalue colvar::cvc::dist2_lgrad(colvarvalue const &x1,
|
|
colvarvalue const &x2) const
|
|
{
|
|
return x1.dist2_grad(x2);
|
|
}
|
|
|
|
|
|
colvarvalue colvar::cvc::dist2_rgrad(colvarvalue const &x1,
|
|
colvarvalue const &x2) const
|
|
{
|
|
return x2.dist2_grad(x1);
|
|
}
|
|
|
|
|
|
void colvar::cvc::wrap(colvarvalue & /* x_unwrapped */) const
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
// Static members
|
|
|
|
std::vector<colvardeps::feature *> colvar::cvc::cvc_features;
|