diff --git a/lib/colvars/colvaratoms.cpp b/lib/colvars/colvaratoms.cpp index 0e67cf3ba..7a3ffa190 100644 --- a/lib/colvars/colvaratoms.cpp +++ b/lib/colvars/colvaratoms.cpp @@ -1,771 +1,781 @@ #include "colvarmodule.h" #include "colvarparse.h" #include "colvaratoms.h" // atom member functions depend tightly on the MD interface, and are // thus defined in colvarproxy_xxx.cpp // atom_group member functions cvm::atom_group::atom_group (std::string const &conf, char const *key, atom_group *ref_pos_group_in) - : b_center (false), b_rotate (false), b_prevent_fitting (false), + : b_center (false), b_rotate (false), b_user_defined_fit (false), ref_pos_group (NULL), // this is always set within parse(), // regardless of ref_pos_group_in noforce (false) { cvm::log ("Defining atom group \""+ std::string (key)+"\".\n"); parse (conf, key, ref_pos_group_in); } void cvm::atom_group::parse (std::string const &conf, char const *key, atom_group *ref_pos_group_in) { std::string group_conf; // save_delimiters is set to false for this call, because "conf" is // not the config string of this group, but of its parent object // (which has already taken care of the delimiters) save_delimiters = false; key_lookup (conf, key, group_conf, dummy_pos); // restoring the normal value, because we do want keywords checked // inside "group_conf" save_delimiters = true; if (group_conf.size() == 0) { cvm::fatal_error ("Error: atom group \""+ std::string (key)+"\" is set, but " "has no definition.\n"); } cvm::increase_depth(); cvm::log ("Initializing atom group \""+std::string (key)+"\".\n"); // whether or not to include messages in the log // colvarparse::Parse_Mode mode = parse_silent; // { // bool b_verbose; // get_keyval (group_conf, "verboseOutput", b_verbose, false, parse_silent); // if (b_verbose) mode = parse_normal; // } colvarparse::Parse_Mode mode = parse_normal; { // get the atoms by numbers std::vector<int> atom_indexes; if (get_keyval (group_conf, "atomNumbers", atom_indexes, atom_indexes, mode)) { if (atom_indexes.size()) { this->reserve (this->size()+atom_indexes.size()); for (size_t i = 0; i < atom_indexes.size(); i++) { this->push_back (cvm::atom (atom_indexes[i])); } } else cvm::fatal_error ("Error: no numbers provided for \"" "atomNumbers\".\n"); } } { std::string range_conf = ""; size_t pos = 0; while (key_lookup (group_conf, "atomNumbersRange", range_conf, pos)) { if (range_conf.size()) { std::istringstream is (range_conf); int initial, final; char dash; if ( (is >> initial) && (initial > 0) && (is >> dash) && (dash == '-') && (is >> final) && (final > 0) ) { for (int anum = initial; anum <= final; anum++) { this->push_back (cvm::atom (anum)); } range_conf = ""; continue; } } cvm::fatal_error ("Error: no valid definition for \"" "atomNumbersRange\", \""+ range_conf+"\".\n"); } } std::vector<std::string> psf_segids; get_keyval (group_conf, "psfSegID", psf_segids, std::vector<std::string> (), mode); for (std::vector<std::string>::iterator psii = psf_segids.begin(); psii < psf_segids.end(); psii++) { if ( (psii->size() == 0) || (psii->size() > 4) ) { cvm::fatal_error ("Error: invalid segmend identifier provided, \""+ (*psii)+"\".\n"); } } { std::string range_conf = ""; size_t pos = 0; size_t range_count = 0; std::vector<std::string>::iterator psii = psf_segids.begin(); while (key_lookup (group_conf, "atomNameResidueRange", range_conf, pos)) { range_count++; if (range_count > psf_segids.size()) { cvm::fatal_error ("Error: more instances of \"atomNameResidueRange\" than " "values of \"psfSegID\".\n"); } std::string const &psf_segid = psf_segids.size() ? *psii : std::string (""); if (range_conf.size()) { std::istringstream is (range_conf); std::string atom_name; int initial, final; char dash; if ( (is >> atom_name) && (atom_name.size()) && (is >> initial) && (initial > 0) && (is >> dash) && (dash == '-') && (is >> final) && (final > 0) ) { for (int resid = initial; resid <= final; resid++) { this->push_back (cvm::atom (resid, atom_name, psf_segid)); } range_conf = ""; } else { cvm::fatal_error ("Error: cannot parse definition for \"" "atomNameResidueRange\", \""+ range_conf+"\".\n"); } } else { cvm::fatal_error ("Error: atomNameResidueRange with empty definition.\n"); } if (psf_segid.size()) psii++; } } { // read the atoms from a file std::string atoms_file_name; if (get_keyval (group_conf, "atomsFile", atoms_file_name, std::string (""), mode)) { std::string atoms_col; if (!get_keyval (group_conf, "atomsCol", atoms_col, std::string (""), mode)) { cvm::fatal_error ("Error: parameter atomsCol is required if atomsFile is set.\n"); } double atoms_col_value; bool const atoms_col_value_defined = get_keyval (group_conf, "atomsColValue", atoms_col_value, 0.0, mode); if (atoms_col_value_defined && (!atoms_col_value)) cvm::fatal_error ("Error: atomsColValue, " "if provided, must be non-zero.\n"); cvm::load_atoms (atoms_file_name.c_str(), *this, atoms_col, atoms_col_value); } } for (std::vector<cvm::atom>::iterator a1 = this->begin(); a1 != this->end(); a1++) { std::vector<cvm::atom>::iterator a2 = a1; ++a2; for ( ; a2 != this->end(); a2++) { if (a1->id == a2->id) { if (cvm::debug()) cvm::log ("Discarding doubly counted atom with number "+ cvm::to_str (a1->id+1)+".\n"); a2 = this->erase (a2); if (a2 == this->end()) break; } } } if (get_keyval (group_conf, "dummyAtom", dummy_atom_pos, cvm::atom_pos(), mode)) { b_dummy = true; this->total_mass = 1.0; } else b_dummy = false; if (b_dummy && (this->size())) cvm::fatal_error ("Error: cannot set up group \""+ std::string (key)+"\" as a dummy atom " "and provide it with atom definitions.\n"); #if (! defined (COLVARS_STANDALONE)) if ( (!b_dummy) && (!cvm::b_analysis) && (!(this->size())) ) { cvm::fatal_error ("Error: no atoms defined for atom group \""+ std::string (key)+"\".\n"); } #endif if (!b_dummy) { this->total_mass = 0.0; for (cvm::atom_iter ai = this->begin(); ai != this->end(); ai++) { this->total_mass += ai->mass; } } if (!b_dummy) get_keyval (group_conf, "disableForces", noforce, false, mode); // FITTING OPTIONS - bool fit_defined_by_user = - ( get_keyval (group_conf, "centerReference", b_center, false, mode) || - get_keyval (group_conf, "rotateReference", b_rotate, false, mode) ); - if ((!b_rotate) && (!b_center) && fit_defined_by_user) - b_prevent_fitting = true; + bool b_defined_center = get_keyval (group_conf, "centerReference", b_center, false, mode); + bool b_defined_rotate = get_keyval (group_conf, "rotateReference", b_rotate, false, mode); + + // this cannot be shortened to one statement because lazy evaluation may prevent one + // function from being called! + b_user_defined_fit = b_defined_center || b_defined_rotate; // if ((b_center || b_rotate) && b_dummy) // cvm::fatal_error ("Error: cannot set \"centerReference\" or " // "\"rotateReference\" when \"dummyAtom\" is defined.\n"); - // instead of this group, define another group (refPositionsGroup) to be the one - // used to fit the coordinates - if (key_lookup (group_conf, "refPositionsGroup")) { - if (ref_pos_group) { - cvm::fatal_error ("Error: the atom group \""+ - std::string (key)+"\" has already a reference group " - "for the rototranslational fit, which was communicated by the " - "colvar component. You should not use refPositionsGroup " - "in this case.\n"); + if (b_center || b_rotate) { + + // instead of this group, define another group (refPositionsGroup) to be the one + // used to fit the coordinates + if (key_lookup (group_conf, "refPositionsGroup")) { + if (ref_pos_group) { + cvm::fatal_error ("Error: the atom group \""+ + std::string (key)+"\" has already a reference group " + "for the rototranslational fit, which was communicated by the " + "colvar component. You should not use refPositionsGroup " + "in this case.\n"); + } + cvm::log ("Within atom group \""+std::string (key)+"\":\n"); + ref_pos_group = new atom_group (group_conf, "refPositionsGroup"); } - cvm::log ("Within atom group \""+std::string (key)+"\":\n"); - ref_pos_group = new atom_group (group_conf, "refPositionsGroup"); - } - atom_group *group_for_fit = ref_pos_group ? ref_pos_group : this; + atom_group *group_for_fit = ref_pos_group ? ref_pos_group : this; - if (get_keyval (group_conf, "refPositions", ref_pos, ref_pos, mode)) { - if (ref_pos.size() != group_for_fit->size()) { - cvm::fatal_error ("Error: the number of reference positions provided ("+ - cvm::to_str (ref_pos.size())+ - ") does not match the number of atoms of group \""+ - std::string (key)+ - "\" ("+cvm::to_str (group_for_fit->size())+").\n"); + if (get_keyval (group_conf, "refPositions", ref_pos, ref_pos, mode)) { + if (ref_pos.size() != group_for_fit->size()) { + cvm::fatal_error ("Error: the number of reference positions provided ("+ + cvm::to_str (ref_pos.size())+ + ") does not match the number of atoms of group \""+ + std::string (key)+ + "\" ("+cvm::to_str (group_for_fit->size())+").\n"); + } } - } - { std::string ref_pos_file; if (get_keyval (group_conf, "refPositionsFile", ref_pos_file, std::string (""), mode)) { if (ref_pos.size()) { cvm::fatal_error ("Error: cannot specify \"refPositionsFile\" and " "\"refPositions\" at the same time.\n"); } std::string ref_pos_col; double ref_pos_col_value; if (get_keyval (group_conf, "refPositionsCol", ref_pos_col, std::string (""), mode)) { // if provided, use PDB column to select coordinates bool found = get_keyval (group_conf, "refPositionsColValue", ref_pos_col_value, 0.0, mode); if (found && !ref_pos_col_value) cvm::fatal_error ("Error: refPositionsColValue, " "if provided, must be non-zero.\n"); } else { // if not, rely on existing atom indices for the group group_for_fit->create_sorted_ids(); } cvm::load_coords (ref_pos_file.c_str(), ref_pos, group_for_fit->sorted_ids, ref_pos_col, ref_pos_col_value); } - } - if (ref_pos.size()) { + if (ref_pos.size()) { + + if (b_rotate) { + if (ref_pos.size() != group_for_fit->size()) + cvm::fatal_error ("Error: the number of reference positions provided ("+ + cvm::to_str (ref_pos.size())+ + ") does not match the number of atoms within \""+ + std::string (key)+ + "\" ("+cvm::to_str (group_for_fit->size())+ + "): to perform a rotational fit, "+ + "these numbers should be equal.\n"); + } - if (b_rotate) { - if (ref_pos.size() != group_for_fit->size()) - cvm::fatal_error ("Error: the number of reference positions provided ("+ - cvm::to_str (ref_pos.size())+ - ") does not match the number of atoms within \""+ - std::string (key)+ - "\" ("+cvm::to_str (group_for_fit->size())+ - "): to perform a rotational fit, "+ - "these numbers should be equal.\n"); - } - // save the center of geometry of ref_pos and then subtract it from - // them; in this way it will be possible to use ref_pos also for - // the rotational fit - ref_pos_cog = cvm::atom_pos (0.0, 0.0, 0.0); - std::vector<cvm::atom_pos>::iterator pi = ref_pos.begin(); - for ( ; pi != ref_pos.end(); pi++) { - ref_pos_cog += *pi; - } - ref_pos_cog /= (cvm::real) ref_pos.size(); - for (std::vector<cvm::atom_pos>::iterator pi = ref_pos.begin(); - pi != ref_pos.end(); pi++) { - (*pi) -= ref_pos_cog; - } + // save the center of geometry of ref_pos and subtract it + center_ref_pos(); - } else { + } else { #if (! defined (COLVARS_STANDALONE)) - cvm::fatal_error ("Error: no reference positions provided.\n"); + cvm::fatal_error ("Error: no reference positions provided.\n"); #endif - } + } - if (b_rotate && !noforce) { - cvm::log ("Warning: atom group \""+std::string (key)+ - "\" will be fitted automatically onto a fixed orientation: " - "in few cases, torques applied on this group " - "may make the simulation unstable. " - "If this happens, set \"disableForces\" to yes " - "for this group.\n"); - // initialize rot member data - rot.request_group1_gradients (this->size()); + if (b_rotate && !noforce) { + cvm::log ("Warning: atom group \""+std::string (key)+ + "\" will be fitted automatically onto a fixed orientation: " + "in few cases, torques applied on this group " + "may make the simulation unstable. " + "If this happens, set \"disableForces\" to yes " + "for this group.\n"); + // initialize rot member data + rot.request_group1_gradients (this->size()); + } } if (cvm::debug()) cvm::log ("Done initializing atom group with name \""+ std::string (key)+"\".\n"); this->check_keywords (group_conf, key); cvm::log ("Atom group \""+std::string (key)+"\" defined, "+ cvm::to_str (this->size())+" initialized: total mass = "+ cvm::to_str (this->total_mass)+".\n"); cvm::decrease_depth(); } cvm::atom_group::atom_group (std::vector<cvm::atom> const &atoms) : b_dummy (false), b_center (false), b_rotate (false), ref_pos_group (NULL), noforce (false) { this->reserve (atoms.size()); for (size_t i = 0; i < atoms.size(); i++) { this->push_back (atoms[i]); } total_mass = 0.0; for (cvm::atom_iter ai = this->begin(); ai != this->end(); ai++) { total_mass += ai->mass; } } cvm::atom_group::atom_group() : b_dummy (false), b_center (false), b_rotate (false), ref_pos_group (NULL), noforce (false) { total_mass = 0.0; } cvm::atom_group::~atom_group() { if (ref_pos_group) { delete ref_pos_group; ref_pos_group = NULL; } } void cvm::atom_group::add_atom (cvm::atom const &a) { if (b_dummy) { cvm::fatal_error ("Error: cannot add atoms to a dummy group.\n"); } else { this->push_back (a); total_mass += a.mass; } } void cvm::atom_group::create_sorted_ids (void) { // Only do the work if the vector is not yet populated if (sorted_ids.size()) return; std::list<int> temp_id_list; for (cvm::atom_iter ai = this->begin(); ai != this->end(); ai++) { temp_id_list.push_back (ai->id); } temp_id_list.sort(); temp_id_list.unique(); if (temp_id_list.size() != this->size()) { cvm::fatal_error ("Error: duplicate atom IDs in atom group? (found " + cvm::to_str(temp_id_list.size()) + " unique atom IDs instead of" + cvm::to_str(this->size()) + ").\n"); } sorted_ids = std::vector<int> (temp_id_list.begin(), temp_id_list.end()); return; } +void cvm::atom_group::center_ref_pos() +{ + // save the center of geometry of ref_pos and then subtract it from + // them; in this way it will be possible to use ref_pos also for + // the rotational fit + // This is called either by atom_group::parse or by CVCs that set + // reference positions (eg. RMSD, eigenvector) + ref_pos_cog = cvm::atom_pos (0.0, 0.0, 0.0); + std::vector<cvm::atom_pos>::iterator pi = ref_pos.begin(); + for ( ; pi != ref_pos.end(); pi++) { + ref_pos_cog += *pi; + } + ref_pos_cog /= (cvm::real) ref_pos.size(); + for (std::vector<cvm::atom_pos>::iterator pi = ref_pos.begin(); + pi != ref_pos.end(); pi++) { + (*pi) -= ref_pos_cog; + } +} void cvm::atom_group::read_positions() { if (b_dummy) return; #if (! defined (COLVARS_STANDALONE)) if (!this->size()) cvm::fatal_error ("Error: no atoms defined in the requested group.\n"); #endif for (cvm::atom_iter ai = this->begin(); ai != this->end(); ai++) { ai->read_position(); } if (ref_pos_group) ref_pos_group->read_positions(); atom_group *fit_group = ref_pos_group ? ref_pos_group : this; if (b_center) { // store aside the current center of geometry (all positions will be // set to the closest images to the first one) and then center on // the origin cvm::atom_pos const cog = fit_group->center_of_geometry(); for (cvm::atom_iter ai = this->begin(); ai != this->end(); ai++) { ai->pos -= cog; } } if (b_rotate) { // rotate the group (around the center of geometry if b_center is // true, around the origin otherwise); store the rotation, in // order to bring back the forces to the original frame before // applying them rot.calc_optimal_rotation (fit_group->positions(), ref_pos); for (cvm::atom_iter ai = this->begin(); ai != this->end(); ai++) { ai->pos = rot.rotate (ai->pos); } } if (b_center) { // use the center of geometry of ref_pos for (cvm::atom_iter ai = this->begin(); ai != this->end(); ai++) { ai->pos += ref_pos_cog; } } } void cvm::atom_group::apply_translation (cvm::rvector const &t) { if (b_dummy) return; for (cvm::atom_iter ai = this->begin(); ai != this->end(); ai++) { ai->pos += t; } } void cvm::atom_group::apply_rotation (cvm::rotation const &rot) { if (b_dummy) return; for (cvm::atom_iter ai = this->begin(); ai != this->end(); ai++) { ai->pos = rot.rotate (ai->pos); } } void cvm::atom_group::read_velocities() { if (b_dummy) return; if (b_rotate) { for (cvm::atom_iter ai = this->begin(); ai != this->end(); ai++) { ai->read_velocity(); ai->vel = rot.rotate (ai->vel); } } else { for (cvm::atom_iter ai = this->begin(); ai != this->end(); ai++) { ai->read_velocity(); } } } void cvm::atom_group::read_system_forces() { if (b_dummy) return; if (b_rotate) { for (cvm::atom_iter ai = this->begin(); ai != this->end(); ai++) { ai->read_system_force(); ai->system_force = rot.rotate (ai->system_force); } } else { for (cvm::atom_iter ai = this->begin(); ai != this->end(); ai++) { ai->read_system_force(); } } } /* This is deprecated. cvm::atom_pos cvm::atom_group::center_of_geometry (cvm::atom_pos const &ref_pos) { if (b_dummy) { cvm::select_closest_image (dummy_atom_pos, ref_pos); return dummy_atom_pos; } cvm::atom_pos cog (0.0, 0.0, 0.0); for (cvm::atom_iter ai = this->begin(); ai != this->end(); ai++) { cvm::select_closest_image (ai->pos, ref_pos); cog += ai->pos; } cog /= this->size(); return cog; } */ cvm::atom_pos cvm::atom_group::center_of_geometry() const { if (b_dummy) return dummy_atom_pos; cvm::atom_pos cog (0.0, 0.0, 0.0); for (cvm::atom_const_iter ai = this->begin(); ai != this->end(); ai++) { cog += ai->pos; } cog /= this->size(); return cog; } /* This is deprecated. cvm::atom_pos cvm::atom_group::center_of_mass (cvm::atom_pos const &ref_pos) { if (b_dummy) { cvm::select_closest_image (dummy_atom_pos, ref_pos); return dummy_atom_pos; } cvm::atom_pos com (0.0, 0.0, 0.0); for (cvm::atom_iter ai = this->begin(); ai != this->end(); ai++) { cvm::select_closest_image (ai->pos, ref_pos); com += ai->mass * ai->pos; } com /= this->total_mass; return com; }*/ cvm::atom_pos cvm::atom_group::center_of_mass() const { if (b_dummy) return dummy_atom_pos; cvm::atom_pos com (0.0, 0.0, 0.0); for (cvm::atom_const_iter ai = this->begin(); ai != this->end(); ai++) { com += ai->mass * ai->pos; } com /= this->total_mass; return com; } void cvm::atom_group::set_weighted_gradient (cvm::rvector const &grad) { if (b_dummy) return; for (cvm::atom_iter ai = this->begin(); ai != this->end(); ai++) { ai->grad = (ai->mass/this->total_mass) * grad; } } std::vector<cvm::atom_pos> cvm::atom_group::positions() const { if (b_dummy) cvm::fatal_error ("Error: positions are not available " "from a dummy atom group.\n"); std::vector<cvm::atom_pos> x (this->size(), 0.0); cvm::atom_const_iter ai = this->begin(); std::vector<cvm::atom_pos>::iterator xi = x.begin(); for ( ; ai != this->end(); xi++, ai++) { *xi = ai->pos; } return x; } std::vector<cvm::atom_pos> cvm::atom_group::positions_shifted (cvm::rvector const &shift) const { if (b_dummy) cvm::fatal_error ("Error: positions are not available " "from a dummy atom group.\n"); std::vector<cvm::atom_pos> x (this->size(), 0.0); cvm::atom_const_iter ai = this->begin(); std::vector<cvm::atom_pos>::iterator xi = x.begin(); for ( ; ai != this->end(); xi++, ai++) { *xi = (ai->pos + shift); } return x; } std::vector<cvm::rvector> cvm::atom_group::velocities() const { if (b_dummy) cvm::fatal_error ("Error: velocities are not available " "from a dummy atom group.\n"); std::vector<cvm::rvector> v (this->size(), 0.0); cvm::atom_const_iter ai = this->begin(); std::vector<cvm::atom_pos>::iterator vi = v.begin(); for ( ; ai != this->end(); vi++, ai++) { *vi = ai->vel; } return v; } std::vector<cvm::rvector> cvm::atom_group::system_forces() const { if (b_dummy) cvm::fatal_error ("Error: system forces are not available " "from a dummy atom group.\n"); std::vector<cvm::rvector> f (this->size(), 0.0); cvm::atom_const_iter ai = this->begin(); std::vector<cvm::atom_pos>::iterator fi = f.begin(); for ( ; ai != this->end(); fi++, ai++) { *fi = ai->system_force; } return f; } cvm::rvector cvm::atom_group::system_force() const { if (b_dummy) cvm::fatal_error ("Error: system forces are not available " "from a dummy atom group.\n"); cvm::rvector f (0.0); for (cvm::atom_const_iter ai = this->begin(); ai != this->end(); ai++) { f += ai->system_force; } return f; } void cvm::atom_group::apply_colvar_force (cvm::real const &force) { if (b_dummy) return; if (noforce) cvm::fatal_error ("Error: sending a force to a group that has " "\"disableForces\" defined.\n"); if (b_rotate) { // get the forces back from the rotated frame cvm::rotation const rot_inv = rot.inverse(); for (cvm::atom_iter ai = this->begin(); ai != this->end(); ai++) { ai->apply_force (rot_inv.rotate (force * ai->grad)); } } else { // no need to manipulate gradients, they are still in the original // frame for (cvm::atom_iter ai = this->begin(); ai != this->end(); ai++) { ai->apply_force (force * ai->grad); } } } void cvm::atom_group::apply_force (cvm::rvector const &force) { if (b_dummy) return; if (noforce) cvm::fatal_error ("Error: sending a force to a group that has " "\"disableForces\" defined.\n"); if (b_rotate) { cvm::rotation const rot_inv = rot.inverse(); for (cvm::atom_iter ai = this->begin(); ai != this->end(); ai++) { ai->apply_force (rot_inv.rotate ((ai->mass/this->total_mass) * force)); } } else { for (cvm::atom_iter ai = this->begin(); ai != this->end(); ai++) { ai->apply_force ((ai->mass/this->total_mass) * force); } } } void cvm::atom_group::apply_forces (std::vector<cvm::rvector> const &forces) { if (b_dummy) return; if (noforce) cvm::fatal_error ("Error: sending a force to a group that has " "\"disableForces\" defined.\n"); if (forces.size() != this->size()) { cvm::fatal_error ("Error: trying to apply an array of forces to an atom " "group which does not have the same length.\n"); } if (b_rotate) { cvm::rotation const rot_inv = rot.inverse(); cvm::atom_iter ai = this->begin(); std::vector<cvm::rvector>::const_iterator fi = forces.begin(); for ( ; ai != this->end(); fi++, ai++) { ai->apply_force (rot_inv.rotate (*fi)); } } else { cvm::atom_iter ai = this->begin(); std::vector<cvm::rvector>::const_iterator fi = forces.begin(); for ( ; ai != this->end(); fi++, ai++) { ai->apply_force (*fi); } } } diff --git a/lib/colvars/colvaratoms.h b/lib/colvars/colvaratoms.h index 7f1f00295..6ffda6db5 100644 --- a/lib/colvars/colvaratoms.h +++ b/lib/colvars/colvaratoms.h @@ -1,318 +1,322 @@ // -*- c++ -*- #ifndef COLVARATOMS_H #define COLVARATOMS_H #include "colvarmodule.h" #include "colvarparse.h" /// \brief Stores numeric id, mass and all mutable data for an atom, /// mostly used by a \link cvc \endlink /// /// This class may be used (although not necessarily) to keep atomic /// data (id, mass, position and collective variable derivatives) /// altogether. There may be multiple instances with identical /// numeric id, all acting independently: forces communicated through /// these instances will be summed together. /// /// Read/write operations depend on the underlying code: hence, some /// member functions are defined in colvarproxy_xxx.h. class colvarmodule::atom { protected: /// \brief Index in the list of atoms involved by the colvars (\b /// NOT in the global topology!) size_t index; public: /// Internal identifier (zero-based) int id; /// Mass cvm::real mass; /// \brief Current position (copied from the program, can be /// manipulated) cvm::atom_pos pos; /// \brief Current velocity (copied from the program, can be /// manipulated) cvm::rvector vel; /// \brief System force at the previous step (copied from the /// program, can be manipulated) cvm::rvector system_force; /// \brief Gradient of a scalar collective variable with respect /// to this atom /// /// This can only handle a scalar collective variable (i.e. when /// the \link colvarvalue::real_value \endlink member is used /// from the \link colvarvalue \endlink class), which is also the /// most frequent case. For more complex types of \link /// colvarvalue \endlink objects, atomic gradients should be /// defined within the specific \link cvc \endlink /// implementation cvm::rvector grad; /// \brief Default constructor, setting id to a non-valid value inline atom() {} /// \brief Initialize an atom for collective variable calculation /// and get its internal identifier \param atom_number Atom index in /// the system topology (starting from 1) atom (int const &atom_number); /// \brief Initialize an atom for collective variable calculation /// and get its internal identifier \param residue Residue number /// \param atom_name Name of the atom in the residue \param /// segment_id For PSF topologies, the segment identifier; for other /// type of topologies, may not be required atom (cvm::residue_id const &residue, std::string const &atom_name, std::string const &segment_id = std::string ("")); /// Copy constructor atom (atom const &a); /// Destructor ~atom(); /// Set non-constant data (everything except id and mass) to zero inline void reset_data() { pos = atom_pos (0.0); vel = grad = system_force = rvector (0.0); } /// Get the current position void read_position(); /// Get the current velocity void read_velocity(); /// Get the system force void read_system_force(); /// \brief Apply a force to the atom /// /// The force will be used later by the MD integrator, the /// collective variables module does not integrate equations of /// motion. Multiple calls to this function by either the same /// \link atom \endlink object or different objects with identical /// \link id \endlink, will all add to the existing MD force. void apply_force (cvm::rvector const &new_force); }; /// \brief Group of \link atom \endlink objects, mostly used by a /// \link cvc \endlink /// /// This class inherits from \link colvarparse \endlink and from /// std::vector<colvarmodule::atom>, and hence all functions and /// operators (including the bracket operator, group[i]) can be used /// on an \link atom_group \endlink object. It can be initialized as /// a vector, or by parsing a keyword in the configuration. class colvarmodule::atom_group : public std::vector<cvm::atom>, public colvarparse { public: // Note: all members here are kept public, to make possible to any // object accessing and manipulating them /// \brief If this option is on, this group merely acts as a wrapper /// for a fixed position; any calls to atoms within or to /// functions that return disaggregated data will fail bool b_dummy; /// \brief dummy atom position cvm::atom_pos dummy_atom_pos; /// Sorted list of zero-based (internal) atom ids /// (populated on-demand by create_sorted_ids) std::vector<int> sorted_ids; /// Allocates and populates the sorted list of atom ids void create_sorted_ids (void); /// \brief Before calculating colvars, move the group to overlap the /// center of mass of reference coordinates bool b_center; /// \brief Right after updating atom coordinates (and after /// centering coordinates, if b_center is true), rotate the group to /// overlap the reference coordinates. You should not manipulate /// atoms individually if you turn on this flag. /// /// Note: gradients will be calculated in the rotated frame: when /// forces will be applied, they will rotated back to the original /// frame bool b_rotate; /// Rotation between the group and its reference coordinates cvm::rotation rot; - /// \brief Indicates that b_center and b_rotate are not false by default, - /// but following the user's choice instead: cvc's will not override - /// the value of b_center or b_rotate - bool b_prevent_fitting; + /// \brief Indicates that the user has specified centerReference or + /// rotateReference (and the related reference coordinates): + /// cvc's (eg rmsd, eigenvector) should not override these settings + bool b_user_defined_fit; /// \brief use these reference coordinates, for b_center, b_rotate, or to compute /// certain colvar components (orientation, rmsd, etc) std::vector<cvm::atom_pos> ref_pos; /// \brief Center of geometry of the reference coordinates; regardless /// of whether b_center is true, ref_pos is centered to zero at /// initialization, and ref_pos_cog serves to center the positions cvm::atom_pos ref_pos_cog; /// \brief In case b_center or b_rotate is true, fit this group to /// the reference positions (default: the parent group itself) atom_group *ref_pos_group; /// Total mass of the atom group cvm::real total_mass; /// \brief Don't apply any force on this group (use its coordinates /// only to calculate a colvar) bool noforce; /// \brief Initialize the group by looking up its configuration /// string in conf and parsing it; this is actually done by parse(), /// which is a member function so that a group can be initialized /// also after construction atom_group (std::string const &conf, char const *key, atom_group *ref_pos_group = NULL); /// \brief Initialize the group by looking up its configuration /// string in conf and parsing it void parse (std::string const &conf, char const *key, atom_group *ref_pos_group = NULL); /// \brief Initialize the group after a temporary vector of atoms atom_group (std::vector<cvm::atom> const &atoms); /// \brief Add an atom to this group void add_atom (cvm::atom const &a); /// \brief Default constructor atom_group(); /// \brief Destructor ~atom_group(); /// \brief Get the current positions; if b_center or b_rotate are /// true, center and/or rotate the coordinates right after reading /// them void read_positions(); + /// \brief Save center of geometry fo ref positions, + /// then subtract it + void center_ref_pos(); + /// \brief Move all positions void apply_translation (cvm::rvector const &t); /// \brief Rotate all positions void apply_rotation (cvm::rotation const &q); /// \brief Get the current velocities; this must be called always /// *after* read_positions(); if b_rotate is defined, the same /// rotation applied to the coordinates will be used void read_velocities(); /// \brief Get the current system_forces; this must be called always /// *after* read_positions(); if b_rotate is defined, the same /// rotation applied to the coordinates will be used void read_system_forces(); /// Call reset_data() for each atom inline void reset_atoms_data() { for (cvm::atom_iter ai = this->begin(); ai != this->end(); ai++) ai->reset_data(); } /// \brief Return a copy of the current atom positions std::vector<cvm::atom_pos> positions() const; /// \brief Return a copy of the current atom positions, shifted by a constant vector std::vector<cvm::atom_pos> positions_shifted (cvm::rvector const &shift) const; /// \brief Return the center of geometry of the positions \param ref_pos /// Use the closest periodic images to this position cvm::atom_pos center_of_geometry (cvm::atom_pos const &ref_pos); /// \brief Return the center of geometry of the positions, assuming /// that coordinates are already pbc-wrapped cvm::atom_pos center_of_geometry() const; /// \brief Return the center of mass of the positions \param ref_pos /// Use the closest periodic images to this position cvm::atom_pos center_of_mass (cvm::atom_pos const &ref_pos); /// \brief Return the center of mass of the positions, assuming that /// coordinates are already pbc-wrapped cvm::atom_pos center_of_mass() const; /// \brief Store atom positions from the previous step std::vector<cvm::atom_pos> old_pos; /// \brief Return a copy of the current atom velocities std::vector<cvm::rvector> velocities() const; /// \brief Return a copy of the system forces std::vector<cvm::rvector> system_forces() const; /// \brief Return a copy of the aggregated total force on the group cvm::rvector system_force() const; /// \brief Shorthand: save the specified gradient on each atom, /// weighting with the atom mass (mostly used in combination with /// \link center_of_mass() \endlink) void set_weighted_gradient (cvm::rvector const &grad); /// \brief Used by a (scalar) colvar to apply its force on its \link /// atom_group \endlink members /// /// The (scalar) force is multiplied by the colvar gradient for each /// atom; this should be used when a colvar with scalar \link /// colvarvalue \endlink type is used (this is the most frequent /// case: for colvars with a non-scalar type, the most convenient /// solution is to sum together the Cartesian forces from all the /// colvar components, and use apply_force() or apply_forces()). If /// the group is being rotated to a reference frame (e.g. to express /// the colvar independently from the solute rotation), the /// gradients are temporarily to the original frame. void apply_colvar_force (cvm::real const &force); /// \brief Apply a force "to the center of mass", i.e. the force is /// distributed on each atom according to its mass /// /// If the group is being rotated to a reference frame (e.g. to /// express the colvar independently from the solute rotation), the /// force is rotated back to the original frame. Colvar gradients /// are not used, either because they were not defined (e.g because /// the colvar has not a scalar value) or the biases require to /// micromanage the force. void apply_force (cvm::rvector const &force); /// \brief Apply an array of forces directly on the individual /// atoms; the length of the specified vector must be the same of /// this \link atom_group \endlink. /// /// If the group is being rotated to a reference frame (e.g. to /// express the colvar independently from the solute rotation), the /// forces are rotated back to the original frame. Colvar gradients /// are not used, either because they were not defined (e.g because /// the colvar has not a scalar value) or the biases require to /// micromanage the forces. void apply_forces (std::vector<cvm::rvector> const &forces); }; #endif diff --git a/lib/colvars/colvarcomp_distances.cpp b/lib/colvars/colvarcomp_distances.cpp index 82b7b8aa6..bc77c5a4b 100644 --- a/lib/colvars/colvarcomp_distances.cpp +++ b/lib/colvars/colvarcomp_distances.cpp @@ -1,1189 +1,1198 @@ #include <cmath> #include "colvarmodule.h" #include "colvarvalue.h" #include "colvarparse.h" #include "colvar.h" #include "colvarcomp.h" /// \file cvc_distance.cpp \brief Collective variables /// determining various type of distances between two groups // "twogroup" flag defaults to true; set to false by selfCoordNum // (only distance-derived component based on only one group) colvar::distance::distance (std::string const &conf, bool twogroups) : cvc (conf) { function_type = "distance"; b_inverse_gradients = true; b_Jacobian_derivative = true; if (get_keyval (conf, "forceNoPBC", b_no_PBC, false)) { cvm::log ("Computing distance using absolute positions (not minimal-image)"); } if (twogroups && get_keyval (conf, "oneSiteSystemForce", b_1site_force, false)) { cvm::log ("Computing system force on group 1 only"); } parse_group (conf, "group1", group1); atom_groups.push_back (&group1); if (twogroups) { parse_group (conf, "group2", group2); atom_groups.push_back (&group2); } x.type (colvarvalue::type_scalar); } colvar::distance::distance() : cvc () { function_type = "distance"; b_inverse_gradients = true; b_Jacobian_derivative = true; b_1site_force = false; x.type (colvarvalue::type_scalar); } void colvar::distance::calc_value() { group1.reset_atoms_data(); group2.reset_atoms_data(); group1.read_positions(); group2.read_positions(); if (b_no_PBC) { dist_v = group2.center_of_mass() - group1.center_of_mass(); } else { dist_v = cvm::position_distance (group1.center_of_mass(), group2.center_of_mass()); } x.real_value = dist_v.norm(); } void colvar::distance::calc_gradients() { cvm::rvector const u = dist_v.unit(); group1.set_weighted_gradient (-1.0 * u); group2.set_weighted_gradient ( u); } void colvar::distance::calc_force_invgrads() { group1.read_system_forces(); if ( b_1site_force ) { ft.real_value = -1.0 * (group1.system_force() * dist_v.unit()); } else { group2.read_system_forces(); ft.real_value = 0.5 * ((group2.system_force() - group1.system_force()) * dist_v.unit()); } } void colvar::distance::calc_Jacobian_derivative() { jd.real_value = x.real_value ? (2.0 / x.real_value) : 0.0; } void colvar::distance::apply_force (colvarvalue const &force) { if (!group1.noforce) group1.apply_colvar_force (force.real_value); if (!group2.noforce) group2.apply_colvar_force (force.real_value); } colvar::distance_vec::distance_vec (std::string const &conf) : distance (conf) { function_type = "distance_vec"; x.type (colvarvalue::type_vector); } colvar::distance_vec::distance_vec() : distance() { function_type = "distance_vec"; x.type (colvarvalue::type_vector); } void colvar::distance_vec::calc_value() { group1.reset_atoms_data(); group2.reset_atoms_data(); group1.read_positions(); group2.read_positions(); if (b_no_PBC) { x.rvector_value = group2.center_of_mass() - group1.center_of_mass(); } else { x.rvector_value = cvm::position_distance (group1.center_of_mass(), group2.center_of_mass()); } } void colvar::distance_vec::calc_gradients() { // gradients are not stored: a 3x3 matrix for each atom would be // needed to store just the identity matrix } void colvar::distance_vec::apply_force (colvarvalue const &force) { if (!group1.noforce) group1.apply_force (-1.0 * force.rvector_value); if (!group2.noforce) group2.apply_force ( force.rvector_value); } colvar::distance_z::distance_z (std::string const &conf) : cvc (conf) { function_type = "distance_z"; b_inverse_gradients = true; b_Jacobian_derivative = true; x.type (colvarvalue::type_scalar); // TODO detect PBC from MD engine (in simple cases) // and then update period in real time if (period != 0.0) b_periodic = true; if ((wrap_center != 0.0) && (period == 0.0)) { cvm::fatal_error ("Error: wrapAround was defined in a distanceZ component," " but its period has not been set.\n"); } parse_group (conf, "main", main); parse_group (conf, "ref", ref1); atom_groups.push_back (&main); atom_groups.push_back (&ref1); // this group is optional parse_group (conf, "ref2", ref2, true); if (ref2.size()) { atom_groups.push_back (&ref2); cvm::log ("Using axis joining the centers of mass of groups \"ref\" and \"ref2\""); fixed_axis = false; if (key_lookup (conf, "axis")) cvm::log ("Warning: explicit axis definition will be ignored!"); } else { if (get_keyval (conf, "axis", axis, cvm::rvector (0.0, 0.0, 1.0))) { if (axis.norm2() == 0.0) cvm::fatal_error ("Axis vector is zero!"); if (axis.norm2() != 1.0) { axis = axis.unit(); cvm::log ("The normalized axis is: "+cvm::to_str (axis)+".\n"); } } fixed_axis = true; } if (get_keyval (conf, "forceNoPBC", b_no_PBC, false)) { cvm::log ("Computing distance using absolute positions (not minimal-image)"); } if (get_keyval (conf, "oneSiteSystemForce", b_1site_force, false)) { cvm::log ("Computing system force on group \"main\" only"); } } colvar::distance_z::distance_z() { function_type = "distance_z"; b_inverse_gradients = true; b_Jacobian_derivative = true; x.type (colvarvalue::type_scalar); } void colvar::distance_z::calc_value() { main.reset_atoms_data(); ref1.reset_atoms_data(); main.read_positions(); ref1.read_positions(); if (fixed_axis) { if (b_no_PBC) { dist_v = main.center_of_mass() - ref1.center_of_mass(); } else { dist_v = cvm::position_distance (ref1.center_of_mass(), main.center_of_mass()); } } else { ref2.reset_atoms_data(); ref2.read_positions(); if (b_no_PBC) { dist_v = main.center_of_mass() - (0.5 * (ref1.center_of_mass() + ref2.center_of_mass())); axis = ref2.center_of_mass() - ref1.center_of_mass(); } else { dist_v = cvm::position_distance (0.5 * (ref1.center_of_mass() + ref2.center_of_mass()), main.center_of_mass()); axis = cvm::position_distance (ref1.center_of_mass(), ref2.center_of_mass()); } axis_norm = axis.norm(); axis = axis.unit(); } x.real_value = axis * dist_v; this->wrap (x); } void colvar::distance_z::calc_gradients() { main.set_weighted_gradient ( axis ); if (fixed_axis) { ref1.set_weighted_gradient (-1.0 * axis); } else { if (b_no_PBC) { ref1.set_weighted_gradient ( 1.0 / axis_norm * (main.center_of_mass() - ref2.center_of_mass() - x.real_value * axis )); ref2.set_weighted_gradient ( 1.0 / axis_norm * (ref1.center_of_mass() - main.center_of_mass() + x.real_value * axis )); } else { ref1.set_weighted_gradient ( 1.0 / axis_norm * ( cvm::position_distance (ref2.center_of_mass(), main.center_of_mass()) - x.real_value * axis )); ref2.set_weighted_gradient ( 1.0 / axis_norm * ( cvm::position_distance (main.center_of_mass(), ref1.center_of_mass()) + x.real_value * axis )); } } } void colvar::distance_z::calc_force_invgrads() { main.read_system_forces(); if (fixed_axis && !b_1site_force) { ref1.read_system_forces(); ft.real_value = 0.5 * ((main.system_force() - ref1.system_force()) * axis); } else { ft.real_value = main.system_force() * axis; } } void colvar::distance_z::calc_Jacobian_derivative() { jd.real_value = 0.0; } void colvar::distance_z::apply_force (colvarvalue const &force) { if (!ref1.noforce) ref1.apply_colvar_force (force.real_value); if (ref2.size() && !ref2.noforce) ref2.apply_colvar_force (force.real_value); if (!main.noforce) main.apply_colvar_force (force.real_value); } colvar::distance_xy::distance_xy (std::string const &conf) : distance_z (conf) { function_type = "distance_xy"; b_inverse_gradients = true; b_Jacobian_derivative = true; x.type (colvarvalue::type_scalar); } colvar::distance_xy::distance_xy() : distance_z() { function_type = "distance_xy"; b_inverse_gradients = true; b_Jacobian_derivative = true; x.type (colvarvalue::type_scalar); } void colvar::distance_xy::calc_value() { ref1.reset_atoms_data(); main.reset_atoms_data(); ref1.read_positions(); main.read_positions(); if (b_no_PBC) { dist_v = main.center_of_mass() - ref1.center_of_mass(); } else { dist_v = cvm::position_distance (ref1.center_of_mass(), main.center_of_mass()); } if (!fixed_axis) { ref2.reset_atoms_data(); ref2.read_positions(); if (b_no_PBC) { v12 = ref2.center_of_mass() - ref1.center_of_mass(); } else { v12 = cvm::position_distance (ref1.center_of_mass(), ref2.center_of_mass()); } axis_norm = v12.norm(); axis = v12.unit(); } dist_v_ortho = dist_v - (dist_v * axis) * axis; x.real_value = dist_v_ortho.norm(); } void colvar::distance_xy::calc_gradients() { // Intermediate quantity (r_P3 / r_12 where P is the projection // of 3 (main) on the plane orthogonal to 12, containing 1 (ref1)) cvm::real A; cvm::real x_inv; if (x.real_value == 0.0) return; x_inv = 1.0 / x.real_value; if (fixed_axis) { ref1.set_weighted_gradient (-1.0 * x_inv * dist_v_ortho); main.set_weighted_gradient ( x_inv * dist_v_ortho); } else { if (b_no_PBC) { v13 = main.center_of_mass() - ref1.center_of_mass(); } else { v13 = cvm::position_distance (ref1.center_of_mass(), main.center_of_mass()); } A = (dist_v * axis) / axis_norm; ref1.set_weighted_gradient ( (A - 1.0) * x_inv * dist_v_ortho); ref2.set_weighted_gradient ( -A * x_inv * dist_v_ortho); main.set_weighted_gradient ( 1.0 * x_inv * dist_v_ortho); } } void colvar::distance_xy::calc_force_invgrads() { main.read_system_forces(); if (fixed_axis && !b_1site_force) { ref1.read_system_forces(); ft.real_value = 0.5 / x.real_value * ((main.system_force() - ref1.system_force()) * dist_v_ortho); } else { ft.real_value = 1.0 / x.real_value * main.system_force() * dist_v_ortho; } } void colvar::distance_xy::calc_Jacobian_derivative() { jd.real_value = x.real_value ? (1.0 / x.real_value) : 0.0; } void colvar::distance_xy::apply_force (colvarvalue const &force) { if (!ref1.noforce) ref1.apply_colvar_force (force.real_value); if (ref2.size() && !ref2.noforce) ref2.apply_colvar_force (force.real_value); if (!main.noforce) main.apply_colvar_force (force.real_value); } colvar::distance_dir::distance_dir (std::string const &conf) : distance (conf) { function_type = "distance_dir"; x.type (colvarvalue::type_unitvector); } colvar::distance_dir::distance_dir() : distance() { function_type = "distance_dir"; x.type (colvarvalue::type_unitvector); } void colvar::distance_dir::calc_value() { group1.reset_atoms_data(); group2.reset_atoms_data(); group1.read_positions(); group2.read_positions(); if (b_no_PBC) { dist_v = group2.center_of_mass() - group1.center_of_mass(); } else { dist_v = cvm::position_distance (group1.center_of_mass(), group2.center_of_mass()); } x.rvector_value = dist_v.unit(); } void colvar::distance_dir::calc_gradients() { // gradients are computed on the fly within apply_force() // Note: could be a problem if a future bias relies on gradient // calculations... } void colvar::distance_dir::apply_force (colvarvalue const &force) { // remove the radial force component cvm::real const iprod = force.rvector_value * x.rvector_value; cvm::rvector const force_tang = force.rvector_value - iprod * x.rvector_value; if (!group1.noforce) group1.apply_force (-1.0 * force_tang); if (!group2.noforce) group2.apply_force ( force_tang); } colvar::distance_inv::distance_inv (std::string const &conf) : distance (conf) { function_type = "distance_inv"; get_keyval (conf, "exponent", exponent, 6); if (exponent%2) { cvm::fatal_error ("Error: odd exponent provided, can only use even ones.\n"); } if (exponent <= 0) { cvm::fatal_error ("Error: negative or zero exponent provided.\n"); } for (cvm::atom_iter ai1 = group1.begin(); ai1 != group1.end(); ai1++) { for (cvm::atom_iter ai2 = group2.begin(); ai2 != group2.end(); ai2++) { if (ai1->id == ai2->id) cvm::fatal_error ("Error: group1 and group1 have some atoms in common: this is not allowed for distanceInv.\n"); } } b_inverse_gradients = false; b_Jacobian_derivative = false; x.type (colvarvalue::type_scalar); } colvar::distance_inv::distance_inv() { function_type = "distance_inv"; exponent = 6; b_inverse_gradients = false; b_Jacobian_derivative = false; b_1site_force = false; x.type (colvarvalue::type_scalar); } void colvar::distance_inv::calc_value() { group1.reset_atoms_data(); group2.reset_atoms_data(); group1.read_positions(); group2.read_positions(); x.real_value = 0.0; if (b_no_PBC) { for (cvm::atom_iter ai1 = group1.begin(); ai1 != group1.end(); ai1++) { for (cvm::atom_iter ai2 = group2.begin(); ai2 != group2.end(); ai2++) { cvm::rvector const dv = ai2->pos - ai1->pos; cvm::real const d2 = dv.norm2(); cvm::real dinv = 1.0; for (int ne = 0; ne < exponent/2; ne++) dinv *= 1.0/d2; x.real_value += dinv; cvm::rvector const dsumddv = -(cvm::real (exponent)) * dinv/d2 * dv; ai1->grad += -1.0 * dsumddv; ai2->grad += dsumddv; } } } else { for (cvm::atom_iter ai1 = group1.begin(); ai1 != group1.end(); ai1++) { for (cvm::atom_iter ai2 = group2.begin(); ai2 != group2.end(); ai2++) { cvm::rvector const dv = cvm::position_distance (ai1->pos, ai2->pos); cvm::real const d2 = dv.norm2(); cvm::real dinv = 1.0; for (int ne = 0; ne < exponent/2; ne++) dinv *= 1.0/d2; x.real_value += dinv; cvm::rvector const dsumddv = -(cvm::real (exponent)) * dinv/d2 * dv; ai1->grad += -1.0 * dsumddv; ai2->grad += dsumddv; } } } x.real_value *= 1.0 / cvm::real (group1.size() * group2.size()); x.real_value = std::pow (x.real_value, -1.0/(cvm::real (exponent))); } void colvar::distance_inv::calc_gradients() { cvm::real const dxdsum = (-1.0/(cvm::real (exponent))) * std::pow (x.real_value, exponent+1) / cvm::real (group1.size() * group2.size()); for (cvm::atom_iter ai1 = group1.begin(); ai1 != group1.end(); ai1++) { ai1->grad *= dxdsum; } for (cvm::atom_iter ai2 = group2.begin(); ai2 != group2.end(); ai2++) { ai2->grad *= dxdsum; } } void colvar::distance_inv::apply_force (colvarvalue const &force) { if (!group1.noforce) group1.apply_colvar_force (force.real_value); if (!group2.noforce) group2.apply_colvar_force (force.real_value); } colvar::gyration::gyration (std::string const &conf) : cvc (conf) { function_type = "gyration"; b_inverse_gradients = true; b_Jacobian_derivative = true; parse_group (conf, "atoms", atoms); atom_groups.push_back (&atoms); x.type (colvarvalue::type_scalar); } colvar::gyration::gyration() { function_type = "gyration"; b_inverse_gradients = true; b_Jacobian_derivative = true; x.type (colvarvalue::type_scalar); } void colvar::gyration::calc_value() { atoms.reset_atoms_data(); atoms.read_positions(); atoms.apply_translation ((-1.0) * atoms.center_of_geometry()); x.real_value = 0.0; for (cvm::atom_iter ai = atoms.begin(); ai != atoms.end(); ai++) { x.real_value += (ai->pos).norm2(); } x.real_value = std::sqrt (x.real_value / cvm::real (atoms.size())); } void colvar::gyration::calc_gradients() { cvm::real const drdx = 1.0/(cvm::real (atoms.size()) * x.real_value); for (cvm::atom_iter ai = atoms.begin(); ai != atoms.end(); ai++) { ai->grad = drdx * ai->pos; } } void colvar::gyration::calc_force_invgrads() { atoms.read_system_forces(); cvm::real const dxdr = 1.0/x.real_value; ft.real_value = 0.0; for (cvm::atom_iter ai = atoms.begin(); ai != atoms.end(); ai++) { ft.real_value += dxdr * ai->pos * ai->system_force; } } void colvar::gyration::calc_Jacobian_derivative() { jd = x.real_value ? (3.0 * cvm::real (atoms.size()) - 4.0) / x.real_value : 0.0; } void colvar::gyration::apply_force (colvarvalue const &force) { if (!atoms.noforce) atoms.apply_colvar_force (force.real_value); } colvar::inertia::inertia (std::string const &conf) : cvc (conf) { function_type = "inertia"; parse_group (conf, "atoms", atoms); atom_groups.push_back (&atoms); x.type (colvarvalue::type_scalar); } colvar::inertia::inertia() { function_type = "inertia"; x.type (colvarvalue::type_scalar); } void colvar::inertia::calc_value() { atoms.reset_atoms_data(); atoms.read_positions(); atoms.apply_translation ((-1.0) * atoms.center_of_mass()); x.real_value = 0.0; for (cvm::atom_iter ai = atoms.begin(); ai != atoms.end(); ai++) { x.real_value += ai->mass * (ai->pos).norm2(); } } void colvar::inertia::calc_gradients() { for (cvm::atom_iter ai = atoms.begin(); ai != atoms.end(); ai++) { ai->grad = 2.0 * ai->mass * ai->pos; } } void colvar::inertia::apply_force (colvarvalue const &force) { if (!atoms.noforce) atoms.apply_colvar_force (force.real_value); } colvar::inertia_z::inertia_z (std::string const &conf) : inertia (conf) { function_type = "inertia_z"; if (get_keyval (conf, "axis", axis, cvm::rvector (0.0, 0.0, 1.0))) { if (axis.norm2() == 0.0) cvm::fatal_error ("Axis vector is zero!"); if (axis.norm2() != 1.0) { axis = axis.unit(); cvm::log ("The normalized axis is: "+cvm::to_str (axis)+".\n"); } } x.type (colvarvalue::type_scalar); } colvar::inertia_z::inertia_z() { function_type = "inertia_z"; x.type (colvarvalue::type_scalar); } void colvar::inertia_z::calc_value() { atoms.reset_atoms_data(); atoms.read_positions(); atoms.apply_translation ((-1.0) * atoms.center_of_mass()); x.real_value = 0.0; for (cvm::atom_iter ai = atoms.begin(); ai != atoms.end(); ai++) { cvm::real const iprod = ai->pos * axis; x.real_value += ai->mass * iprod * iprod; } } void colvar::inertia_z::calc_gradients() { for (cvm::atom_iter ai = atoms.begin(); ai != atoms.end(); ai++) { ai->grad = 2.0 * ai->mass * (ai->pos * axis) * axis; } } void colvar::inertia_z::apply_force (colvarvalue const &force) { if (!atoms.noforce) atoms.apply_colvar_force (force.real_value); } colvar::rmsd::rmsd (std::string const &conf) : cvc (conf) { b_inverse_gradients = true; b_Jacobian_derivative = true; function_type = "rmsd"; x.type (colvarvalue::type_scalar); parse_group (conf, "atoms", atoms); atom_groups.push_back (&atoms); if (atoms.b_dummy) cvm::fatal_error ("Error: \"atoms\" cannot be a dummy atom."); - if (!atoms.b_prevent_fitting) { - // fit everything, unless the user made an explicit choice - cvm::log ("No value was specified within \"atoms\" for \"centerReference\" or " - "\"rotateReference\": enabling both.\n"); + if (atoms.b_user_defined_fit) { + cvm::log ("WARNING: explicit fitting parameters were provided for atom group \"atoms\"."); + cvm::log ("Not computing standard minimum RMSD (if that is the desired result, leave atom group parameters at default values)."); + } else { + // Default: fit everything // NOTE: this won't work for class V cvc's atoms.b_center = true; atoms.b_rotate = true; } if (atoms.b_center || atoms.b_rotate) { // request the calculation of the derivatives of the rotation defined by the atom group atoms.rot.request_group1_gradients (atoms.size()); // request derivatives of optimal rotation wrt 2nd group for Jacobian: this is only // required for ABF, but we do both groups here for better caching atoms.rot.request_group2_gradients (atoms.size()); if (atoms.ref_pos_group != NULL) { cvm::log ("The option \"refPositionsGroup\" (alternative group for fitting) was enabled: " "Jacobian derivatives of the RMSD will not be calculated.\n"); b_Jacobian_derivative = false; } } // the following is a simplified version of the corresponding atom group options: // to define the reference coordinates to compute this variable if (get_keyval (conf, "refPositions", ref_pos, ref_pos)) { cvm::log ("Using reference positions from configuration file to calculate the variable.\n"); if (ref_pos.size() != atoms.size()) { cvm::fatal_error ("Error: the number of reference positions provided ("+ cvm::to_str (ref_pos.size())+ ") does not match the number of atoms of group \"atoms\" ("+ cvm::to_str (atoms.size())+").\n"); } } { std::string ref_pos_file; if (get_keyval (conf, "refPositionsFile", ref_pos_file, std::string (""))) { if (ref_pos.size()) { cvm::fatal_error ("Error: cannot specify \"refPositionsFile\" and " "\"refPositions\" at the same time.\n"); } std::string ref_pos_col; double ref_pos_col_value; if (get_keyval (conf, "refPositionsCol", ref_pos_col, std::string (""))) { // if provided, use PDB column to select coordinates bool found = get_keyval (conf, "refPositionsColValue", ref_pos_col_value, 0.0); if (found && !ref_pos_col_value) cvm::fatal_error ("Error: refPositionsColValue, " "if provided, must be non-zero.\n"); } else { // if not, rely on existing atom indices for the group atoms.create_sorted_ids(); } + + ref_pos.resize (atoms.size()); cvm::load_coords (ref_pos_file.c_str(), ref_pos, atoms.sorted_ids, ref_pos_col, ref_pos_col_value); } } - - + if (!atoms.b_user_defined_fit) { + // Default case: reference positions for calculating the rmsd are also those used + // for fitting + atoms.ref_pos = ref_pos; + atoms.center_ref_pos(); + } } void colvar::rmsd::calc_value() { atoms.reset_atoms_data(); atoms.read_positions(); // rotational fit is done internally // atoms_cog = atoms.center_of_geometry(); // rot.calc_optimal_rotation (ref_pos, atoms.positions_shifted (-1.0 * atoms_cog)); // cvm::real group_pos_sum2 = 0.0; // for (size_t i = 0; i < atoms.size(); i++) { // group_pos_sum2 += (atoms[i].pos - atoms_cog).norm2(); // } // // value of the RMSD (Coutsias et al) // cvm::real const MSD = 1.0/(cvm::real (atoms.size())) * // ( group_pos_sum2 + ref_pos_sum2 - 2.0 * rot.lambda ); // x.real_value = (MSD > 0.0) ? std::sqrt (MSD) : 0.0; x.real_value = 0.0; for (size_t ia = 0; ia < atoms.size(); ia++) { x.real_value += (atoms[ia].pos - ref_pos[ia]).norm2(); } x.real_value /= cvm::real (atoms.size()); // MSD x.real_value = (x.real_value > 0.0) ? std::sqrt (x.real_value) : 0.0; } void colvar::rmsd::calc_gradients() { cvm::real const drmsddx2 = (x.real_value > 0.0) ? 0.5 / (x.real_value * cvm::real (atoms.size())) : 0.0; for (size_t ia = 0; ia < atoms.size(); ia++) { - atoms[ia].grad = (drmsddx2 * 2.0 * (atoms[ia].pos - atoms.ref_pos[ia])); + atoms[ia].grad = (drmsddx2 * 2.0 * (atoms[ia].pos - ref_pos[ia])); } } void colvar::rmsd::apply_force (colvarvalue const &force) { if (!atoms.noforce) atoms.apply_colvar_force (force.real_value); } void colvar::rmsd::calc_force_invgrads() { atoms.read_system_forces(); ft.real_value = 0.0; // Note: gradient square norm is 1/N_atoms for (size_t ia = 0; ia < atoms.size(); ia++) { ft.real_value += atoms[ia].grad * atoms[ia].system_force; } ft.real_value *= atoms.size(); } void colvar::rmsd::calc_Jacobian_derivative() { // divergence of the rotated coordinates (including only derivatives of the rotation matrix) cvm::real divergence = 0.0; if (atoms.b_rotate) { // gradient of the rotation matrix cvm::matrix2d <cvm::rvector, 3, 3> grad_rot_mat; // gradients of products of 2 quaternion components cvm::rvector g11, g22, g33, g01, g02, g03, g12, g13, g23; for (size_t ia = 0; ia < atoms.size(); ia++) { // Gradient of optimal quaternion wrt current Cartesian position cvm::vector1d< cvm::rvector, 4 > &dq = atoms.rot.dQ0_1[ia]; g11 = 2.0 * (atoms.rot.q)[1]*dq[1]; g22 = 2.0 * (atoms.rot.q)[2]*dq[2]; g33 = 2.0 * (atoms.rot.q)[3]*dq[3]; g01 = (atoms.rot.q)[0]*dq[1] + (atoms.rot.q)[1]*dq[0]; g02 = (atoms.rot.q)[0]*dq[2] + (atoms.rot.q)[2]*dq[0]; g03 = (atoms.rot.q)[0]*dq[3] + (atoms.rot.q)[3]*dq[0]; g12 = (atoms.rot.q)[1]*dq[2] + (atoms.rot.q)[2]*dq[1]; g13 = (atoms.rot.q)[1]*dq[3] + (atoms.rot.q)[3]*dq[1]; g23 = (atoms.rot.q)[2]*dq[3] + (atoms.rot.q)[3]*dq[2]; // Gradient of the rotation matrix wrt current Cartesian position grad_rot_mat[0][0] = -2.0 * (g22 + g33); grad_rot_mat[1][0] = 2.0 * (g12 + g03); grad_rot_mat[2][0] = 2.0 * (g13 - g02); grad_rot_mat[0][1] = 2.0 * (g12 - g03); grad_rot_mat[1][1] = -2.0 * (g11 + g33); grad_rot_mat[2][1] = 2.0 * (g01 + g23); grad_rot_mat[0][2] = 2.0 * (g02 + g13); grad_rot_mat[1][2] = 2.0 * (g23 - g01); grad_rot_mat[2][2] = -2.0 * (g11 + g22); cvm::atom_pos &x = atoms[ia].pos; for (size_t i = 0; i < 3; i++) { for (size_t j = 0; j < 3; j++) { divergence += grad_rot_mat[i][j][i] * x[j]; } } } } jd.real_value = x.real_value > 0.0 ? (3.0 * atoms.size() - 4.0 - divergence) / x.real_value : 0.0; } colvar::eigenvector::eigenvector (std::string const &conf) : cvc (conf) { b_inverse_gradients = true; b_Jacobian_derivative = true; function_type = "eigenvector"; x.type (colvarvalue::type_scalar); parse_group (conf, "atoms", atoms); atom_groups.push_back (&atoms); if (atoms.b_rotate) { cvm::fatal_error ("Error: rotateReference should be disabled:" "eigenvector component will set it internally."); } if (get_keyval (conf, "refPositions", ref_pos, ref_pos)) { cvm::log ("Using reference positions from input file.\n"); if (ref_pos.size() != atoms.size()) { cvm::fatal_error ("Error: reference positions do not " "match the number of requested atoms.\n"); } } { std::string file_name; if (get_keyval (conf, "refPositionsFile", file_name)) { std::string file_col; double file_col_value; if (get_keyval (conf, "refPositionsCol", file_col, std::string (""))) { // use PDB flags if column is provided bool found = get_keyval (conf, "refPositionsColValue", file_col_value, 0.0); if (found && !file_col_value) cvm::fatal_error ("Error: refPositionsColValue, " "if provided, must be non-zero.\n"); } else { // if not, use atom indices atoms.create_sorted_ids(); } ref_pos.resize (atoms.size()); cvm::load_coords (file_name.c_str(), ref_pos, atoms.sorted_ids, file_col, file_col_value); } } // Set mobile frame of reference for atom group atoms.b_center = true; atoms.b_rotate = true; atoms.ref_pos = ref_pos; + atoms.center_ref_pos(); // now load the eigenvector if (get_keyval (conf, "vector", eigenvec, eigenvec)) { cvm::log ("Using vector components from input file.\n"); if (eigenvec.size() != atoms.size()) { cvm::fatal_error ("Error: vector components do not " "match the number of requested atoms.\n"); } } { std::string file_name; if (get_keyval (conf, "vectorFile", file_name)) { std::string file_col; if (!get_keyval (conf, "vectorCol", file_col, std::string (""))) { cvm::fatal_error ("Error: parameter vectorCol is required if vectorFile is set.\n"); } double file_col_value; bool found = get_keyval (conf, "vectorColValue", file_col_value, 0.0); if (found && !file_col_value) cvm::fatal_error ("Error: eigenvectorColValue, " "if provided, must be non-zero.\n"); eigenvec.resize (atoms.size()); cvm::load_coords (file_name.c_str(), eigenvec, atoms.sorted_ids, file_col, file_col_value); } } if (!ref_pos.size() || !eigenvec.size()) { cvm::fatal_error ("Error: both reference coordinates" "and eigenvector must be defined.\n"); } cvm::rvector center (0.0, 0.0, 0.0); eigenvec_invnorm2 = 0.0; for (size_t i = 0; i < atoms.size(); i++) { center += eigenvec[i]; } + center *= 1.0 / atoms.size(); - cvm::log ("Subtracting sum of eigenvector components: " + cvm::to_str (center) + "\n"); + cvm::log ("Subtracting center of eigenvector components: " + cvm::to_str (center) + "\n"); for (size_t i = 0; i < atoms.size(); i++) { eigenvec[i] = eigenvec[i] - center; eigenvec_invnorm2 += eigenvec[i].norm2(); } eigenvec_invnorm2 = 1.0 / eigenvec_invnorm2; // request derivatives of optimal rotation wrt 2nd group // for Jacobian atoms.rot.request_group1_gradients(atoms.size()); atoms.rot.request_group2_gradients(atoms.size()); } void colvar::eigenvector::calc_value() { atoms.reset_atoms_data(); atoms.read_positions(); // this will also update atoms.rot x.real_value = 0.0; for (size_t i = 0; i < atoms.size(); i++) { x.real_value += (atoms[i].pos - ref_pos[i]) * eigenvec[i]; } } void colvar::eigenvector::calc_gradients() { // There are two versions of this code // The simple version is not formally exact, but its // results are numerically indistinguishable from the // exact one. The exact one is more expensive and possibly // less stable in cases where the optimal rotation // becomes ill-defined. // Version A: simple, intuitive, cheap, robust. Wrong. // Works just fine in practice. for (size_t ia = 0; ia < atoms.size(); ia++) { atoms[ia].grad = eigenvec[ia]; } /* // Version B: complex, expensive, fragile. Right. // gradient of the rotation matrix cvm::matrix2d <cvm::rvector, 3, 3> grad_rot_mat; cvm::quaternion &quat0 = atoms.rot.q; // gradients of products of 2 quaternion components cvm::rvector g11, g22, g33, g01, g02, g03, g12, g13, g23; // a term that involves the rotation gradients cvm::rvector rot_grad_term; cvm::atom_pos x_relative; cvm::atom_pos atoms_cog = atoms.center_of_geometry(); for (size_t ia = 0; ia < atoms.size(); ia++) { // Gradient of optimal quaternion wrt current Cartesian position // WARNING: we want derivatives wrt the FIRST group here (unlike RMSD) cvm::vector1d< cvm::rvector, 4 > &dq = atoms.rot.dQ0_1[ia]; g11 = 2.0 * quat0[1]*dq[1]; g22 = 2.0 * quat0[2]*dq[2]; g33 = 2.0 * quat0[3]*dq[3]; g01 = quat0[0]*dq[1] + quat0[1]*dq[0]; g02 = quat0[0]*dq[2] + quat0[2]*dq[0]; g03 = quat0[0]*dq[3] + quat0[3]*dq[0]; g12 = quat0[1]*dq[2] + quat0[2]*dq[1]; g13 = quat0[1]*dq[3] + quat0[3]*dq[1]; g23 = quat0[2]*dq[3] + quat0[3]*dq[2]; // Gradient of the rotation matrix wrt current Cartesian position grad_rot_mat[0][0] = -2.0 * (g22 + g33); grad_rot_mat[1][0] = 2.0 * (g12 + g03); grad_rot_mat[2][0] = 2.0 * (g13 - g02); grad_rot_mat[0][1] = 2.0 * (g12 - g03); grad_rot_mat[1][1] = -2.0 * (g11 + g33); grad_rot_mat[2][1] = 2.0 * (g01 + g23); grad_rot_mat[0][2] = 2.0 * (g02 + g13); grad_rot_mat[1][2] = 2.0 * (g23 - g01); grad_rot_mat[2][2] = -2.0 * (g11 + g22); // this term needs to be rotated back, so we sum it separately rot_grad_term.reset(); for (size_t ja = 0; ja < atoms.size(); ja++) { x_relative = atoms[ja].pos - atoms_cog; for (size_t i = 0; i < 3; i++) { for (size_t j = 0; j < 3; j++) { rot_grad_term += eigenvec[ja][i] * grad_rot_mat[i][j] * x_relative[j]; } } } // Rotate correction term back to reference frame atoms[ia].grad = eigenvec[ia] + quat0.rotate (rot_grad_term); } */ } void colvar::eigenvector::apply_force (colvarvalue const &force) { if (!atoms.noforce) atoms.apply_colvar_force (force.real_value); } void colvar::eigenvector::calc_force_invgrads() { atoms.read_system_forces(); ft.real_value = 0.0; for (size_t ia = 0; ia < atoms.size(); ia++) { ft.real_value += eigenvec_invnorm2 * atoms[ia].grad * atoms[ia].system_force; } } void colvar::eigenvector::calc_Jacobian_derivative() { // gradient of the rotation matrix cvm::matrix2d <cvm::rvector, 3, 3> grad_rot_mat; cvm::quaternion &quat0 = atoms.rot.q; // gradients of products of 2 quaternion components cvm::rvector g11, g22, g33, g01, g02, g03, g12, g13, g23; cvm::atom_pos x_relative; cvm::real sum = 0.0; for (size_t ia = 0; ia < atoms.size(); ia++) { // Gradient of optimal quaternion wrt current Cartesian position // trick: d(R^-1)/dx = d(R^t)/dx = (dR/dx)^t // we can just transpose the derivatives of the direct matrix cvm::vector1d< cvm::rvector, 4 > &dq_1 = atoms.rot.dQ0_1[ia]; g11 = 2.0 * quat0[1]*dq_1[1]; g22 = 2.0 * quat0[2]*dq_1[2]; g33 = 2.0 * quat0[3]*dq_1[3]; g01 = quat0[0]*dq_1[1] + quat0[1]*dq_1[0]; g02 = quat0[0]*dq_1[2] + quat0[2]*dq_1[0]; g03 = quat0[0]*dq_1[3] + quat0[3]*dq_1[0]; g12 = quat0[1]*dq_1[2] + quat0[2]*dq_1[1]; g13 = quat0[1]*dq_1[3] + quat0[3]*dq_1[1]; g23 = quat0[2]*dq_1[3] + quat0[3]*dq_1[2]; // Gradient of the inverse rotation matrix wrt current Cartesian position // (transpose of the gradient of the direct rotation) grad_rot_mat[0][0] = -2.0 * (g22 + g33); grad_rot_mat[0][1] = 2.0 * (g12 + g03); grad_rot_mat[0][2] = 2.0 * (g13 - g02); grad_rot_mat[1][0] = 2.0 * (g12 - g03); grad_rot_mat[1][1] = -2.0 * (g11 + g33); grad_rot_mat[1][2] = 2.0 * (g01 + g23); grad_rot_mat[2][0] = 2.0 * (g02 + g13); grad_rot_mat[2][1] = 2.0 * (g23 - g01); grad_rot_mat[2][2] = -2.0 * (g11 + g22); for (size_t i = 0; i < 3; i++) { for (size_t j = 0; j < 3; j++) { sum += grad_rot_mat[i][j][i] * eigenvec[ia][j]; } } } jd.real_value = sum * std::sqrt (eigenvec_invnorm2); } diff --git a/lib/colvars/colvarmodule.h b/lib/colvars/colvarmodule.h index dfc0578eb..2375fafa3 100644 --- a/lib/colvars/colvarmodule.h +++ b/lib/colvars/colvarmodule.h @@ -1,493 +1,493 @@ #ifndef COLVARMODULE_H #define COLVARMODULE_H #ifndef COLVARS_VERSION -#define COLVARS_VERSION "2012-10-03" +#define COLVARS_VERSION "2012-11-20" #endif #ifndef COLVARS_DEBUG #define COLVARS_DEBUG false #endif /// \file colvarmodule.h /// \brief Collective variables main module /// /// This file declares the main class for defining and manipulating /// collective variables: there should be only one instance of this /// class, because several variables are made static (i.e. they are /// shared between all object instances) to be accessed from other /// objects. #include <iostream> #include <iomanip> #include <string> #include <sstream> #include <fstream> #include <cmath> #include <vector> #include <list> class colvarparse; class colvar; class colvarbias; class colvarproxy; /// \brief Collective variables module (main class) /// /// Class to control the collective variables calculation. An object /// (usually one) of this class is spawned from the MD program, /// containing all i/o routines and general interface. /// /// At initialization, the colvarmodule object creates a proxy object /// to provide a transparent interface between the MD program and the /// child objects class colvarmodule { private: /// Impossible to initialize the main object without arguments colvarmodule(); public: friend class colvarproxy; /// Defining an abstract real number allows to switch precision typedef double real; /// Residue identifier typedef int residue_id; class rvector; template <class T, size_t const length> class vector1d; template <class T, size_t const outer_length, size_t const inner_length> class matrix2d; class quaternion; class rotation; /// \brief Atom position (different type name from rvector, to make /// possible future PBC-transparent implementations) typedef rvector atom_pos; /// \brief 3x3 matrix of real numbers class rmatrix; // allow these classes to access protected data class atom; class atom_group; friend class atom; friend class atom_group; typedef std::vector<atom>::iterator atom_iter; typedef std::vector<atom>::const_iterator atom_const_iter; /// Current step number static size_t it; /// Starting step number for this run static size_t it_restart; /// Return the current step number from the beginning of this run static inline size_t step_relative() { return it - it_restart; } /// Return the current step number from the beginning of the whole /// calculation static inline size_t step_absolute() { return it; } /// If true, get it_restart from the state file; if set to false, /// the MD program is providing it bool it_restart_from_state_file; /// \brief Finite difference step size (if there is no dynamics, or /// if gradients need to be tested independently from the size of /// dt) static real debug_gradients_step_size; /// Prefix for all output files for this run static std::string output_prefix; /// Prefix for files from a previous run (including restart/output) static std::string input_prefix; /// input restart file name (determined from input_prefix) static std::string restart_in_name; /// Array of collective variables static std::vector<colvar *> colvars; /* TODO: implement named CVCs /// Array of named (reusable) collective variable components static std::vector<cvc *> cvcs; /// Named cvcs register themselves at initialization time inline void register_cvc (cvc *p) { cvcs.push_back(p); } */ /// Array of collective variable biases static std::vector<colvarbias *> biases; /// \brief Number of ABF biases initialized (in normal conditions /// should be 1) static size_t n_abf_biases; /// \brief Number of metadynamics biases initialized (in normal /// conditions should be 1) static size_t n_meta_biases; /// \brief Number of harmonic biases initialized (no limit on the /// number) static size_t n_harm_biases; /// \brief Number of histograms initialized (no limit on the /// number) static size_t n_histo_biases; /// \brief Whether debug output should be enabled (compile-time option) static inline bool debug() { return COLVARS_DEBUG; } /// \brief Constructor \param config_name Configuration file name /// \param restart_name (optional) Restart file name colvarmodule (char const *config_name, colvarproxy *proxy_in); /// Destructor ~colvarmodule(); /// Initialize collective variables void init_colvars (std::string const &conf); /// Initialize collective variable biases void init_biases (std::string const &conf); /// Load new configuration - force constant and/or centers only void change_configuration(std::string const &name, std::string const &conf); /// Calculate change in energy from using alternate configuration real energy_difference(std::string const &name, std::string const &conf); /// Calculate collective variables and biases void calc(); /// Read the input restart file std::istream & read_restart (std::istream &is); /// Write the output restart file std::ostream & write_restart (std::ostream &os); /// Write all output files (called by the proxy) void write_output_files(); /// \brief Call colvarproxy::backup_file() static void backup_file (char const *filename); /// Perform analysis void analyze(); /// \brief Read a collective variable trajectory (post-processing /// only, not called at runtime) bool read_traj (char const *traj_filename, size_t traj_read_begin, size_t traj_read_end); /// Get the pointer of a colvar from its name (returns NULL if not found) static colvar * colvar_p (std::string const &name); /// Quick conversion of an object to a string template<typename T> static std::string to_str (T const &x, size_t const &width = 0, size_t const &prec = 0); /// Quick conversion of a vector of objects to a string template<typename T> static std::string to_str (std::vector<T> const &x, size_t const &width = 0, size_t const &prec = 0); /// Reduce the number of characters in a string static inline std::string wrap_string (std::string const &s, size_t const &nchars) { if (!s.size()) return std::string (nchars, ' '); else return ( (s.size() <= size_t (nchars)) ? (s+std::string (nchars-s.size(), ' ')) : (std::string (s, 0, nchars)) ); } /// Number of characters to represent a time step static size_t const it_width; /// Number of digits to represent a collective variables value(s) static size_t const cv_prec; /// Number of characters to represent a collective variables value(s) static size_t const cv_width; /// Number of digits to represent the collective variables energy static size_t const en_prec; /// Number of characters to represent the collective variables energy static size_t const en_width; /// Line separator in the log output static std::string const line_marker; // proxy functions /// \brief Value of the unit for atomic coordinates with respect to /// angstroms (used by some variables for hard-coded default values) static real unit_angstrom(); /// \brief Boltmann constant static real boltzmann(); /// \brief Temperature of the simulation (K) static real temperature(); /// \brief Time step of MD integrator (fs) static real dt(); /// Request calculation of system force from MD engine static void request_system_force(); /// Print a message to the main log static void log (std::string const &message); /// Print a message to the main log and exit with error code static void fatal_error (std::string const &message); /// Print a message to the main log and exit normally static void exit (std::string const &message); /// \brief Get the distance between two atomic positions with pbcs handled /// correctly static rvector position_distance (atom_pos const &pos1, atom_pos const &pos2); /// \brief Get the square distance between two positions (with /// periodic boundary conditions handled transparently) /// /// Note: in the case of periodic boundary conditions, this provides /// an analytical square distance (while taking the square of /// position_distance() would produce leads to a cusp) static real position_dist2 (atom_pos const &pos1, atom_pos const &pos2); /// \brief Get the closest periodic image to a reference position /// \param pos The position to look for the closest periodic image /// \param ref_pos (optional) The reference position static void select_closest_image (atom_pos &pos, atom_pos const &ref_pos); /// \brief Perform select_closest_image() on a set of atomic positions /// /// After that, distance vectors can then be calculated directly, /// without using position_distance() static void select_closest_images (std::vector<atom_pos> &pos, atom_pos const &ref_pos); /// \brief Create atoms from a file \param filename name of the file /// (usually a PDB) \param atoms array of the atoms to be allocated /// \param pdb_field (optiona) if "filename" is a PDB file, use this /// field to determine which are the atoms to be set static void load_atoms (char const *filename, std::vector<atom> &atoms, std::string const &pdb_field, double const pdb_field_value = 0.0); /// \brief Load the coordinates for a group of atoms from a file /// (usually a PDB); the number of atoms in "filename" must match /// the number of elements in "pos" static void load_coords (char const *filename, std::vector<atom_pos> &pos, const std::vector<int> &indices, std::string const &pdb_field, double const pdb_field_value = 0.0); /// Frequency for collective variables trajectory output static size_t cv_traj_freq; /// \brief True if only analysis is performed and not a run static bool b_analysis; /// Frequency for saving output restarts static size_t restart_out_freq; /// Output restart file name std::string restart_out_name; /// Pseudo-random number with Gaussian distribution static real rand_gaussian (void); protected: /// Configuration file std::ifstream config_s; /// Configuration file parser object colvarparse *parse; /// Name of the trajectory file std::string cv_traj_name; /// Collective variables output trajectory file std::ofstream cv_traj_os; /// Appending to the existing trajectory file? bool cv_traj_append; /// Output restart file std::ofstream restart_out_os; /// \brief Pointer to the proxy object, used to retrieve atomic data /// from the hosting program; it is static in order to be accessible /// from static functions in the colvarmodule class static colvarproxy *proxy; /// \brief Counter for the current depth in the object hierarchy (useg e.g. in outpu static size_t depth; public: /// Increase the depth (number of indentations in the output) static void increase_depth(); /// Decrease the depth (number of indentations in the output) static void decrease_depth(); }; /// Shorthand for the frequently used type prefix typedef colvarmodule cvm; #include "colvartypes.h" std::ostream & operator << (std::ostream &os, cvm::rvector const &v); std::istream & operator >> (std::istream &is, cvm::rvector &v); template<typename T> std::string cvm::to_str (T const &x, size_t const &width, size_t const &prec) { std::ostringstream os; if (width) os.width (width); if (prec) { os.setf (std::ios::scientific, std::ios::floatfield); os.precision (prec); } os << x; return os.str(); } template<typename T> std::string cvm::to_str (std::vector<T> const &x, size_t const &width, size_t const &prec) { if (!x.size()) return std::string (""); std::ostringstream os; if (prec) { os.setf (std::ios::scientific, std::ios::floatfield); } os << "{ "; if (width) os.width (width); if (prec) os.precision (prec); os << x[0]; for (size_t i = 1; i < x.size(); i++) { os << ", "; if (width) os.width (width); if (prec) os.precision (prec); os << x[i]; } os << " }"; return os.str(); } #include "colvarproxy.h" inline cvm::real cvm::unit_angstrom() { return proxy->unit_angstrom(); } inline cvm::real cvm::boltzmann() { return proxy->boltzmann(); } inline cvm::real cvm::temperature() { return proxy->temperature(); } inline cvm::real cvm::dt() { return proxy->dt(); } inline void cvm::request_system_force() { proxy->request_system_force (true); } inline void cvm::select_closest_image (atom_pos &pos, atom_pos const &ref_pos) { proxy->select_closest_image (pos, ref_pos); } inline void cvm::select_closest_images (std::vector<atom_pos> &pos, atom_pos const &ref_pos) { proxy->select_closest_images (pos, ref_pos); } inline cvm::rvector cvm::position_distance (atom_pos const &pos1, atom_pos const &pos2) { return proxy->position_distance (pos1, pos2); } inline cvm::real cvm::position_dist2 (cvm::atom_pos const &pos1, cvm::atom_pos const &pos2) { return proxy->position_dist2 (pos1, pos2); } inline void cvm::load_atoms (char const *file_name, std::vector<cvm::atom> &atoms, std::string const &pdb_field, double const pdb_field_value) { proxy->load_atoms (file_name, atoms, pdb_field, pdb_field_value); } inline void cvm::load_coords (char const *file_name, std::vector<cvm::atom_pos> &pos, const std::vector<int> &indices, std::string const &pdb_field, double const pdb_field_value) { proxy->load_coords (file_name, pos, indices, pdb_field, pdb_field_value); } inline void cvm::backup_file (char const *filename) { proxy->backup_file (filename); } inline cvm::real cvm::rand_gaussian (void) { return proxy->rand_gaussian(); } #endif // Emacs // Local Variables: // mode: C++ // End: