#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), 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; } { // get the atoms by numbers std::vector 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 psf_segids; get_keyval (group_conf, "psfSegID", psf_segids, std::vector (), mode); for (std::vector::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::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::iterator a1 = this->begin(); a1 != this->end(); a1++) { std::vector::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; } } get_keyval (group_conf, "disableForces", noforce, false, mode); get_keyval (group_conf, "centerReference", b_center, false, mode); get_keyval (group_conf, "rotateReference", b_rotate, false, mode); if (b_center || b_rotate) { if (b_dummy) cvm::fatal_error ("Error: cannot set \"centerReference\" or " "\"rotateReference\" with \"dummyAtom\".\n"); // use refPositionsGroup instead of this group as the one which is // 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"); } atom_group *ag = ref_pos_group ? ref_pos_group : this; if (get_keyval (group_conf, "refPositions", ref_pos, ref_pos, mode)) { cvm::log ("Using reference positions from input file.\n"); if (ref_pos.size() != ag->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 (ag->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 ag->create_sorted_ids(); } cvm::load_coords (ref_pos_file.c_str(), ref_pos, ag->sorted_ids, ref_pos_col, ref_pos_col_value); } if (ref_pos.size()) { if (b_rotate) { if (ref_pos.size() != ag->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 (ag->size())+").\n"); } // save the center of mass of ref_pos and then subtract it from // them; in this way it is possible to use the coordinates for // the rotational fit, if needed ref_pos_cog = cvm::atom_pos (0.0, 0.0, 0.0); std::vector::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::iterator pi = ref_pos.begin(); pi != ref_pos.end(); pi++) { (*pi) -= ref_pos_cog; } } else { #if (! defined (COLVARS_STANDALONE)) if (!cvm::b_analysis) cvm::fatal_error ("Error: no reference positions provided.\n"); #endif } if (b_rotate && !noforce) { cvm::log ("Warning: atom group \""+std::string (key)+ "\" is set to be rotated to a reference orientation: " "a torque different than zero on this group " "could make the simulation unstable. " "If this happens, set \"disableForces\" to yes " "for this group.\n"); } } 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 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 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 (temp_id_list.begin(), temp_id_list.end()); return; } 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_group::positions() const { if (b_dummy) cvm::fatal_error ("Error: positions are not available " "from a dummy atom group.\n"); std::vector x (this->size(), 0.0); cvm::atom_const_iter ai = this->begin(); std::vector::iterator xi = x.begin(); for ( ; ai != this->end(); xi++, ai++) { *xi = ai->pos; } return x; } std::vector 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 x (this->size(), 0.0); cvm::atom_const_iter ai = this->begin(); std::vector::iterator xi = x.begin(); for ( ; ai != this->end(); xi++, ai++) { *xi = (ai->pos + shift); } return x; } std::vector cvm::atom_group::velocities() const { if (b_dummy) cvm::fatal_error ("Error: velocities are not available " "from a dummy atom group.\n"); std::vector v (this->size(), 0.0); cvm::atom_const_iter ai = this->begin(); std::vector::iterator vi = v.begin(); for ( ; ai != this->end(); vi++, ai++) { *vi = ai->vel; } return v; } std::vector 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 f (this->size(), 0.0); cvm::atom_const_iter ai = this->begin(); std::vector::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 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::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::const_iterator fi = forces.begin(); for ( ; ai != this->end(); fi++, ai++) { ai->apply_force (*fi); } } }